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); } }
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, },
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(); } } }