// Handle player selection and movement private void picBoard_MouseClick(object sender, MouseEventArgs e) { if (!picBoard.Enabled) { return; } stopai = false; // Get mouse position relative to the game board int mousex = (int)(picBoard.PointToClient(Cursor.Position).X); int mousey = (int)(picBoard.PointToClient(Cursor.Position).Y); // Select or deselect a piece if (e.Button == MouseButtons.Left) { if (playerTurn || type == GameType.Human) { GamePiece clicked = board.GetPieceAtPos(Board.CoordToIndex(mousex), Board.CoordToIndex(mousey)); foreach (GamePiece gp in board) { if (gp == clicked) { continue; } gp.Selected = false; } if (clicked.Selected) { clicked.Selected = false; } else { clicked.Selected = true; } } } // Move selected piece else if (e.Button == MouseButtons.Right) { // Represents whose turn it is int pcode; if (type == GameType.Human && !playerTurn) { pcode = 2; } else { pcode = 1; } bool moved = board.MoveSelectedPiece(Board.CoordToIndex(mousex), Board.CoordToIndex(mousey), pcode); if (!moved) { return; } // Set up next player's turn playerTurn = !playerTurn; board.SelectedPiece().Selected = false; picBoard.Invalidate(); // If the next player can't move, fill in all reachable empty spaces if (board.Count(pcode == 1 ? 2 : 1) != 0 && !board.HasMovesLeft((pcode == 1 ? 2 : 1))) { MessageBox.Show("The next player has no moves left."); board.FillEmptySpaces(pcode); endgame = true; } // If player 2 is not human, tell AI to move UpdateGameStats(playerTurn, "Thinking..."); if (picBoard.Enabled && type != GameType.Human) { DoAI(2); } } picBoard.Invalidate(); }
// Controls AI's Decision Making public override void MakeMove() { #region Variables //int aCount = board.Count(aicode); // AI count int naCount = board.Count(notaicode); // Opponent count int greatest = -1; // Count for best move int movex = 0, movey = 0, curx = 0, cury = 0; // Coordinates for move position and current position int[][] positioncheck = new int[][] { new[] { 0, 1 }, new[] { 1, 1 }, new[] { 1, 0 }, new[] { 1, -1 }, new[] { 0, -1 }, new[] { -1, -1 }, new[] { -1, 0 }, new[] { -1, 1 }, new[] { 0, 2 }, new[] { 2, 2 }, new[] { 2, 0 }, new[] { 2, -2 }, new[] { 0, -2 }, new[] { -2, -2 }, new[] { -2, 0 }, new[] { -2, 2 } }; // Array of possible relative move locations bool jumped = false; // Whether or not the current best move is a jump int gain = 0; // Conversion count from simulated move int defence = 0; // Number of pieces "saved" by simulated move Random r = new Random(); // To add variety to games bool killedEnemy = false; // True when a move is found that wipes out all enemy pieces int maxX = Board.SIZE_X - 1, maxY = Board.SIZE_Y - 1; // Bounds of the board #endregion #region Offensive Moves foreach (GamePiece gp in board) { if (gp.Code == aicode) { for (int i = 0; i < 16; i++) // i>=8 means simulated move is a jump { // Make sure adjacent spot is a valid position if (gp.x + positioncheck[i][0] <= maxX && gp.x + positioncheck[i][0] >= 0 && gp.y + positioncheck[i][1] <= maxY && gp.y + positioncheck[i][1] >= 0) { if (board[gp.x + positioncheck[i][0], gp.y + positioncheck[i][1]] != 0) { continue; } } else { continue; } // Count the gain from each possible move gain = board.Convert(gp.x + positioncheck[i][0], gp.y + positioncheck[i][1], notaicode, notaicode); // Break from search if move that wipes out all pieces is found if (gain - (i >= 8 ? 0 : 1) == naCount) { killedEnemy = true; } if (i < 8 && !killedEnemy) { gain += 1; // Take duplication into account #region Defend Vulnerable Spots // Fill vulnerable holes if (gain <= 2) { defence = board.Convert(gp.x + positioncheck[i][0], gp.y + positioncheck[i][1], aicode, aicode); if (defence >= 6) { // If spot is valuable, check if it's in range of enemy pieces for (int j = 8; j < 16; j++) { // gp.x + positioncheck(i) = checked spot, + positioncheck(j) = area around checked spot int newspotx = gp.x + positioncheck[i][0] + positioncheck[j][0]; int newspoty = gp.y + positioncheck[i][1] + positioncheck[j][1]; if (newspotx <= maxX && newspotx >= 0 && newspoty <= maxY && newspoty >= 0) { // If it finds a green piece in range if (board[newspotx, newspoty] == notaicode) { // Set gain to the number of pieces saved gain = defence; break; } } } } } #endregion } else if (i >= 8 && !killedEnemy) { #region Check For Vulnerable Jumping // If jumping, check if leaving a vulnerable spot if (gain >= greatest && gain != naCount) { int adjacentPlayers = board.Convert(gp.x, gp.y, aicode, aicode); if (adjacentPlayers >= 5) { // If spot is valuable, check if it's in range of enemy pieces for (int j = 0; j < 16; j++) { if (gp.x + positioncheck[j][0] <= maxX && gp.x + positioncheck[j][0] >= 0 && gp.y + positioncheck[j][1] <= maxY && gp.y + positioncheck[j][1] >= 0) { // If it finds a green piece in range and the simulated move does not consume it if (board[gp.x + positioncheck[j][0], gp.y + positioncheck[j][1]] == notaicode && !(Math.Abs(positioncheck[i][0] - positioncheck[j][0]) == 1 || Math.Abs(positioncheck[i][1] - positioncheck[j][1]) == 1)) { // Penalize gain gain -= (adjacentPlayers - 6); if (j < 8) { gain -= 1; } if (gain < 1) { gain = 1; } break; } } } } } #endregion } if (gain >= greatest || killedEnemy) { // Duplication is better than jumping if resulting count is equal if (gain == greatest) { if (!jumped && i >= 8 && !killedEnemy) { continue; } if (r.Next(2) == 0 && !killedEnemy) // So AI doesn't do the same thing every game { continue; } } // Save best move jumped = i >= 8; greatest = gain; curx = gp.x; cury = gp.y; movex = gp.x + positioncheck[i][0]; movey = gp.y + positioncheck[i][1]; if (killedEnemy) { break; } } } if (killedEnemy) { break; } } } #endregion #region Block Enemy Moves // If found no good moves if (greatest == 1 && !killedEnemy) { jumped = false; greatest = 0; // Block enemy movement instead of capturing foreach (GamePiece gp in board) { if (gp.Code == notaicode) { // Check for empty spaces within jumping distance of enemy for (int i = 8; i < 16; i++) { // Make sure checked spot is in bounds if (!(gp.x + positioncheck[i][0] <= maxX && gp.x + positioncheck[i][0] >= 0 && gp.y + positioncheck[i][1] <= maxY && gp.y + positioncheck[i][1] >= 0)) { continue; } // If spot is found if (board[gp.x + positioncheck[i][0], gp.y + positioncheck[i][1]] == 0) { // Search for a piece that can block the spot int newx = gp.x + positioncheck[i][0]; int newy = gp.y + positioncheck[i][1]; GamePiece blocker = null; for (int j = 0; j < 8; j++) { if (!(newx + positioncheck[j][0] <= maxX && newx + positioncheck[j][0] >= 0 && newy + positioncheck[j][1] <= maxY && newy + positioncheck[j][1] >= 0)) { continue; } // Save piece if found if (board[newx + positioncheck[j][0], newy + positioncheck[j][1]] == aicode) { blocker = board.GetPieceAtPos(newx + positioncheck[j][0], newy + positioncheck[j][1]); break; } } // If space can't be blocked, look for more if (blocker == null) { continue; } int loss = board.Convert(newx, newy, aicode, aicode); // Use move that blocks the most valuable spot if (loss >= greatest) { greatest = loss; curx = blocker.x; cury = blocker.y; movex = newx; movey = newy; } } } } } } #endregion // Execute the saved move if (jumped) { board[curx, cury] = 0; } board[movex, movey] = aicode; board.Convert(movex, movey, aicode, notaicode); }