public void GetMoves_WithMoveUnder_6candidates() { var field = Field.Create(0, 0, 0, @" .XX....... XX........ "); var generator = new MoveGenerator(); var candiates = generator.GetMoves(field, Block.S).Select(m => new MoveInstruction(m.Path.Moves.ToArray())).ToList(); var act = candiates.Select(c => c.ToString()).ToArray(); var exp = new string[] { "drop", "down,left,drop", "down,right,drop", "down,right,right,drop", "down,right,right,right,drop", "down,right,right,right,right,drop", }; foreach (var a in act) { Console.WriteLine(a); } CollectionAssert.AreEqual(exp, act); }
public void GetMoves_WithMoveUnder_6candidates() { var field = Field.Create(0, 0, 0, @" .XX....... XX........ "); var generator = new MoveGenerator(); var candiates = generator.GetMoves(field, Block.S).Select(m=> new MoveInstruction(m.Path.Moves.ToArray())).ToList(); var act = candiates.Select(c => c.ToString()).ToArray(); var exp = new string[] { "drop", "down,left,drop", "down,right,drop", "down,right,right,drop", "down,right,right,right,drop", "down,right,right,right,right,drop", }; foreach (var a in act) { Console.WriteLine(a); } CollectionAssert.AreEqual(exp, act); }
// Sets values for initial board position private void SetStartingBoardData() { Parent = null; WhiteEligibleSquares = ~WhitePieces; BlackEligibleSquares = ~BlackPieces; BlackMoves = MoveGenerator.GetMoves(this); TurnIsWhite = true; WhiteMoves = MoveGenerator.GetMoves(this); BlackAttacks = GetBlackAttacks(); WhiteAttacks = GetWhiteAttacks(); this.WhiteInCheck = IsWhiteInCheck(); this.BlackInCheck = IsBlackInCheck(); CanWhiteCastle = true; CanBlackCastle = true; WhiteInCheck = false; BlackInCheck = false; Number = 1; Value = 0; }
public int CountLegalMoves(Board board, int depth) { if (depth == 0) { return(1); } var count = 0; var generator = new MoveGenerator(board); generator.Setup(); foreach (var move in generator.GetMoves(false)) { board.SubmitMove(move); count += CountLegalMoves(board, depth - 1); board.UndoMove(); } if (board.states.Count == 1) { Debug.WriteLine($"depth: {board.states.Count}, count: {count}, after {board.states.Peek().lastMove.ToAlgebraicNotation()}"); } return(count); }
public static void SetPosition(string[] line) { if (line[1] == "startpos") { board = new Board(); ai = new AI(board); } else { throw new Exception("UNSUPPORTED"); } for (int i = 3; i < line.Length; i++) { var moveAlg = line[i]; Move theMove = null; var gen = new MoveGenerator(board); gen.Setup(); foreach (var move in gen.GetMoves(false)) { if (move.ToAlgebraicNotation().Equals(moveAlg)) { theMove = move; break; } } if (theMove == null) { Console.WriteLine("Move " + moveAlg + " was not found"); } board.SubmitMove(theMove); } }
public void Ensure_king_can_just_step_away_from_check() { var target = new MoveGenerator(GameState.FromForsythEdwardsNotation("1K5r/8/1k6/8/8/8/8/8 w - -")); var moves = target.GetMoves(); Assert.IsFalse(moves.Contains(new Move(Cell.b8, Cell.a8))); }
// Update Methods // Changes all board data to coorespond to the state after white moves private void InitializeWhitesChildBoard(Board Parent, Tuple <ulong, ulong> Move) { this.Parent = Parent; this.Move = Move; this.WhiteKing = (Parent.WhiteKing & Move.Item1) > 0 ? Parent.WhiteKing ^ Move.Item1 | Move.Item2 : Parent.WhiteKing; this.WhiteQueen = (Parent.WhiteQueen & Move.Item1) > 0 ? Parent.WhiteQueen ^ Move.Item1 | Move.Item2 : Parent.WhiteQueen; this.WhiteRooks = (Parent.WhiteRooks & Move.Item1) > 0 ? Parent.WhiteRooks ^ Move.Item1 | Move.Item2 : Parent.WhiteRooks; this.WhiteBishops = (Parent.WhiteBishops & Move.Item1) > 0 ? Parent.WhiteBishops ^ Move.Item1 | Move.Item2 : Parent.WhiteBishops; this.WhiteKnights = (Parent.WhiteKnights & Move.Item1) > 0 ? Parent.WhiteKnights ^ Move.Item1 | Move.Item2 : Parent.WhiteKnights; this.WhitePawns = (Parent.WhitePawns & Move.Item1) > 0 ? Parent.WhitePawns ^ Move.Item1 | Move.Item2 : Parent.WhitePawns; this.WhitePieces |= this.WhiteKing; this.WhitePieces |= this.WhiteQueen; this.WhitePieces |= this.WhiteRooks; this.WhitePieces |= this.WhiteBishops; this.WhitePieces |= this.WhiteKnights; this.WhitePieces |= this.WhitePawns; this.BlackKing = (Parent.BlackKing & Move.Item2) > 0 ? Parent.BlackKing ^ Move.Item2: Parent.BlackKing; this.BlackQueen = (Parent.BlackQueen & Move.Item2) > 0 ? Parent.BlackQueen ^ Move.Item2 : Parent.BlackQueen; this.BlackRooks = (Parent.BlackRooks & Move.Item2) > 0 ? Parent.BlackRooks ^ Move.Item2 : Parent.BlackRooks; this.BlackBishops = (Parent.BlackBishops & Move.Item2) > 0 ? Parent.BlackBishops ^ Move.Item2 : Parent.BlackBishops; this.BlackKnights = (Parent.BlackKnights & Move.Item2) > 0 ? Parent.BlackKnights ^ Move.Item2 : Parent.BlackKnights; this.BlackPawns = (Parent.BlackPawns & Move.Item2) > 0 ? Parent.BlackPawns ^ Move.Item2 : Parent.BlackPawns; this.BlackPieces |= this.BlackKing; this.BlackPieces |= this.BlackQueen; this.BlackPieces |= this.BlackRooks; this.BlackPieces |= this.BlackBishops; this.BlackPieces |= this.BlackKnights; this.BlackPieces |= this.BlackPawns; WhiteEligibleSquares = ~WhitePieces; BlackEligibleSquares = ~BlackPieces; WhiteMoves = MoveGenerator.GetMoves(this); this.TurnIsWhite = false; BlackMoves = MoveGenerator.GetMoves(this); this.BlackAttacks = GetBlackAttacks(); this.WhiteAttacks = GetWhiteAttacks(); this.WhiteInCheck = IsWhiteInCheck(); this.BlackInCheck = IsBlackInCheck(); this.CanWhiteCastle = true; this.CanBlackCastle = true; this.Value = this.CalcValue(); }
public void ZoomToPiece(bool suppress = false) { StartCoroutine(game.CenterCameraOnPiece(this)); if (!suppress) { List <Move> moveTargets = MoveGenerator.GetMoves(game.gameBoard, this); StartCoroutine(game.HighlightMoves(moveTargets, 1f)); } }
public void GetMovesCornerCase() { var board = new SquareBoard(4); var generator = new MoveGenerator(board); const int repeat = 1000; for (int i = 0; i < repeat; ++i) { var moves = generator.GetMoves(); Assert.IsTrue(moves.Length == 2); } }
private static void AssertGetMoves(string[] exp, Block block) { var generator = new MoveGenerator(); var moves = generator.GetMoves(TestData.Small, block).Select(candidate => candidate.Path.ToString()).ToArray(); foreach (var move in moves) { Console.WriteLine('"' + move + '"' + ','); } Assert.AreEqual(block.ChildCount, moves.Length, "GetMoves for {0} has the wrong number of answers.", block.Name); CollectionAssert.AreEqual(exp, moves); }
public Move WaitMove(Position position, Score score) { PrintPosition(position, score); StringBuilder candidates = new StringBuilder("合法手: "); Stopwatch sw = new Stopwatch(); sw.Start(); foreach (var move in MoveGenerator.GetMoves(position)) { candidates.AppendFormat("{0}{1}{2} ", move.DstIndex, move.PieceType.ToPieceName(), move.Promote ? "成" : ""); } sw.Stop(); Console.WriteLine(candidates); Console.WriteLine(String.Format("Time to get legal moves: {0}", sw.Elapsed)); return(position.Turn == Color.Black ? black.WaitMove(position) : white.WaitMove(position)); }
public static int CountMoves(Board board, MoveGenerator generator, uint us, int depth) { if (depth == 0) { return(1); } var count = 0; var them = Piece.OtherColor(us); generator.Setup(); foreach (var move in generator.GetMoves(false)) { board.SubmitMove(move); count += CountMoves(board, generator, them, depth - 1); board.UndoMove(); } return(count); }
private static void AssertGetFields(string[] exp, Block block) { var generator = new MoveGenerator(); var candidateFields = generator.GetMoves(TestData.Small, block).Select(candidate => candidate.Field.ToString()).ToArray(); var fields = generator.GetFields(TestData.Small, block).Select(field => field.ToString()).ToArray(); foreach (var field in candidateFields) { Console.WriteLine('"' + field + '"' + ','); } foreach (var field in candidateFields) { Console.WriteLine(field.Replace("|", Environment.NewLine)); Console.WriteLine(); } Assert.AreEqual(block.ChildCount, candidateFields.Length, "GetMoves for {0} has the wrong number of answers.", block.Name); CollectionAssert.AreEqual(exp, candidateFields, "Moves"); CollectionAssert.AreEqual(exp, fields, "Fields"); }
public void GetMoves_AlmostFilledField_21candidates() { var field = Field.Create(0, 0, 0, @" .......... .......... XXXX.XX... .XXXXXXXXX XXXX..XX.X XXX.XXXXXX XXX.XXXXXX XXX.XXXXXX XXX.XXXXXX XXX.XXXXXX XXX.XXXXXX XXX.XXXXXX XXXXX.XXXX .XXXX.XXXX"); var generator = new MoveGenerator(); var candiates = generator.GetMoves(field, Block.J).ToList(); Assert.AreEqual(8 + 8 + 3 + 2, candiates.Count); }
public void GetMoves_() { var field = Field.Create(0, 0, 0, @" .......XXX .........X .........X X........X XX.......X XX.......X XX......XX XXX.....XX XXXXXX..XX XXXXXX.XXX XXXXXX.XXX XXX.XX.XXX XXXXXX.XXX XXXXXX.XXX X.XXXXXXXX XXXXXX.XXX"); var generator = new MoveGenerator(); var candiates = generator.GetMoves(field, Block.I).ToList(); }
public Move GetRandomMove(GameBoardState gameBoard, Player player) { PieceState piece; List <Move> moves = new List <Move>(); int i = 0; do { piece = gameBoard.AlivePieces[sysRandom.Next(0, gameBoard.AlivePieces.Count)]; moves = MoveGenerator.GetMoves(gameBoard, piece); i++; }while ((piece.player != player || moves.Count == 0) && i < 10000 && gameBoard.AlivePieces.Count > 0); if (i == 10000) { throw new System.Exception("no move found " + i + " " + gameBoard.AlivePieces.Count); } return(moves[sysRandom.Next(0, moves.Count)]); }
public void SquareClickEvent(int level, int row, int col) { if (game.currentPlayer.playerType == Player.PlayerType.Human) { ISpaceState s = game.gameBoard.GetSpace(level, row, col); bool moved = false; if (moveTargets != null) { foreach (Move m in moveTargets) { if (m.space == s) { game.currentPlayer.selectedMove = m; game.currentPlayer.playerState = Player.PlayerState.Moving; moved = true; break; } } moveTargets = null; } ResetButtonHighlights(); if (s.IsOccupied() && !moved && s.Occupier().GetPlayer() == game.currentPlayer.playerNumber) { game.gameBoard.GetPiece(s.Occupier()).ZoomToPiece(); moveTargets = MoveGenerator.GetMoves(game.gameBoard, s.Occupier()); foreach (Move m in moveTargets) { squareLookup[m.space.GetLevel()][m.space.GetRow()][m.space.GetCol()].color = Color.cyan; } } } }
/// <summary> /// select a random piece until a legal list of moves is found. select a random move from the list. /// </summary> public Move GetRandomMove(Player player) { Piece piece; List <Move> moves = new List <Move>(); int i = 0; do { piece = gameBoard.AlivePieces[sysRandom.Next(0, gameBoard.AlivePieces.Count)]; moves = MoveGenerator.GetMoves(gameBoard, piece); i++; }while ((piece.player != player || moves.Count == 0) && i < 10000 && gameBoard.AlivePieces.Count > 0); if (i == 10000) { throw new System.Exception("no move found " + i + " " + gameBoard.AlivePieces.Count); } piece.space.AnimateShell(MoveTime, Color.red); StartCoroutine(HighlightMoves(moves)); return(moves[sysRandom.Next(0, moves.Count)]); }
/// Returns a list of moves given a pgn string. /// Note that Board should be set to whatever starting position of pgn is. public static List <ushort> MovesFromPGN(string pgn) { List <string> moveStrings = MoveStringsFromPGN(pgn); List <ushort> allMoves = new List <ushort> (); MoveGenerator moveGen = new MoveGenerator(); for (int i = 0; i < moveStrings.Count; i++) { string moveString = moveStrings[i]; moveString = moveString.Replace("+", ""); // remove check symbol moveString = moveString.Replace("#", ""); // remove mate symbol moveString = moveString.Replace("x", ""); // remove capture symbol string moveStringLower = moveStrings[i].ToLower(); ushort[] movesInPosition = moveGen.GetMoves(false, false).moves; ushort move = 0; for (int j = 0; j < movesInPosition.Length; j++) { move = movesInPosition[j]; int moveFromIndex = move & 127; int moveToIndex = (move >> 7) & 127; int movePieceType = Board.boardArray[moveFromIndex] & ~1; int colourCode = Board.boardArray[moveFromIndex] & 1; if (moveStringLower == "oo") // castle kingside { if (movePieceType == Board.kingCode && moveToIndex - moveFromIndex == 2) { break; } } else if (moveStringLower == "ooo") // castle queenside { if (movePieceType == Board.kingCode && moveToIndex - moveFromIndex == -2) { break; } } else if (Definitions.fileNames.Contains(moveString[0] + "")) // pawn move if starts with any file indicator (e.g. 'e'4. Note that uppercase B is used for bishops) { if (movePieceType != Board.pawnCode) { continue; } if (Definitions.FileNumberFromAlgebraicName(moveStringLower[0]) == Board.FileFrom128(moveFromIndex)) // correct starting file { if (moveString.Contains("=")) // is promotion { char promotionChar = moveStringLower[moveStringLower.Length - 1]; int promotionPieceIndex = move >> 14 & 3; int promotionPieceCode = Board.pieceCodeArray [promotionPieceIndex]; if ((promotionPieceCode == Board.queenCode && promotionChar != 'q') || (promotionPieceCode == Board.rookCode && promotionChar != 'r') || (promotionPieceCode == Board.bishopCode && promotionChar != 'b') || (promotionPieceCode == Board.knightCode && promotionChar != 'n')) { continue; // skip this move, incorrect promotion type } break; } else { char targetFile = moveString[moveString.Length - 2]; char targetRank = moveString[moveString.Length - 1]; if (Definitions.FileNumberFromAlgebraicName(targetFile) == Board.FileFrom128(moveToIndex)) // correct ending file { if (Definitions.RankNumberFromAlgebraicName(targetRank) == Board.RankFrom128(moveToIndex)) // correct ending rank { break; } } } } } else // regular piece move { char movePieceChar = moveString[0]; if (!(movePieceType == Board.queenCode && movePieceChar == 'Q') && !(movePieceType == Board.rookCode && movePieceChar == 'R') && !(movePieceType == Board.bishopCode && movePieceChar == 'B') && !(movePieceType == Board.knightCode && movePieceChar == 'N') && !(movePieceType == Board.kingCode && movePieceChar == 'K')) { continue; // skip this move, incorrect move piece type } char targetFile = moveString[moveString.Length - 2]; char targetRank = moveString[moveString.Length - 1]; if (Definitions.FileNumberFromAlgebraicName(targetFile) == Board.FileFrom128(moveToIndex)) // correct ending file { if (Definitions.RankNumberFromAlgebraicName(targetRank) == Board.RankFrom128(moveToIndex)) // correct ending rank { if (moveString.Length == 4) // addition char present for disambiguation (e.g. Nbd7 or R7e2) { char disambiguationChar = moveString[1]; if (Definitions.fileNames.Contains(disambiguationChar + "")) // is file disambiguation { if (Definitions.FileNumberFromAlgebraicName(disambiguationChar) != Board.FileFrom128(moveFromIndex)) // incorrect starting file { continue; } } else // is rank disambiguation { if (Definitions.RankNumberFromAlgebraicName(disambiguationChar) != Board.RankFrom128(moveFromIndex)) // incorrect starting rank { continue; } } } break; } } } } if (move == 0) // move is illegal; discard and return moves up to this point { UnityEngine.Debug.Log(moveString); break; } else { allMoves.Add(move); } Board.MakeMove(move); } for (int i = allMoves.Count - 1; i >= 0; i--) { Board.UnmakeMove(allMoves[i]); } return(allMoves); }
public static int Evaluate() { Timer.Start("Eval"); int materialEval = 0; int mobilityEval = 0; int developmentEval = 0; int kingSafetyEval = 0; // piece index vars (assigned when found to be used in later calculations) int whiteKingSquareIndex = -1; int blackKingSquareIndex = -1; List <int> whiteQueenIndices = new List <int> (2); List <int> whiteRookIndices = new List <int> (2); List <int> whiteKnightIndices = new List <int> (2); List <int> whiteBishopIndices = new List <int> (2); List <int> whitePawnIndices = new List <int> (8); List <int> blackQueenIndices = new List <int> (2); List <int> blackRookIndices = new List <int> (2); List <int> blackKnightIndices = new List <int> (2); List <int> blackBishopIndices = new List <int> (2); List <int> blackPawnIndices = new List <int> (8); for (int squareIndex = 0; squareIndex <= 127; squareIndex++) { if ((squareIndex & 8) != 0) // don't look at indices which are not on the real board { continue; } if (Board.boardArray[squareIndex] != 0) { int pieceCode = Board.boardArray[squareIndex]; if (pieceCode == Board.kingCode + 1) // found white king { whiteKingSquareIndex = squareIndex; } else if (pieceCode == Board.kingCode) // found black king { blackKingSquareIndex = squareIndex; } else // non-king pieces { materialEval += pieceValues[pieceCode]; // count material (excluding kings) switch (pieceCode) { case Board.queenCode + 1: whiteQueenIndices.Add(squareIndex); break; case Board.rookCode + 1: whiteRookIndices.Add(squareIndex); break; case Board.knightCode + 1: whiteKnightIndices.Add(squareIndex); break; case Board.bishopCode + 1: whiteBishopIndices.Add(squareIndex); break; case Board.pawnCode + 1: whitePawnIndices.Add(squareIndex); break; case Board.queenCode: blackQueenIndices.Add(squareIndex); break; case Board.rookCode: blackRookIndices.Add(squareIndex); break; case Board.knightCode: blackKnightIndices.Add(squareIndex); break; case Board.bishopCode: blackBishopIndices.Add(squareIndex); break; case Board.pawnCode: blackPawnIndices.Add(squareIndex); break; } } } } if (whiteKingSquareIndex == -1) // return best score for black if white's king has been captured (this may sometimes be allowed during alphabeta search for faster move generation) //return int.MinValue; { } // piece mobility moveGenerator.SetMoveColour(1); mobilityEval += moveGenerator.GetMoves(false, true, false).Count; // white piece mobility mobilityEval += moveGenerator.GetMoves(true, true, false).Count; // white piece attacking black moveGenerator.SetMoveColour(0); mobilityEval -= moveGenerator.GetMoves(false, true, false).Count; // black piece mobility mobilityEval -= moveGenerator.GetMoves(true, true, false).Count; // black piece attacking white // piece development white for (int i = 0; i < whiteKnightIndices.Count; i++) { if (Board.RankFrom128(whiteKnightIndices[i]) == 1) // penalize knight remaining on first rank { developmentEval -= 50; } else if (Board.RankFrom128(whiteKnightIndices[i]) == 2) // penalize knight remaining on second rank { developmentEval -= 10; } if (Board.FileFrom128(whiteKnightIndices[i]) == 1) // knights on the rim are dim { developmentEval -= 5; } else if (Board.FileFrom128(whiteKnightIndices[i]) == 8) // knights on the rim are dim { developmentEval -= 5; } } for (int i = 0; i < whiteBishopIndices.Count; i++) { if (Board.RankFrom128(whiteBishopIndices[i]) == 1) // penalize bishop remaining on first rank { developmentEval -= 50; } } // piece development black for (int i = 0; i < blackKnightIndices.Count; i++) { if (Board.RankFrom128(blackKnightIndices[i]) == 8) // penalize knight remaining on eighth rank { developmentEval += 50; } else if (Board.RankFrom128(blackKnightIndices[i]) == 7) // penalize knight remaining on seventh rank { developmentEval += 10; } if (Board.FileFrom128(blackKnightIndices[i]) == 1) // knights on the rim are dim { developmentEval += 5; } else if (Board.FileFrom128(blackKnightIndices[i]) == 8) // knights on the rim are dim { developmentEval += 5; } } for (int i = 0; i < blackBishopIndices.Count; i++) { if (Board.RankFrom128(blackBishopIndices[i]) == 8) // penalize bishop remaining on eighth rank { developmentEval += 50; } } // king safety white if (Board.WhiteHasCastlingRights()) { kingSafetyEval += 10; // not safe, but at least retaining ability to castle } else { if (whiteKingSquareIndex == 6 || whiteKingSquareIndex == 7) // generally safe kingside squares for king (g1,h1) { kingSafetyEval += 50; for (int i = 0; i < whiteRookIndices.Count; i++) { if (Board.FileFrom128(whiteRookIndices[i]) > 6) { kingSafetyEval -= 55; // penalize non-castling king manoeuvres where rook is boxed in by king } } } else if (whiteKingSquareIndex == 2 || whiteKingSquareIndex == 1 || whiteKingSquareIndex == 0) // generally safe queenside squares for king (a1,b1,c1) { kingSafetyEval += 50; for (int i = 0; i < whiteRookIndices.Count; i++) { if (Board.FileFrom128(whiteRookIndices[i]) < 3) { kingSafetyEval -= 55; // penalize non-castling king manoeuvres where rook is boxed in by king } } } } // king safety black if (Board.BlackHasCastlingRights()) { kingSafetyEval -= 10; // not safe, but at least retaining ability to castle } else { if (blackKingSquareIndex == 118 || blackKingSquareIndex == 119) // generally safe kingside squares for king (g8,h8) { kingSafetyEval -= 50; for (int i = 0; i < blackRookIndices.Count; i++) { if (Board.FileFrom128(blackRookIndices[i]) > 6) { kingSafetyEval += 55; // penalize non-castling king manoeuvres where rook is boxed in by king } } } else if (blackKingSquareIndex == 114 || blackKingSquareIndex == 113 || blackKingSquareIndex == 112) // generally safe queenside squares for king (a8,b8,c8) { kingSafetyEval -= 50; for (int i = 0; i < blackRookIndices.Count; i++) { if (Board.FileFrom128(blackRookIndices[i]) < 3) { kingSafetyEval += 55; // penalize non-castling king manoeuvres where rook is boxed in by king } } } } int openingMaterialCount = 16 * pawnValue + 4 * (rookValue + knightValue + bishopValue) + 2 * queenValue; int endgameMaterialCount = 2 * (rookValue + knightValue); //float gameStage = int finalEval = materialEval * 1000 + mobilityEval + kingSafetyEval + developmentEval; Timer.Stop("Eval"); return(finalEval); }
/// <summary> /// Recursive alpha beta pruning minimax search strategy. /// </summary> /// <param name="gbs">The Game Board State.</param> /// <param name="searchLevels">The number of levels to search (depth)</param> /// <param name="currentPlayer">The current player's turn.</param> /// <param name="BestMove">The best move found during the search</param> /// <param name="alpha">Starting alpha value.</param> /// <param name="beta">Starting beta value.</param> /// <returns></returns> public static AIMinMaxResult AIMinMaxSearch(this IGameBoardState gbs, int searchLevels, Player.PlayerNumber currentPlayer, bool QuiescenceSearch = false, double alpha = -1.0, double beta = 1.0) { Move BestMove = null; double checkWinner = gbs.CheckWinner(); //cutoff for search (recursive base cases) if (checkWinner == -1.0) { return(new AIMinMaxResult(BestMove, -1.0, 1)); } if (checkWinner == 1.0) { return(new AIMinMaxResult(BestMove, 1.0, 1)); } if (searchLevels == 0) { return(new AIMinMaxResult(BestMove, gbs.CalculateUtility(), 1)); } AIMinMaxResult result = null; long statesSearched = 0; //iterate by looking at all possible moves for each piece List <IPieceState> pieces = gbs.GetAlivePieces().Where(s => s.GetPlayer() == currentPlayer).Select(s => s).ToList(); foreach (IPieceState piece in pieces.Shuffle()) { List <Move> moves; if (QuiescenceSearch) { moves = MoveGenerator.GetCaptureMoves(gbs, piece); } else { moves = MoveGenerator.GetMoves(gbs, piece); } MovesSearched += moves.Count; //perform each move on a cloned board and search clone recursively, swapping players each turn foreach (Move move in moves.Shuffle()) { IGameBoardState clone = gbs.Clone(); clone.Move(move.piece, move.space); if (currentPlayer == Player.PlayerNumber.Player1) { result = clone.AIMinMaxSearch(searchLevels - 1, Player.PlayerNumber.Player2, true, alpha, beta); statesSearched += result.TotalStatesSearched; if (statesSearched > StatesSearched) { StatesSearched = statesSearched; } if (result.AlphaBeta > alpha) { alpha = result.AlphaBeta; BestMove = move; } //beta cut off if (beta <= alpha) { break; } } else /* (currentPlayer == Player.PlayerNumber.Player2) */ { result = clone.AIMinMaxSearch(searchLevels - 1, Player.PlayerNumber.Player1, true, alpha, beta); statesSearched += result.TotalStatesSearched; if (statesSearched > StatesSearched) { StatesSearched = statesSearched; } if (result.AlphaBeta < beta) { beta = result.AlphaBeta; BestMove = move; } //alpha cut off if (beta <= alpha) { break; } } if (jobStatus == AIMinMaxJobStatus.StopRequested && LevelsSearched > 0) { searchLevels = 1; } } if (jobStatus == AIMinMaxJobStatus.StopRequested && LevelsSearched > 0) { searchLevels = 1; } } //no moves found, treat as a base case if (BestMove == null) { return(new AIMinMaxResult(BestMove, gbs.CalculateUtility(), 1)); } //Create a result and return it return(new AIMinMaxResult(BestMove, result.AlphaBeta, statesSearched)); }
public static string NotationFromMove(ushort move) { Board.UnmakeMove(move); // unmake move on board MoveGenerator moveGen = new MoveGenerator(); int moveFromIndex = move & 127; int moveToIndex = (move >> 7) & 127; int promotionPieceIndex = (move >> 14) & 3; // 0 = queen, 1 = rook, 2 = knight, 3 = bishop int colourToMove = Board.boardColourArray[moveFromIndex]; int movePieceCode = Board.boardArray [moveFromIndex]; // get move piece code int movePieceType = movePieceCode & ~1; // get move piece type code (no colour info) int capturedPieceCode = Board.boardArray [moveToIndex]; // get capture piece code int promotionPieceType = Board.pieceCodeArray [promotionPieceIndex]; if (movePieceType == Board.kingCode) { if (moveToIndex - moveFromIndex == 2) { Board.MakeMove(move); // remake move return("O-O"); } else if (moveToIndex - moveFromIndex == -2) { Board.MakeMove(move); // remake move return("O-O-O"); } } string moveNotation = GetSymbolFromPieceType(movePieceType); // check if any ambiguity exists in notation (e.g if e2 can be reached via Nfe2 and Nbe2) if (movePieceType != Board.pawnCode && movePieceType != Board.kingCode) { Heap allMoves = moveGen.GetMoves(false, false); for (int i = 0; i < allMoves.Count; i++) { int alternateMoveFromIndex = allMoves.moves[i] & 127; int alternateMoveToIndex = (allMoves.moves[i] >> 7) & 127; int alternateMovePieceCode = Board.boardArray [alternateMoveFromIndex]; if (alternateMoveFromIndex != moveFromIndex && alternateMoveToIndex == moveToIndex) // if moving to same square from different square { if (alternateMovePieceCode == movePieceCode) // same piece type { int fromFileIndex = Board.FileFrom128(moveFromIndex) - 1; int alternateFromFileIndex = Board.FileFrom128(alternateMoveFromIndex) - 1; int fromRankIndex = Board.RankFrom128(moveFromIndex) - 1; int alternateFromRankIndex = Board.RankFrom128(alternateMoveFromIndex) - 1; if (fromFileIndex != alternateFromFileIndex) // pieces on different files, thus ambiguity can be resolved by specifying file { moveNotation += Definitions.fileNames[fromFileIndex]; break; // ambiguity resolved } else if (fromRankIndex != alternateFromRankIndex) { moveNotation += Definitions.rankNames[fromRankIndex]; break; // ambiguity resolved } } } } } if (capturedPieceCode != 0) // add 'x' to indicate capture { if (movePieceType == Board.pawnCode) { moveNotation += Definitions.fileNames[Board.FileFrom128(moveFromIndex) - 1]; } moveNotation += "x"; } else // check if capturing ep { if (movePieceType == Board.pawnCode) { if (System.Math.Abs(moveToIndex - moveFromIndex) != 16 && System.Math.Abs(moveToIndex - moveFromIndex) != 32) { moveNotation += Definitions.fileNames[Board.FileFrom128(moveFromIndex) - 1] + "x"; } } } moveNotation += Definitions.fileNames [Board.FileFrom128(moveToIndex) - 1]; moveNotation += Definitions.rankNames [Board.RankFrom128(moveToIndex) - 1]; // add = piece type if promotion if (movePieceType == Board.pawnCode) { if (moveToIndex >= 112 || moveToIndex <= 7) // pawn has reached first/eighth rank { moveNotation += "=" + GetSymbolFromPieceType(promotionPieceType); } } // add check/mate symbol if applicable Board.MakeMove(move); // remake move if (moveGen.PositionIsMate()) { moveNotation += "#"; } else if (moveGen.PositionIsCheck()) { moveNotation += "+"; } return(moveNotation); }
int AlphaBetaSearch(int plyRemaining, int alpha, int beta, bool isWhite) { Heap moveHeap = moveGenerator.GetMoves(false, false); int count = moveHeap.Count; if (moveHeap.Count == 0) { return(((isWhite)?-1:1) * (10000000 + plyRemaining)); // if no moves available, side has been checkmated. Return best score for opponent. Checkmating sooner (higher depth) is rewarded. } if (plyRemaining == 0) { nodesSearched++; return(Evaluate()); //QuiescenceSearch(int.MinValue,int.MaxValue,!isWhite,true); //return quiescenceScore; } if (isWhite) // white is trying to attain the highest evaluation possible { for (int i = 0; i < count; i++) { ushort move = moveHeap.RemoveFirst(); Board.MakeMove(move); alpha = Math.Max(alpha, AlphaBetaSearch(plyRemaining - 1, alpha, beta, false)); Board.UnmakeMove(move); if (plyRemaining == searchDepth) // has searched full depth and is now looking at top layer of moves to select the best { if (alpha > bestScoreThisIteration) { bestScoreThisIteration = alpha; bestMoveThisIteration = move; } } if (beta <= alpha) // break { breakCount++; break; } } return(alpha); } else { // black is trying to obtain the lowest evaluation possible for (int i = 0; i < count; i++) { ushort move = moveHeap.RemoveFirst(); Board.MakeMove(move); beta = Math.Min(beta, AlphaBetaSearch(plyRemaining - 1, alpha, beta, true)); Board.UnmakeMove(move); if (plyRemaining == searchDepth) // has searched full depth and is now looking at top layer of moves to select the best { if (beta < bestScoreThisIteration) { bestScoreThisIteration = beta; bestMoveThisIteration = move; } } if (beta <= alpha) // break { breakCount++; break; } } return(beta); } return(0); }
public Move WaitMove(Position position) { var moves = MoveGenerator.GetMoves(position); return(moves.ToList()[new Random().Next(moves.Count())]); }
public void Expect_white_pawn_can_make_double_move_from_2th_rank() { var target = new MoveGenerator(GameState.FromForsythEdwardsNotation("4k3/8/8/8/8/8/P7/4K3 w - -")); var moves = target.GetMoves(); Assert.IsTrue(moves.Contains(new Move(Cell.a2, Cell.a4))); }
public void GetMoves_Column3And3Full_5NoneEmptyFields() { var generator = new MoveGenerator(); var field = Field.Parse(@" 0,0,0,1,1,0,0; 2,0,0,2,1,0,0; 2,0,0,1,2,0,0; 1,0,0,1,1,0,0; 2,0,0,2,1,0,0; 2,0,0,1,2,0,0"); var act = generator.GetMoves(field, false); var exp = new Field[] { // Column 0 Field.Parse(@" 0,0,0,1,1,0,0; 2,0,0,2,1,0,0; 2,0,0,1,2,0,0; 1,0,0,1,1,0,0; 2,0,0,2,1,0,0; 2,0,0,1,2,0,2"), // Column 1 Field.Parse(@" 0,0,0,1,1,0,0; 2,0,0,2,1,0,0; 2,0,0,1,2,0,0; 1,0,0,1,1,0,0; 2,0,0,2,1,0,0; 2,0,0,1,2,2,0"), // Column 2 Field.Empty, // Column 3 Field.Empty, // Column 4 Field.Parse(@" 0,0,0,1,1,0,0; 2,0,0,2,1,0,0; 2,0,0,1,2,0,0; 1,0,0,1,1,0,0; 2,0,0,2,1,0,0; 2,0,2,1,2,0,0"), // Column 5 Field.Parse(@" 0,0,0,1,1,0,0; 2,0,0,2,1,0,0; 2,0,0,1,2,0,0; 1,0,0,1,1,0,0; 2,0,0,2,1,0,0; 2,2,0,1,2,0,0"), // Column 6 Field.Parse(@" 2,0,0,1,1,0,0; 2,0,0,2,1,0,0; 2,0,0,1,2,0,0; 1,0,0,1,1,0,0; 2,0,0,2,1,0,0; 2,0,0,1,2,0,0"), }; CollectionAssert.AreEqual(exp, act); }
public void Expect_black_pawn_can_make_double_move_from_7th_rank() { var target = new MoveGenerator(GameState.FromForsythEdwardsNotation("4k3/p7/8/8/8/8/8/4K3 b - -")); var moves = target.GetMoves(); Assert.IsTrue(moves.Contains(new Move(Cell.a7, Cell.a5))); }
public bool ValidateEntry() { if (currentInput.Length > 0) { List <ushort> inputtedMoves = PGNReader.MovesFromPGN(currentInput); if (inputtedMoves.Count > 0) { string cleanedInput = PGNReader.MoveStringsFromPGN(currentInput) [0]; ushort inputMove = inputtedMoves [0]; int movePieceCode = PieceTypeCodeFromNotation(cleanedInput) + ((Board.IsWhiteToPlay()) ? 1 : 0); ushort[] legalMoves = moveGen.GetMoves(false, false).moves; bool ambiguousCaseSpecified = false; // does the inputted string describe an ambiguous case (e.g Rad1) if (cleanedInput.Length >= 3) { ambiguousCaseSpecified = Definitions.fileNames.Contains(cleanedInput [2] + ""); } if ((movePieceCode & ~1) == Board.kingCode || (movePieceCode & ~1) == Board.pawnCode) // king and pawn moves can't be ambiguous { ambiguousCaseSpecified = true; } bool moveIsAmbiguous = false; if (!ambiguousCaseSpecified) // check if case is ambiguous if no specification has been given in input { int movesFoundFollowingInput = 0; for (int i = 0; i < legalMoves.Length; i++) { int moveFromIndex = legalMoves [i] & 127; int moveToIndex = (legalMoves [i] >> 7) & 127; if (Board.boardArray [moveFromIndex] == movePieceCode && moveToIndex == ((inputMove >> 7) & 127)) // is move as described by input string { movesFoundFollowingInput++; if (movesFoundFollowingInput == 2) { moveIsAmbiguous = true; break; } } } } if (moveIsAmbiguous) { uiManager.SetMessage("move is ambiguous. please specify file/rank of piece you wish to move", 3, true); } else { if (player != null) { player.TryMakeMove(inputMove); } return(true); } } else { print("Move illegal/incorrect format"); } } return(false); }
/// Returns a list of moves given a pgn string. /// Note that Board should be set to whatever starting position of pgn is. public static List<ushort> MovesFromPGN(string pgn) { List<string> moveStrings = MoveStringsFromPGN (pgn); List<ushort> allMoves = new List<ushort> (); MoveGenerator moveGen = new MoveGenerator (); for (int i =0; i < moveStrings.Count; i++) { string moveString = moveStrings[i]; moveString = moveString.Replace("+",""); // remove check symbol moveString = moveString.Replace("#",""); // remove mate symbol moveString = moveString.Replace("x",""); // remove capture symbol string moveStringLower = moveStrings[i].ToLower(); ushort[] movesInPosition = moveGen.GetMoves(false,false).moves; ushort move = 0; for (int j =0; j < movesInPosition.Length; j ++) { move = movesInPosition[j]; int moveFromIndex = move & 127; int moveToIndex = (move >> 7) & 127; int movePieceType = Board.boardArray[moveFromIndex] & ~1; int colourCode = Board.boardArray[moveFromIndex] & 1; if (moveStringLower == "oo") { // castle kingside if (movePieceType == Board.kingCode && moveToIndex - moveFromIndex == 2) { break; } } else if (moveStringLower == "ooo") { // castle queenside if (movePieceType == Board.kingCode && moveToIndex - moveFromIndex == -2) { break; } } else if (Definitions.fileNames.Contains(moveString[0] + "")) { // pawn move if starts with any file indicator (e.g. 'e'4. Note that uppercase B is used for bishops) if (movePieceType != Board.pawnCode) { continue; } if (Definitions.FileNumberFromAlgebraicName(moveStringLower[0]) == Board.FileFrom128(moveFromIndex)) { // correct starting file if (moveString.Contains("=")) { // is promotion char promotionChar = moveStringLower[moveStringLower.Length-1]; int promotionPieceIndex = move >> 14 & 3; int promotionPieceCode = Board.pieceCodeArray [promotionPieceIndex]; if ((promotionPieceCode == Board.queenCode && promotionChar != 'q') || (promotionPieceCode == Board.rookCode && promotionChar != 'r') || (promotionPieceCode == Board.bishopCode && promotionChar != 'b') || (promotionPieceCode == Board.knightCode && promotionChar != 'n')) { continue; // skip this move, incorrect promotion type } break; } else { char targetFile = moveString[moveString.Length-2]; char targetRank = moveString[moveString.Length-1]; if (Definitions.FileNumberFromAlgebraicName(targetFile) == Board.FileFrom128(moveToIndex)) { // correct ending file if (Definitions.RankNumberFromAlgebraicName(targetRank) == Board.RankFrom128(moveToIndex)) { // correct ending rank break; } } } } } else { // regular piece move char movePieceChar = moveString[0]; if (!(movePieceType == Board.queenCode && movePieceChar == 'Q') && !(movePieceType == Board.rookCode && movePieceChar == 'R') && !(movePieceType == Board.bishopCode && movePieceChar == 'B') && !(movePieceType == Board.knightCode && movePieceChar == 'N') && !(movePieceType == Board.kingCode && movePieceChar == 'K')) { continue; // skip this move, incorrect move piece type } char targetFile = moveString[moveString.Length-2]; char targetRank = moveString[moveString.Length-1]; if (Definitions.FileNumberFromAlgebraicName(targetFile) == Board.FileFrom128(moveToIndex)) { // correct ending file if (Definitions.RankNumberFromAlgebraicName(targetRank) == Board.RankFrom128(moveToIndex)) { // correct ending rank if (moveString.Length == 4) { // addition char present for disambiguation (e.g. Nbd7 or R7e2) char disambiguationChar = moveString[1]; if (Definitions.fileNames.Contains(disambiguationChar + "")) { // is file disambiguation if (Definitions.FileNumberFromAlgebraicName(disambiguationChar) != Board.FileFrom128(moveFromIndex)) { // incorrect starting file continue; } } else { // is rank disambiguation if (Definitions.RankNumberFromAlgebraicName(disambiguationChar) != Board.RankFrom128(moveFromIndex)) { // incorrect starting rank continue; } } } break; } } } } if (move == 0) { // move is illegal; discard and return moves up to this point UnityEngine.Debug.Log(moveString); break; } else { allMoves.Add(move); } Board.MakeMove(move); } for (int i = allMoves.Count-1; i>= 0; i --) { Board.UnmakeMove(allMoves[i]); } return allMoves; }
public static string NotationFromMove(ushort move) { Board.UnmakeMove (move); // unmake move on board MoveGenerator moveGen = new MoveGenerator (); int moveFromIndex = move & 127; int moveToIndex = (move >> 7) & 127; int promotionPieceIndex = (move >> 14) & 3; // 0 = queen, 1 = rook, 2 = knight, 3 = bishop int colourToMove = Board.boardColourArray[moveFromIndex]; int movePieceCode = Board.boardArray [moveFromIndex]; // get move piece code int movePieceType = movePieceCode & ~1; // get move piece type code (no colour info) int capturedPieceCode = Board.boardArray [moveToIndex]; // get capture piece code int promotionPieceType = Board.pieceCodeArray [promotionPieceIndex]; if (movePieceType == Board.kingCode) { if (moveToIndex - moveFromIndex == 2) { Board.MakeMove (move); // remake move return "O-O"; } else if (moveToIndex - moveFromIndex == -2) { Board.MakeMove (move); // remake move return "O-O-O"; } } string moveNotation = GetSymbolFromPieceType(movePieceType); // check if any ambiguity exists in notation (e.g if e2 can be reached via Nfe2 and Nbe2) if (movePieceType != Board.pawnCode && movePieceType != Board.kingCode) { Heap allMoves = moveGen.GetMoves(false, false); for (int i =0; i < allMoves.Count; i ++) { int alternateMoveFromIndex = allMoves.moves[i] & 127; int alternateMoveToIndex = ( allMoves.moves[i] >> 7) & 127; int alternateMovePieceCode = Board.boardArray [alternateMoveFromIndex]; if (alternateMoveFromIndex != moveFromIndex && alternateMoveToIndex == moveToIndex) { // if moving to same square from different square if (alternateMovePieceCode == movePieceCode) { // same piece type int fromFileIndex = Board.FileFrom128(moveFromIndex) -1; int alternateFromFileIndex = Board.FileFrom128(alternateMoveFromIndex) -1; int fromRankIndex = Board.RankFrom128(moveFromIndex) -1; int alternateFromRankIndex = Board.RankFrom128(alternateMoveFromIndex) -1; if (fromFileIndex != alternateFromFileIndex) { // pieces on different files, thus ambiguity can be resolved by specifying file moveNotation += Definitions.fileNames[fromFileIndex]; break; // ambiguity resolved } else if (fromRankIndex != alternateFromRankIndex) { moveNotation += Definitions.rankNames[fromRankIndex]; break; // ambiguity resolved } } } } } if (capturedPieceCode != 0) { // add 'x' to indicate capture if (movePieceType == Board.pawnCode) { moveNotation += Definitions.fileNames[Board.FileFrom128(moveFromIndex)-1]; } moveNotation += "x"; } else { // check if capturing ep if (movePieceType == Board.pawnCode) { if (System.Math.Abs (moveToIndex - moveFromIndex) != 16 && System.Math.Abs (moveToIndex - moveFromIndex) != 32) { moveNotation += Definitions.fileNames[Board.FileFrom128(moveFromIndex)-1] + "x"; } } } moveNotation += Definitions.fileNames [Board.FileFrom128 (moveToIndex) - 1]; moveNotation += Definitions.rankNames [Board.RankFrom128 (moveToIndex) - 1]; // add = piece type if promotion if (movePieceType == Board.pawnCode) { if (moveToIndex >= 112 || moveToIndex <= 7) { // pawn has reached first/eighth rank moveNotation += "=" + GetSymbolFromPieceType(promotionPieceType); } } // add check/mate symbol if applicable Board.MakeMove (move); // remake move if (moveGen.PositionIsMate ()) { moveNotation += "#"; } else if (moveGen.PositionIsCheck ()) { moveNotation += "+"; } return moveNotation; }