Example #1
0
        public static void DrawTetrisBoard(RenderTarget target, Vector2f pos, Tetris tetris, TetrisRNG rng, IEnumerable <TetriminoState> moves, TetriminoState move)
        {
            void DrawTetrimino(Tetrimino mino, int x, int y, int rot)
            {
                tetrisRect.FillColor = cellColors[(byte)mino.type];
                for (int i = mino.states.GetLength(1) - 1; i >= 0; i--)
                {
                    Pair <sbyte> block = mino.states[rot, i];
                    DrawCell(target, block.x + x, block.y + y, pos);
                }
            }

            for (int y = 0; y < 20; y++)
            {
                for (int x = 0; x < 10; x++)
                {
                    tetrisRect.FillColor = cellColors[(byte)tetris.GetCell(x, y + 20)];
                    if (tetrisRect.FillColor == Color.Black)
                    {
                        tetrisRect.OutlineThickness = 1;
                    }
                    DrawCell(target, x + 5, y, pos);
                    if (tetrisRect.FillColor == Color.Black)
                    {
                        tetrisRect.OutlineThickness = 0;
                    }
                }
            }
            if (moves != null)
            {
                Vector2f origScale     = tetrisRect.Scale;
                Vector2f fifthScale    = new Vector2f(0.20f, 0.20f);
                Vector2f twoFifthScale = fifthScale * 2;
                tetrisRect.Scale = fifthScale;
                foreach (TetriminoState m in moves)
                {
                    if (m.Equals(move))
                    {
                        tetrisRect.Scale = twoFifthScale;
                    }
                    DrawTetrimino(tetris.current, m.x + 5, m.y - 20, m.rot);
                    if (m.Equals(move))
                    {
                        tetrisRect.Scale = fifthScale;
                    }
                }
                tetrisRect.Scale = origScale;
            }
            //DrawTetrimino(tetris.current, move.x + 5, move.y - 20, move.rot);
            DrawTetrimino(tetris.current, tetris.pieceX + 5, tetris.pieceY - 20, tetris.pieceRotation);
            if (tetris.hold != null)
            {
                DrawTetrimino(tetris.hold, 1, 1, 0);
            }
            for (int x = 0; x < 5; x++)
            {
                DrawTetrimino(rng.GetPiece(x), 17, 1 + 4 * x, 0);
            }
        }
