public void AISelect() { // wait for previous ai thread to stop while (AI.RUNNING) { Thread.Sleep(100); } // ai is dump this.m_UI.SetStatus(true, "Thinking..."); // calculate move move_t move = AI.MiniMaxAB(this.Board, this.Turn); // if valid move, make the move if (move.to.letter >= 0 && move.to.number >= 0) { MakeMove(move); } else // if invalid move { if (!AI.STOP) // and not caused by AI interupt { // fuuuuuu this.m_UI.LogMove("Null Move\n"); } } bool checkmate = false; // if the AI wasn't interupted finish our turn if (!AI.STOP) { switchPlayer(); checkmate = detectCheckmate(); } // we're done now AI.RUNNING = false; // if the AI wan't interupted // and we're in AI vs AI mode // and not in checkmate/stalemate // start the next AI's turn if (!AI.STOP && this.m_nPlayers == 0 && !checkmate) { new Thread(AISelect).Start(); } }
public static bool isEnPassant(ChessBoard b, move_t m) { // step = where opposite pawn is int step = ((b.Grid[m.from.number][m.from.letter].player == Player.WHITE) ? -1 : 1); // true if // move is pawn // space is blank // move is diagonal // opposite pawn exists at step // the last move for opposite player was the pawn // the last move for opposite pawn was the double jump return( b.Grid[m.from.number][m.from.letter].piece == Piece.PAWN && b.Grid[m.to.number][m.to.letter].piece == Piece.NONE && m.to.letter != m.from.letter && b.Grid[m.to.number + step][m.to.letter].piece == Piece.PAWN && b.LastMove[(b.Grid[m.from.number][m.from.letter].player == Player.WHITE) ? Player.BLACK : Player.WHITE].Equals(new position_t(m.to.letter, m.to.number + step)) && Math.Abs(b.Grid[m.to.number + step][m.to.letter].lastPosition.number - (m.to.number + step)) == 2 //jumped from last position ); }
private void MakeMove(move_t m) { // start move log output string move = (this.Turn == Player.WHITE) ? "W" : "B"; move += ":\t"; // piece switch (this.Board.Grid[m.from.number][m.from.letter].piece) { case Piece.PAWN: move += "P"; break; case Piece.ROOK: move += "R"; break; case Piece.KNIGHT: move += "k"; break; case Piece.BISHOP: move += "B"; break; case Piece.QUEEN: move += "Q"; break; case Piece.KING: move += "K"; break; } // kill if (this.Board.Grid[m.to.number][m.to.letter].piece != Piece.NONE || LegalMoveSet.isEnPassant(this.Board, m)) { move += "x"; } // letter switch (m.to.letter) { case 0: move += "a"; break; case 1: move += "b"; break; case 2: move += "c"; break; case 3: move += "d"; break; case 4: move += "e"; break; case 5: move += "f"; break; case 6: move += "g"; break; case 7: move += "h"; break; } // number move += (m.to.number + 1).ToString(); // update board / make actual move this.Board = LegalMoveSet.move(this.Board, m); // if that move put someone in check if (LegalMoveSet.isCheck(this.Board, (Turn == Player.WHITE) ? Player.BLACK : Player.WHITE)) { move += "+"; } // show log this.m_UI.LogMove(move + "\n"); }
public static move_t MiniMaxAB(ChessBoard board, Player turn) { RUNNING = true; // we've started running STOP = false; // no interupt command sent MAX = turn; // who is maximizing // gather all possible moves Dictionary <position_t, List <position_t> > moves = LegalMoveSet.getPlayerMoves(board, turn); // because we're threading safely store best result from each thread int[] bestresults = new int[moves.Count]; move_t[] bestmoves = new move_t[moves.Count]; // thread the generation of each move Parallel.ForEach(moves, (movelist, state, index) => { if (STOP) // interupt { state.Stop(); return; } // initialize thread best bestresults[index] = int.MinValue; bestmoves[index] = new move_t(new position_t(-1, -1), new position_t(-1, -1)); // for each move for the current piece(thread) foreach (position_t move in movelist.Value) { if (STOP) // interupt { state.Stop(); return; } // make initial move and start recursion ChessBoard b2 = LegalMoveSet.move(board, new move_t(movelist.Key, move)); int result = mimaab(b2, (turn == Player.WHITE) ? Player.BLACK : Player.WHITE, 1, Int32.MinValue, Int32.MaxValue); // if result is better or best hasn't been set yet if (bestresults[index] < result || (bestmoves[index].to.Equals(new position_t(-1, -1)) && bestresults[index] == int.MinValue)) { bestresults[index] = result; bestmoves[index].from = movelist.Key; bestmoves[index].to = move; } } }); // interupted if (STOP) { return(new move_t(new position_t(-1, -1), new position_t(-1, -1))); } // find the best of the thread results int best = int.MinValue; move_t m = new move_t(new position_t(-1, -1), new position_t(-1, -1)); for (int i = 0; i < bestmoves.Length; i++) { if (best < bestresults[i] || (m.to.Equals(new position_t(-1, -1)) && !bestmoves[i].to.Equals(new position_t(-1, -1)))) { best = bestresults[i]; m = bestmoves[i]; } } return(m); }
/// <summary> /// Performs all necessary steps to update the game state and move the pieces. /// </summary> /// <param name="b">The state of the game.</param> /// <param name="m">The desired move.</param> /// <returns>The new state of the game.</returns> public static ChessBoard move(ChessBoard b, move_t m) { // create a copy of the board ChessBoard b2 = new ChessBoard(b); // determine if move is enpassant or castling bool enpassant = (b2.Grid[m.from.number][m.from.letter].piece == Piece.PAWN && isEnPassant(b2, m)); bool castle = (b2.Grid[m.from.number][m.from.letter].piece == Piece.KING && Math.Abs(m.to.letter - m.from.letter) == 2); // update piece list, remove old position from piece list for moving player b2.Pieces[b2.Grid[m.from.number][m.from.letter].player].Remove(m.from); // if move kills a piece directly, remove killed piece from killed player piece list if (b2.Grid[m.to.number][m.to.letter].piece != Piece.NONE && b2.Grid[m.from.number][m.from.letter].player != b2.Grid[m.to.number][m.to.letter].player) { b2.Pieces[b2.Grid[m.to.number][m.to.letter].player].Remove(m.to); } else if (enpassant) { // if kill was through enpassant determine which direction and remove the killed pawn int step = (b.Grid[m.from.number][m.from.letter].player == Player.WHITE) ? -1 : 1; b2.Pieces[b2.Grid[m.to.number + step][m.to.letter].player].Remove(new position_t(m.to.letter, m.to.number + step)); } else if (castle) { // if no kill but enpassant, update the rook position if (m.to.letter == 6) { b2.Pieces[b2.Grid[m.to.number][m.to.letter].player].Remove(new position_t(7, m.to.number)); b2.Pieces[b2.Grid[m.to.number][m.to.letter].player].Add(new position_t(5, m.to.number)); } else { b2.Pieces[b2.Grid[m.to.number][m.to.letter].player].Remove(new position_t(0, m.to.number)); b2.Pieces[b2.Grid[m.to.number][m.to.letter].player].Remove(new position_t(3, m.to.number)); } } // add the new piece location to piece list b2.Pieces[b2.Grid[m.from.number][m.from.letter].player].Add(m.to); // update board grid b2.Grid[m.to.number][m.to.letter] = new piece_t(b2.Grid[m.from.number][m.from.letter]); b2.Grid[m.to.number][m.to.letter].lastPosition = m.from; b2.Grid[m.from.number][m.from.letter].piece = Piece.NONE; if (enpassant) { // if kill was through enpassant determine which direction and remove the killed pawn int step = (b.Grid[m.from.number][m.from.letter].player == Player.WHITE) ? -1 : 1; b2.Grid[m.to.number + step][m.to.letter].piece = Piece.NONE; } else if (castle) { // if no kill but enpassant, update the rook position if (m.to.letter == 6) { b2.Grid[m.to.number][5] = new piece_t(b2.Grid[m.to.number][7]); b2.Grid[m.to.number][7].piece = Piece.NONE; } else { b2.Grid[m.to.number][3] = new piece_t(b2.Grid[m.to.number][0]); b2.Grid[m.to.number][0].piece = Piece.NONE; } } //promotion if (b2.Grid[m.to.number][m.to.letter].piece == Piece.PAWN) { for (int i = 0; i < 8; i++) { if (b2.Grid[0][i].piece == Piece.PAWN) { b2.Grid[0][i].piece = Piece.QUEEN; } if (b2.Grid[7][i].piece == Piece.PAWN) { b2.Grid[7][i].piece = Piece.QUEEN; } } } // update king position if (b2.Grid[m.to.number][m.to.letter].piece == Piece.KING) { b2.Kings[b2.Grid[m.to.number][m.to.letter].player] = m.to; } // update last move b2.LastMove[b2.Grid[m.to.number][m.to.letter].player] = m.to; return(b2); }
public static move_t MiniMaxAB(ChessBoard board, Player turn) { RUNNING = true; // we've started running STOP = false; // no interupt command sent MAX = turn; // who is maximizing // gather all possible moves Dictionary<position_t, List<position_t>> moves = LegalMoveSet.getPlayerMoves(board, turn); // because we're threading safely store best result from each thread int[] bestresults = new int[moves.Count]; move_t[] bestmoves = new move_t[moves.Count]; // thread the generation of each move Parallel.ForEach(moves, (movelist,state,index) => { if (STOP) // interupt { state.Stop(); return; } // initialize thread best bestresults[index] = int.MinValue; bestmoves[index] = new move_t(new position_t(-1, -1), new position_t(-1, -1)); // for each move for the current piece(thread) foreach (position_t move in movelist.Value) { if (STOP) // interupt { state.Stop(); return; } // make initial move and start recursion ChessBoard b2 = LegalMoveSet.move(board, new move_t(movelist.Key, move)); int result = mimaab(b2, (turn == Player.WHITE) ? Player.BLACK : Player.WHITE, 1, Int32.MinValue, Int32.MaxValue); // if result is better or best hasn't been set yet if (bestresults[index] < result || (bestmoves[index].to.Equals(new position_t(-1, -1)) && bestresults[index] == int.MinValue)) { bestresults[index] = result; bestmoves[index].from = movelist.Key; bestmoves[index].to = move; } } }); // interupted if (STOP) return new move_t(new position_t(-1, -1), new position_t(-1, -1)); // find the best of the thread results int best = int.MinValue; move_t m = new move_t(new position_t(-1, -1), new position_t(-1, -1)); for(int i = 0; i < bestmoves.Length; i++) { if (best < bestresults[i] || (m.to.Equals(new position_t(-1,-1)) && !bestmoves[i].to.Equals(new position_t(-1,-1)))) { best = bestresults[i]; m = bestmoves[i]; } } return m; }
public static bool isEnPassant(ChessBoard b, move_t m) { // step = where opposite pawn is int step = ((b.Grid[m.from.number][m.from.letter].player == Player.WHITE) ? -1 : 1); // true if // move is pawn // space is blank // move is diagonal // opposite pawn exists at step // the last move for opposite player was the pawn // the last move for opposite pawn was the double jump return ( b.Grid[m.from.number][m.from.letter].piece == Piece.PAWN && b.Grid[m.to.number][m.to.letter].piece == Piece.NONE && m.to.letter != m.from.letter && b.Grid[m.to.number + step][m.to.letter].piece == Piece.PAWN && b.LastMove[(b.Grid[m.from.number][m.from.letter].player == Player.WHITE) ? Player.BLACK : Player.WHITE].Equals(new position_t(m.to.letter, m.to.number + step)) && Math.Abs(b.Grid[m.to.number + step][m.to.letter].lastPosition.number - (m.to.number + step)) == 2 //jumped from last position ); }
/// <summary> /// Performs all necessary steps to update the game state and move the pieces. /// </summary> /// <param name="b">The state of the game.</param> /// <param name="m">The desired move.</param> /// <returns>The new state of the game.</returns> public static ChessBoard move(ChessBoard b, move_t m) { // create a copy of the board ChessBoard b2 = new ChessBoard(b); // determine if move is enpassant or castling bool enpassant = (b2.Grid[m.from.number][m.from.letter].piece == Piece.PAWN && isEnPassant(b2, m)); bool castle = (b2.Grid[m.from.number][m.from.letter].piece == Piece.KING && Math.Abs(m.to.letter - m.from.letter) == 2); // update piece list, remove old position from piece list for moving player b2.Pieces[b2.Grid[m.from.number][m.from.letter].player].Remove(m.from); // if move kills a piece directly, remove killed piece from killed player piece list if (b2.Grid[m.to.number][m.to.letter].piece != Piece.NONE && b2.Grid[m.from.number][m.from.letter].player != b2.Grid[m.to.number][m.to.letter].player) b2.Pieces[b2.Grid[m.to.number][m.to.letter].player].Remove(m.to); else if(enpassant) { // if kill was through enpassant determine which direction and remove the killed pawn int step = (b.Grid[m.from.number][m.from.letter].player == Player.WHITE) ? -1 : 1; b2.Pieces[b2.Grid[m.to.number + step][m.to.letter].player].Remove(new position_t(m.to.letter, m.to.number + step)); } else if (castle) { // if no kill but enpassant, update the rook position if (m.to.letter == 6) { b2.Pieces[b2.Grid[m.to.number][m.to.letter].player].Remove(new position_t(7, m.to.number)); b2.Pieces[b2.Grid[m.to.number][m.to.letter].player].Add(new position_t(5, m.to.number)); } else { b2.Pieces[b2.Grid[m.to.number][m.to.letter].player].Remove(new position_t(0, m.to.number)); b2.Pieces[b2.Grid[m.to.number][m.to.letter].player].Remove(new position_t(3, m.to.number)); } } // add the new piece location to piece list b2.Pieces[b2.Grid[m.from.number][m.from.letter].player].Add(m.to); // update board grid b2.Grid[m.to.number][m.to.letter] = new piece_t(b2.Grid[m.from.number][m.from.letter]); b2.Grid[m.to.number][m.to.letter].lastPosition = m.from; b2.Grid[m.from.number][m.from.letter].piece = Piece.NONE; if (enpassant) { // if kill was through enpassant determine which direction and remove the killed pawn int step = (b.Grid[m.from.number][m.from.letter].player == Player.WHITE) ? -1 : 1; b2.Grid[m.to.number + step][m.to.letter].piece = Piece.NONE; } else if (castle) { // if no kill but enpassant, update the rook position if (m.to.letter == 6) { b2.Grid[m.to.number][5] = new piece_t(b2.Grid[m.to.number][7]); b2.Grid[m.to.number][7].piece = Piece.NONE; } else { b2.Grid[m.to.number][3] = new piece_t(b2.Grid[m.to.number][0]); b2.Grid[m.to.number][0].piece = Piece.NONE; } } //promotion if (b2.Grid[m.to.number][m.to.letter].piece == Piece.PAWN) { for (int i = 0; i < 8; i++) { if (b2.Grid[0][i].piece == Piece.PAWN) b2.Grid[0][i].piece = Piece.QUEEN; if (b2.Grid[7][i].piece == Piece.PAWN) b2.Grid[7][i].piece = Piece.QUEEN; } } // update king position if (b2.Grid[m.to.number][m.to.letter].piece == Piece.KING) { b2.Kings[b2.Grid[m.to.number][m.to.letter].player] = m.to; } // update last move b2.LastMove[b2.Grid[m.to.number][m.to.letter].player] = m.to; return b2; }