Example #2
0
        public (float, float) Evaluate(Node node)
        {
            TetriminoState move        = node.move;
            TetrisState    state       = node.state;
            TetrisState    parentState = node.parent.state;

            int availibleTs = 0;

            for (int i = 0; true; i++)
            {
                Tetrimino tetrimino = parentState.tetris.rng.GetPiece(i);
                if (tetrimino == null)
                {
                    break;
                }
                if (tetrimino == Tetrimino.T)
                {
                    availibleTs += 1;
                }
            }
            if (logging)
            {
                Console.WriteLine($"{availibleTs} availible Ts.");
            }

            int totalFilledEdgeTiles          = 0;
            HashSet <(int, int)> checkedCells = new HashSet <(int, int)>();

            void TestEdge(int x, int y)
            {
                if (checkedCells.Add((x, y)) && parentState.tetris.GetCell(x, y) != CellType.EMPTY)
                {
                    totalFilledEdgeTiles += 1;
                }
            }

            for (int i = 0; i < 4; i++)
            {
                Pair <sbyte> block = state.tetrimino.states[move.rot, i];
                int          x     = block.x + move.x;
                int          y     = block.y + move.y;
                TestEdge(x + 1, y);
                TestEdge(x - 1, y);
                TestEdge(x, y + 1);
                TestEdge(x, y - 1);
            }
            int totalEdgeTiles    = checkedCells.Count - 4;
            int holes             = 0;
            int holeDepths        = 0;
            int holeDepthsSquared = 0;
            //int buriedHoles = 0;
            bool isPC   = true;
            int  wells  = 0;
            int  spikes = 0;
            int  tslots = 0;

            int[] heights = new int[10];
            for (int x = 0; x < 10; x++)
            {
                for (int y = 20; y < 40; y++)
                {
                    if (state.tetris.GetCell(x, y) == CellType.EMPTY)
                    {
                        if (39 - y < heights[x])
                        {
                            int depth = heights[x] - 39 + y;
                            holeDepths        += depth;
                            holeDepthsSquared += depth * depth;
                            holes             += 1;//Math.Max(1, heights[x] - 39 + y / 2);
                        }
                    }
                    else
                    {
                        heights[x] = 39 - y;
                        isPC       = false;
                    }
                    if (wellPattern.Test(state.tetris, x, y) == 0)
                    {
                        wells += 1;
                    }
                    if (spikePattern.Test(state.tetris, x, y) == 0)
                    {
                        spikes += 1;
                    }
                    if (state.tetris.GetCell(x, y) == CellType.EMPTY)
                    {
                        int tCorners = 0;
                        if (state.tetris.GetCell(x - 1, y - 1) != CellType.EMPTY)
                        {
                            tCorners += 1;
                        }
                        if (state.tetris.GetCell(x + 1, y - 1) != CellType.EMPTY)
                        {
                            tCorners += 1;
                        }
                        if (state.tetris.GetCell(x - 1, y + 1) != CellType.EMPTY)
                        {
                            tCorners += 1;
                        }
                        if (state.tetris.GetCell(x + 1, y + 1) != CellType.EMPTY)
                        {
                            tCorners += 1;
                        }
                        if (tCorners > 2)
                        {
                            int tHoles = 0;
                            if (state.tetris.GetCell(x - 1, y) == CellType.EMPTY)
                            {
                                tHoles += 1;
                            }
                            if (state.tetris.GetCell(x + 1, y) == CellType.EMPTY)
                            {
                                tHoles += 1;
                            }
                            if (state.tetris.GetCell(x, y - 1) != CellType.EMPTY)
                            {
                                tHoles = 0;
                                //tHoles += 1;
                            }
                            if (state.tetris.GetCell(x, y + 1) == CellType.EMPTY)
                            {
                                tHoles += 1;
                            }
                            if (tHoles > 2)
                            {
                                if (logging)
                                {
                                    Console.WriteLine($"tslot at {x}, {y}");
                                }
                                tslots += 1;
                            }
                        }
                    }
                }
            }

            if (isPC)
            {
                return(weights.perfectClear, 0);
            }

            int maxHeight   = 0;
            int minHeight   = 0;
            int totalHeight = 0;

            for (int i = 0; i < heights.Length; i++)
            {
                int height = heights[i];
                if (height > maxHeight)
                {
                    maxHeight = height;
                }
                if (height < minHeight)
                {
                    minHeight = height;
                }
                totalHeight += height;
            }

            float accumulated = 0;
            float transient   = 0;

            accumulated += holes * weights.holes;
            accumulated += holes * holes * weights.holesSquared;
            accumulated += holeDepths * weights.holeDepths;
            accumulated += holeDepthsSquared * weights.holeDepthsSquared;
            if (move.y <= 35)
            {
                int moveHeight = 39 - move.y;
                accumulated += moveHeight * weights.moveHeight;
                accumulated += moveHeight * moveHeight * weights.moveHeightSquared;
            }
            accumulated += maxHeight * weights.maxHeight;
            accumulated += maxHeight * weights.maxHeightSquared;
            accumulated += state.tetris.tspin switch {
                TspinType.MINI => state.tetris.linesCleared switch {
                    1 => weights.mini1,
                    2 => weights.mini2,
                    _ => 0,
                },
Example #3
0
        static void Main(string[] args)
        {
            window             = new RenderWindow(new VideoMode(300, 300), "MinoBot GUI", Styles.Close);
            window.Closed     += OnClose;
            window.KeyPressed += OnKeyPressed;
            window.SetActive();
            //window.SetFramerateLimit(60);
            //Thread.Sleep(5000);

            TetrisRNG tetRNG = new TetrisRNG(3);

            //while (tetRNG.GetPiece(0) != Tetrimino.T) tetRNG.NextPiece();
            tetris = new Tetris(tetRNG);
            TetrisDrawer.SetScale(new Vector2f(window.Size.X / 20, window.Size.Y / 20));
            pathfinder = new Pathfinder();
            TetrisBot bot = new TetrisBot(tetris);

            moves = pathfinder.FindAllMoves(tetris, 1, 1, 1);
            long   totalThinkMS     = 0;
            long   totalThinks      = 0;
            double totalNodeScore   = 0;
            int    totalSimulations = 0;
            int    totalDepth       = 0;
            int    totalNodes       = 0;
            int    movesMade        = 0;

            Stopwatch stopWatch = new Stopwatch();

            stopWatch.Start();
            Random             rng     = new Random(2);
            IEnumerator <Move> path    = null;
            TetriminoState     move    = new TetriminoState(-1, -1, 0);
            TetriminoState     invalid = new TetriminoState();

            while (window.IsOpen)
            {
                window.DispatchEvents();
                if (stopWatch.ElapsedMilliseconds > 50)
                {
                    if (path != null)
                    {
                        if (path.MoveNext())
                        {
                            Pathfinder.DoMove(tetris, path.Current);
                        }
                        else
                        {
                            tetris.HardDrop();
                            path  = null;
                            moves = pathfinder.FindAllMoves(tetris, 1, 1, 1);
                            move  = invalid;
                        }
                        window.Clear();
                        TetrisDrawer.DrawTetrisBoard(window, new Vector2f(0, 0), tetris, tetRNG, moves, move);
                        window.Display();
                    }
                    else
                    {
                        if (move.x != -1)
                        {
                            bot.Update(tetris);
                        }
                        int       thinks    = 0;
                        Stopwatch stopwatch = new Stopwatch();
                        Task      thinkTask = Task.Run(() => {
                            stopwatch.Start();
                            while (stopwatch.ElapsedMilliseconds < 100)
                            {
                                bot.Think();
                                thinks += 1;
                            }
                            stopWatch.Stop();
                        });
                        while (!thinkTask.IsCompleted)
                        {
                            window.DispatchEvents();
                        }
                        if (thinkTask.Exception != null)
                        {
                            throw thinkTask.Exception;
                        }
                        MinoBot.MonteCarlo.Node node = bot.GetMove();
                        long elapsed = stopwatch.ElapsedMilliseconds;
                        totalThinkMS     += elapsed;
                        totalThinks      += thinks;
                        totalNodeScore   += node.score;
                        totalSimulations += node.simulations;
                        totalDepth       += bot.maxDepth;
                        int nodes = 0;

                        /*
                         * Queue<MinoBot.MonteCarlo.Node> nodeQueue = new Queue<MinoBot.MonteCarlo.Node>();
                         * nodeQueue.Enqueue(bot.tree.root);
                         * while (nodeQueue.TryDequeue(out MinoBot.MonteCarlo.Node n)) {
                         *  foreach (MinoBot.MonteCarlo.Node child in n.children) {
                         *      nodeQueue.Enqueue(child);
                         *  }
                         *  nodes += 1;
                         * }
                         */
                        totalNodes += nodes;
                        movesMade  += 1;

                        /*
                         * Console.Clear();
                         * Console.WriteLine($"Move {movesMade}.");
                         * Console.WriteLine($"Thought for {elapsed}ms (avg: {totalThinkMS / (double) movesMade})");
                         * Console.WriteLine($"{thinks} thinks (avg: {totalThinks / (double) movesMade})");
                         * Console.WriteLine($"{elapsed / (double) thinks } ms per think (avg: {totalThinkMS / (double) totalThinks})");
                         * Console.WriteLine($"Depth: {bot.maxDepth} (avg: {totalDepth / (double) movesMade}).");
                         * Console.WriteLine($"Nodes: {nodes} (avg: {totalNodes / (double )movesMade})");
                         * Console.WriteLine("Selected node has:");
                         * Console.WriteLine($"  score: {node.score} (avg: {totalNodeScore / movesMade})");
                         * Console.WriteLine($"  simulations: {node.simulations} (avg: {totalSimulations / (double) movesMade})");
                         */
                        move = node.move;
                        if (node.state.usesHeld)
                        {
                            tetris.Hold();
                            moves = pathfinder.FindAllMoves(tetris, 1, 1, 1);
                        }
                        pathfinder.FindAllMoves(tetris, 1, 1, 1);
                        List <Move> pathList = pathfinder.GetPath(move.x, move.y, move.rot);

                        /*
                         * Console.Clear();
                         * //Console.WriteLine(move.x + ", " + move.y + ", " + move.rot);
                         * foreach (Move mv in pathList) {
                         *  Console.WriteLine(Enum.GetName(mv.GetType(), mv));
                         * }
                         */
                        path = pathList.GetEnumerator();
                    }
                    stopWatch.Restart();
                }
            }
        }