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); }
void Start() { if (usePerft) { moveGenerator = new MoveGenerator(); if (runAllTests) { bool allCorrect = true; for (int i =0; i < tests.Length; i ++) { searchDepth = tests[i].depth; fen = tests[i].fen; int correctResult = tests[i].correctResult; int result = RunTest(); if (correctResult == result) { print ("Results match"); } else { allCorrect = false; print ("Error at test " + i + " Result: " + result + "; expected: " + correctResult); } } if (allCorrect) { print ("Test suite passed"); } else { print ("Test suite failed"); } } else { RunTest(); } } }
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))); }
public static void Init() { pieceValues = new Dictionary<int, int> (); for (int i = 0; i <=1; i++) { // piece values (white positive; black negative) int colourCode = i; int sign = i*2-1; pieceValues.Add (Board.pawnCode + colourCode, pawnValue * sign); pieceValues.Add (Board.rookCode + colourCode, rookValue * sign); pieceValues.Add (Board.knightCode + colourCode, knightValue * sign); pieceValues.Add (Board.bishopCode + colourCode, bishopValue * sign); pieceValues.Add (Board.queenCode + colourCode, queenValue * sign); pieceValues.Add (Board.kingCode + colourCode, kingValue * sign); } moveGenerator = new MoveGenerator (); }
static void Main(string[] args) { string inputFile; string outputFile; int treeDepth; string inputBoardAsAtring = "xxxxxxxxxWxxxxxxBxxxxxx"; if (args.Length < 3) { inputFile = "board1.txt"; outputFile = "board2.txt"; treeDepth = 3; } else { inputFile = args[0]; outputFile = args[1]; treeDepth = Convert.ToInt32(args[2]); } // read the board as a string from the input file inputBoardAsAtring = System.IO.File.ReadAllLines(@"" + inputFile)[0]; var board = new VariantDBoard(true, inputBoardAsAtring); Console.WriteLine(board.Print()); var movesGen = new MoveGenerator<VariantDBoard>(VariantDMoveGenerator<VariantDBoard>.Moves); var blackGen = new MoveGenerator<VariantDBoard>(VariantDMoveGenerator<VariantDBoard>.BlackMove); var root = new GameNode<VariantDBoard>(board, null, treeDepth, movesGen, blackGen, true); var minmax = new MiniMax.MiniMax<VariantDBoard>(true); var score = minmax.MiniMaxBase(root); var move = score.Item1.GetParentAtDepth(1); Console.WriteLine("Nodes generated: " + root.NumNodes() + "."); Console.WriteLine(move.Board.Print()); string[] lines = { "Board Position: " + move.Board.ToString(), "Positions evaluated by static estimation: " + minmax.StaticEvals + ".", "MINIMAX estimate: " + score.Item2 + "." }; foreach (var line in lines) { Console.WriteLine(line); } System.IO.File.WriteAllLines(@"" + outputFile, lines); }
public void StartSearch() { bestMoveSoFar = 0; transpositionTable.Clear (); nodesSearched = 0; breakCount = 0; finishedSearch = false; moveGenerator = new MoveGenerator (); findingMoveForWhite = Board.IsWhiteToPlay (); Thread searchThread = new Thread (Iterate); searchThread.Start (); //Iterate (); }
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(); }
protected virtual MoveGenerationResults GenerateMoves(int currentPly, Sides currentSide, out List <Move> moves) { moves = _PlyInfo[currentPly].Moves; int kingAttackCount = _ScratchBoard.KingAttackCount(currentSide); if (kingAttackCount > 0) { MoveGenerator.GenerateCheckEscapes(_ScratchBoard, kingAttackCount, moves); if (moves.Count == 0) { // We are mated. return(MoveGenerationResults.Mated); } } else { MoveGenerator.GenerateAll(_ScratchBoard, moves); } return(MoveGenerationResults.NotMated); }
public Game(Board board, TranspositionTable transpositionTable, Colour humanColour = Colour.None) { this.board = board; HumanColour = humanColour; CpuColour = humanColour.Opposite(); moveGenerator = new MoveGenerator(16); positionEvaluator = new PositionEvaluator(); search = new Search(moveGenerator, positionEvaluator, transpositionTable); search.Info += _search_Info; var moves = new List <uint>(); moveGenerator.Generate(board, Colour.White, moves); AvailableMoves = moves.Select(x => new MoveViewer(x)); history.Push(new GameHistoryNode(board.History.First(), GetGameState())); }
public void GeneratesCorrectBishopMovesBlack() { // Assemble var board = new Board { WhitePawns = 0x00_00_00_00_00_29_d6_00, WhiteBishops = 0x00_00_00_00_02_40_00_00, BlackPawns = 0x00_d5_22_10_00_00_00_00, BlackBishops = 0x00_00_00_00_09_00_00_00 }; var position = new Position(board, Side.Black, null, CastlingRights.None, 0); var moves = new List <Move>(); // Act MoveGenerator.AddBishopMoves(position, moves); // Assert moves.Should().HaveCount(12); moves.Where(m => m.From == Square.a4).Should().HaveCount(6); moves.Where(m => m.From == Square.d4).Should().HaveCount(6); moves.Where(m => m.IsCapture).Should().HaveCount(2); }
public void RunSingleTest() { moveGenerator = new MoveGenerator(); board.LoadPosition(testFen); var sw = new System.Diagnostics.Stopwatch(); sw.Start(); int numNodes = 0; if (divide) { perftDivideResults = new Dictionary <string, int> (); numNodes = SearchDivide(depth, depth); ComparePerftDivideResults(testFen); } else { numNodes = Search(depth); } sw.Stop(); LogMessage(string.Format("Num nodes: {0} at depth: {1} in {2} ms", numNodes, depth, sw.ElapsedMilliseconds)); }
public void GeneratesCorrectMovesForStartingPosition() { // Assemble var position = new Position(Board.StartingPosition, Side.White, null, CastlingRights.All, 0); // Act var moves = MoveGenerator.GetAllMoves(position); // Assert var expectedMoves = new List <Move> { new Move(Square.a2, Square.a3, MoveFlags.QuietMove), new Move(Square.b2, Square.b3, MoveFlags.QuietMove), new Move(Square.c2, Square.c3, MoveFlags.QuietMove), new Move(Square.d2, Square.d3, MoveFlags.QuietMove), new Move(Square.e2, Square.e3, MoveFlags.QuietMove), new Move(Square.f2, Square.f3, MoveFlags.QuietMove), new Move(Square.g2, Square.g3, MoveFlags.QuietMove), new Move(Square.h2, Square.h3, MoveFlags.QuietMove), new Move(Square.a2, Square.a4, MoveFlags.DoublePawnPush), new Move(Square.b2, Square.b4, MoveFlags.DoublePawnPush), new Move(Square.c2, Square.c4, MoveFlags.DoublePawnPush), new Move(Square.d2, Square.d4, MoveFlags.DoublePawnPush), new Move(Square.e2, Square.e4, MoveFlags.DoublePawnPush), new Move(Square.f2, Square.f4, MoveFlags.DoublePawnPush), new Move(Square.g2, Square.g4, MoveFlags.DoublePawnPush), new Move(Square.h2, Square.h4, MoveFlags.DoublePawnPush), new Move(Square.b1, Square.a3, MoveFlags.QuietMove), new Move(Square.b1, Square.c3, MoveFlags.QuietMove), new Move(Square.g1, Square.f3, MoveFlags.QuietMove), new Move(Square.g1, Square.h3, MoveFlags.QuietMove), }; moves.Should().BeEquivalentTo(expectedMoves); }
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; } } } }
public void GeneratesCorrectRookMovesBlack() { // Assemble var board = new Board { WhitePawns = 0x00_00_00_00_80_01_76_00, WhiteRooks = 0x00_00_00_00_00_80_00_08, WhiteKnights = 0x00_00_20_02_00_00_00_62, BlackPawns = 0x00_28_13_84_00_00_00_00, BlackRooks = 0x00_10_00_00_00_02_00_00 }; var position = new Position(board, Side.Black, null, CastlingRights.None, 0); var moves = new List <Move>(); // Act MoveGenerator.AddRookMoves(position, moves); // Assert moves.Should().HaveCount(11); moves.Where(m => m.From == Square.b3).Should().HaveCount(10); moves.Where(m => m.From == Square.e7).Should().HaveCount(1); moves.Where(m => m.IsCapture).Should().HaveCount(4); }
public int SearchBestMove(int depth, Dot player, float alpha, float beta) { int bestMove = 0; var moves = MoveGenerator.GenerateMovesForPlayer(player); Dot nextPlayer = player.NextPlayer(); foreach (var move in moves) { if (alpha < beta) { Field.MakeMove(move); float tmp = -EvaluatePosition(depth - 1, nextPlayer, -beta, -alpha); Field.UnmakeMove(); if (tmp > alpha) { alpha = tmp; bestMove = move; } } } return(bestMove); }
public void PawnTest1() { // Arrange var chessPieces = new List <ChessPiece>(); chessPieces.Add(new Rook(rook_white_of_queen, 1, 1)); chessPieces.Add(new Knight(knight_white_of_queen, 2, 1)); chessPieces.Add(new Bishop(bishop_white_of_queen, 3, 1)); chessPieces.Add(new Queen(queen_white, 4, 1)); chessPieces.Add(new King(king_white, 5, 1)); chessPieces.Add(new Bishop(bishop_white_of_king, 6, 1)); chessPieces.Add(new Knight(knight_white_of_king, 7, 1)); chessPieces.Add(new Rook(rook_white_of_king, 8, 1)); chessPieces.Add(new Pawn(pawn_white_of_rook_of_queen, 1, 2)); chessPieces.Add(new Pawn(pawn_white_of_knight_of_queen, 2, 4)); chessPieces.Add(new Pawn(pawn_white_of_bishop_of_queen, 3, 2)); chessPieces.Add(new Pawn(pawn_white_of_queen, 4, 2)); chessPieces.Add(new Pawn(pawn_white_of_king, 5, 2)); chessPieces.Add(new Pawn(pawn_white_of_bishop_of_king, 6, 2)); chessPieces.Add(new Pawn(pawn_white_of_knight_of_king, 7, 2)); chessPieces.Add(new Pawn(pawn_white_of_rook_of_king, 8, 2)); var pawn = new Pawn(pawn_black_of_knight_of_queen, 2, 5); chessPieces.Add(pawn); var game = Game.Create(chessPieces); var moveGenerator = new MoveGenerator(); // Act var moves = moveGenerator.NextMoves(pawn, game).ToList(); // Assert Assert.Empty(moves); }
public void GeneratesCastlingMovesWhenAvailable() { // Assemble var board = new Board { WhiteKing = 0x00_00_00_00_00_00_00_10, WhiteRooks = 0x00_00_00_00_00_00_00_81 }; var position = new Position(board, Side.White, null, CastlingRights.WhiteBoth, 0); var moves = new List <Move>(); // Act MoveGenerator.AddCastlingMoves(position, moves); // Assert var expectedMoves = new List <Move> { new Move(Square.e1, Square.g1, MoveFlags.KingCastle), new Move(Square.e1, Square.c1, MoveFlags.QueenCastle), }; moves.Should().BeEquivalentTo(expectedMoves); }
public void OnlyBlocksAndKingMovesAllowed(string fenString) { var gameState = FenHelpers.Parse(fenString); var board = CreateBoard(gameState); var moves = new List <uint>(10); MoveGenerator.Generate(board, gameState.ToPlay, moves); var pawnMoveViews = GetPawnMoveViews(moves); var rookMoveViews = GetRookMoveViews(moves); var knightMoveViews = GetKnightMoveViews(moves); var bishopMoveViews = GetBishopMoveViews(moves); var queenMoveViews = GetQueenMoveViews(moves); var kingMoveViews = GetKingMoveViews(moves); Assert.Equal(2, pawnMoveViews.Count); Assert.Equal(1, rookMoveViews.Count); Assert.Equal(2, knightMoveViews.Count); Assert.Equal(1, bishopMoveViews.Count); Assert.Equal(2, queenMoveViews.Count); Assert.Equal(3, kingMoveViews.Count); }
/// <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)]); }
private static (CharMap, int) BuildMapAndFindOxygen(string intcode) { var map = new CharMap(); var movements = new MoveGenerator(); map[movements.Current.Position] = MapSpace; var stepsToOxygen = 0; var debug = false; var engine2 = new Engine() .WithMemory(intcode) .OnInput(engine => { var movement = movements.NextProposal(map); if (movement == MoveNone) { engine.Halt = true; } if (debug) { Console.Clear(); Console.WriteLine($"Moves: {movements}"); foreach (var line in map.Render(MapOverlay)) { Console.WriteLine(line); } Console.ReadKey(); } engine.Input.Add(movement); }) .OnOutput(engine => { var status = engine.Output.Take(); switch (status) { case StatusHitTheWall: map[movements.ProposedPosition] = MapWall; break; case StatusMoved: movements.ApproveMove(); map[movements.Current.Position] = MapSpace; break; case StatusFoundOxygen: movements.ApproveMove(); map[movements.Current.Position] = MapOxygen; stepsToOxygen = movements.Moves; break; } }) .Execute(); return(map, stepsToOxygen); // Draw Droid on top of map char MapOverlay(Point p, char val) { var droid = movements.Current?.Position; return(p == droid ? MapDroid : val); } }
public void GetCastlingMoves() => MoveGenerator.AddCastlingMoves(_position, new List <Move>());
public void GetRookMoves() => MoveGenerator.AddRookMoves(_position, new List <Move>());
public void GetKnightMoves() => MoveGenerator.AddKnightMoves(_position, new List <Move>());
public int Quiescene(int alpha, int beta, BoardState bs, SearchInfo sInfo) { if (sInfo.IsTimeUp()) { sInfo.Stopped = true; } //sInfo.Nodes++; /*if (_helper.IsRepetition(bs) || bs.FiftyMoveRule >= 100) * { * //if (bs.BestPly != null) bs.BestPly.Score = 0; * return 0; * }*/ var score = _eval.EvalPosition(bs); if (score >= beta) { return(beta); } if (score > alpha) { alpha = score; } var mg = new MoveGenerator(bs); var capMoves = mg.AllCapMoves(); Ply bestMove = null; var nMoves = 0; if (capMoves != null) { nMoves = capMoves.Length; } var oldAlpha = alpha; var moveNum = 0; if (bs.BestPlyAtLowerDepth != null && !bs.HaveSearched) { mg.MakeMove(bs.BestPlyAtLowerDepth); bs.BestPlyAtLowerDepth.Score = -Quiescene(-beta, -alpha, bs, sInfo); mg.UndoMove(bs.BestPlyAtLowerDepth); bs.HaveSearched = true; bestMove = bs.BestPlyAtLowerDepth; alpha = bestMove.Score; //Console.WriteLine("This never happens."); } for (moveNum = 0; moveNum < nMoves; moveNum++) { mg.MakeMove(capMoves[moveNum]); capMoves[moveNum].Score = -Quiescene(-beta, -alpha, bs, sInfo); mg.UndoMove(capMoves[moveNum]); if (sInfo.Stopped) { return(Definitions.Stopped); } if (capMoves[moveNum].Score <= alpha) { continue; } if (capMoves[moveNum].Score >= beta) { if (nMoves == 1) { sInfo.Fhf++; } sInfo.Fh++; return(beta); // Fail hard beta-cutoff. } alpha = capMoves[moveNum].Score; // alpha acts like max in minimax. bestMove = capMoves[moveNum]; } if (alpha != oldAlpha) { //Console.WriteLine("New best ply!"); bs.BestPly = bestMove; } return(alpha); }
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); }
static string NotationFromMove(Board board, Move move) { MoveGenerator moveGen = new MoveGenerator(); int movePieceType = Piece.PieceType(board.Square[move.StartSquare]); int capturedPieceType = Piece.PieceType(board.Square[move.TargetSquare]); if (move.MoveFlag == Move.Flag.Castling) { int delta = move.TargetSquare - move.StartSquare; if (delta == 2) { return("O-O"); } else if (delta == -2) { 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 != Piece.Pawn && movePieceType != Piece.King) { var allMoves = moveGen.GenerateMoves(board); foreach (Move altMove in allMoves) { if (altMove.StartSquare != move.StartSquare && altMove.TargetSquare == move.TargetSquare) // if moving to same square from different square { if (Piece.PieceType(board.Square[altMove.StartSquare]) == movePieceType) // same piece type { int fromFileIndex = BoardRepresentation.FileIndex(move.StartSquare); int alternateFromFileIndex = BoardRepresentation.FileIndex(altMove.StartSquare); int fromRankIndex = BoardRepresentation.RankIndex(move.StartSquare); int alternateFromRankIndex = BoardRepresentation.RankIndex(altMove.StartSquare); if (fromFileIndex != alternateFromFileIndex) // pieces on different files, thus ambiguity can be resolved by specifying file { moveNotation += BoardRepresentation.fileNames[fromFileIndex]; break; // ambiguity resolved } else if (fromRankIndex != alternateFromRankIndex) { moveNotation += BoardRepresentation.rankNames[fromRankIndex]; break; // ambiguity resolved } } } } } if (capturedPieceType != 0) // add 'x' to indicate capture { if (movePieceType == Piece.Pawn) { moveNotation += BoardRepresentation.fileNames[BoardRepresentation.FileIndex(move.StartSquare)]; } moveNotation += "x"; } else // check if capturing ep { if (move.MoveFlag == Move.Flag.EnPassantCapture) { moveNotation += BoardRepresentation.fileNames[BoardRepresentation.FileIndex(move.StartSquare)] + "x"; } } moveNotation += BoardRepresentation.fileNames[BoardRepresentation.FileIndex(move.TargetSquare)]; moveNotation += BoardRepresentation.rankNames[BoardRepresentation.RankIndex(move.TargetSquare)]; // add promotion piece if (move.IsPromotion) { int promotionPieceType = move.PromotionPieceType; moveNotation += "=" + GetSymbolFromPieceType(promotionPieceType); } board.MakeMove(move, inSearch: true); var legalResponses = moveGen.GenerateMoves(board); // add check/mate symbol if applicable if (moveGen.InCheck()) { if (legalResponses.Count == 0) { moveNotation += "#"; } else { moveNotation += "+"; } } board.UnmakeMove(move, inSearch: true); return(moveNotation); }
public void GetMove_O_3Fields() { var field = Field.Create(0, 0, 0, @" ......X... XXX....XXX XXXX...XXX"); var generator = new MoveGenerator(); var act = generator.GetFields(field, Block.O).ToArray(); var exp = new [] { Field.Create(0, 0, 0, @" ......X... XXX..XXXXX XXXX.XXXXX"), Field.Create(0, 0, 0, @" ...XX.X... XXXXX..XXX XXXX...XXX"), Field.Create(0, 0, 0, @" ......X... XXX.XX.XXX XXXXXX.XXX"), }; CollectionAssert.AreEqual(exp, act); }
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 static int CalculateBestMove(ChessBoard cb, ThreadData threadData, int ply, int depth, int alpha, int beta, int nullMoveCounter) { if (!IsRunning) { return(ChessConstants.ScoreNotRunning); } if (EngineConstants.Assert) { Assert.IsTrue(depth >= 0); Assert.IsTrue(alpha >= Util.ShortMin && alpha <= Util.ShortMax); Assert.IsTrue(beta >= Util.ShortMin && beta <= Util.ShortMax); } var alphaOrig = alpha; // get extensions depth += Extensions(cb); /* mate-distance pruning */ if (EngineConstants.EnableMateDistancePruning) { alpha = Math.Max(alpha, Util.ShortMin + ply); beta = Math.Min(beta, Util.ShortMax - ply - 1); if (alpha >= beta) { return(alpha); } } // TODO JITWatch unpredictable branch if (depth == 0) { return(QuiescenceUtil.CalculateBestMove(cb, threadData, alpha, beta)); } /* transposition-table */ var ttEntry = TtUtil.GetEntry(cb.ZobristKey); var score = ttEntry.GetScore(ply); if (ttEntry.Key != 0) { if (!EngineConstants.TestTtValues) { if (ttEntry.Depth >= depth) { switch (ttEntry.Flag) { case TtUtil.FlagExact: return(score); case TtUtil.FlagLower: if (score >= beta) { return(score); } break; case TtUtil.FlagUpper: if (score <= alpha) { return(score); } break; } } } } if (Statistics.Enabled) { Statistics.AbNodes++; } var eval = Util.ShortMin; var isPv = beta - alpha != 1; if (!isPv && cb.CheckingPieces == 0) { eval = EvalUtil.GetScore(cb, threadData); /* use tt value as eval */ if (EngineConstants.UseTtScoreAsEval) { if (TtUtil.CanRefineEval(ttEntry, eval, score)) { eval = score; } } /* static null move pruning */ if (EngineConstants.EnableStaticNullMove && depth < StaticNullmoveMargin.Length) { if (eval - StaticNullmoveMargin[depth] >= beta) { if (Statistics.Enabled) { Statistics.StaticNullMoved[depth]++; } return(eval); } } /* razoring */ if (EngineConstants.EnableRazoring && depth < RazoringMargin.Length && Math.Abs(alpha) < EvalConstants.ScoreMateBound) { if (eval + RazoringMargin[depth] < alpha) { score = QuiescenceUtil.CalculateBestMove(cb, threadData, alpha - RazoringMargin[depth], alpha - RazoringMargin[depth] + 1); if (score + RazoringMargin[depth] <= alpha) { if (Statistics.Enabled) { Statistics.Razored[depth]++; } return(score); } } } /* null-move */ if (EngineConstants.EnableNullMove) { if (nullMoveCounter < 2 && eval >= beta && MaterialUtil.HasNonPawnPieces(cb.MaterialKey, cb.ColorToMove)) { cb.DoNullMove(); // TODO less reduction if stm (other side) has only 1 major piece var reduction = depth / 4 + 3 + Math.Min((eval - beta) / 80, 3); score = depth - reduction <= 0 ? -QuiescenceUtil.CalculateBestMove(cb, threadData, -beta, -beta + 1) : -CalculateBestMove(cb, threadData, ply + 1, depth - reduction, -beta, -beta + 1, nullMoveCounter + 1); cb.UndoNullMove(); if (score >= beta) { if (Statistics.Enabled) { Statistics.NullMoveHit++; } return(score); } if (Statistics.Enabled) { Statistics.NullMoveMiss++; } } } } var parentMove = ply == 0 ? 0 : threadData.Previous(); var bestMove = 0; var bestScore = Util.ShortMin - 1; var ttMove = 0; var counterMove = 0; var killer1Move = 0; var killer2Move = 0; var movesPerformed = 0; threadData.StartPly(); var phase = PhaseTt; while (phase <= PhaseQuiet) { switch (phase) { case PhaseTt: if (ttEntry.Key != 0) { ttMove = ttEntry.Move; if (cb.IsValidMove(ttMove)) { threadData.AddMove(ttMove); } // else { // throw new RuntimeException("invalid tt-move found: " + new MoveWrapper(ttMove)); // } } break; case PhaseAttacking: MoveGenerator.GenerateAttacks(threadData, cb); threadData.SetMvvlvaScores(); threadData.Sort(); break; case PhaseKiller1: killer1Move = threadData.GetKiller1(ply); if (killer1Move != 0 && killer1Move != ttMove && cb.IsValidMove(killer1Move)) { threadData.AddMove(killer1Move); break; } phase++; goto case PhaseKiller2; case PhaseKiller2: killer2Move = threadData.GetKiller2(ply); if (killer2Move != 0 && killer2Move != ttMove && cb.IsValidMove(killer2Move)) { threadData.AddMove(killer2Move); break; } phase++; goto case PhaseCounter; case PhaseCounter: counterMove = threadData.GetCounter(cb.ColorToMove, parentMove); if (counterMove != 0 && counterMove != ttMove && counterMove != killer1Move && counterMove != killer2Move && cb.IsValidMove(counterMove)) { threadData.AddMove(counterMove); break; } phase++; goto case PhaseQuiet; case PhaseQuiet: MoveGenerator.GenerateMoves(threadData, cb); threadData.SetHhScores(cb.ColorToMove); threadData.Sort(); break; } while (threadData.HasNext()) { var move = threadData.Next(); switch (phase) { case PhaseQuiet when move == ttMove || move == killer1Move || move == killer2Move || move == counterMove || !cb.IsLegal(move): case PhaseAttacking when move == ttMove || !cb.IsLegal(move): continue; } // pruning allowed? if (!isPv && cb.CheckingPieces == 0 && movesPerformed > 0 && threadData.GetMoveScore() < 100 && !cb.IsDiscoveredMove(MoveUtil.GetFromIndex(move))) { if (phase == PhaseQuiet) { /* late move pruning */ if (EngineConstants.EnableLmp && depth <= 4 && movesPerformed >= depth * 3 + 3) { if (Statistics.Enabled) { Statistics.Lmped[depth]++; } continue; } /* futility pruning */ if (EngineConstants.EnableFutilityPruning && depth < FutilityMargin.Length) { if (!MoveUtil.IsPawnPush78(move)) { if (eval == Util.ShortMin) { eval = EvalUtil.GetScore(cb, threadData); } if (eval + FutilityMargin[depth] <= alpha) { if (Statistics.Enabled) { Statistics.Futile[depth]++; } continue; } } } } /* SEE Pruning */ else if (EngineConstants.EnableSeePruning && depth <= 6 && phase == PhaseAttacking && SeeUtil.GetSeeCaptureScore(cb, move) < -20 * depth * depth) { continue; } } cb.DoMove(move); movesPerformed++; /* draw check */ if (cb.IsRepetition(move) || MaterialUtil.IsDrawByMaterial(cb)) { score = EvalConstants.ScoreDraw; } else { score = alpha + 1; // initial is above alpha var reduction = 1; if (depth > 2 && movesPerformed > 1 && MoveUtil.IsQuiet(move) && !MoveUtil.IsPawnPush78(move)) { reduction = LmrTable[Math.Min(depth, 63)][Math.Min(movesPerformed, 63)]; if (threadData.GetMoveScore() > 40) { reduction -= 1; } if (move == killer1Move || move == killer2Move || move == counterMove) { reduction -= 1; } if (!isPv) { reduction += 1; } reduction = Math.Min(depth - 1, Math.Max(reduction, 1)); } /* LMR */ if (EngineConstants.EnableLmr && reduction != 1) { score = -CalculateBestMove(cb, threadData, ply + 1, depth - reduction, -alpha - 1, -alpha, 0); } /* PVS */ if (EngineConstants.EnablePvs && score > alpha && movesPerformed > 1) { score = -CalculateBestMove(cb, threadData, ply + 1, depth - 1, -alpha - 1, -alpha, 0); } /* normal bounds */ if (score > alpha) { score = -CalculateBestMove(cb, threadData, ply + 1, depth - 1, -beta, -alpha, 0); } } cb.UndoMove(move); if (score > bestScore) { bestScore = score; bestMove = move; if (ply == 0 && IsRunning) { threadData.SetBestMove(cb, bestMove, alphaOrig, beta, bestScore, depth); } alpha = Math.Max(alpha, score); if (alpha >= beta) { if (Statistics.Enabled) { Statistics.FailHigh[Math.Min(movesPerformed - 1, Statistics.FailHigh.Length - 1)]++; } /* killer and history */ if (MoveUtil.IsQuiet(move) && cb.CheckingPieces == 0) { threadData.AddCounterMove(cb.ColorToMove, parentMove, move); threadData.AddKillerMove(move, ply); threadData.AddHhValue(cb.ColorToMove, move, depth); } phase += 10; break; } } if (MoveUtil.IsQuiet(move)) { threadData.AddBfValue(cb.ColorToMove, move, depth); } } phase++; } threadData.EndPly(); /* checkmate or stalemate */ if (movesPerformed == 0) { if (cb.CheckingPieces == 0) { if (Statistics.Enabled) { Statistics.StaleMateCount++; } return(EvalConstants.ScoreDraw); } if (Statistics.Enabled) { Statistics.MateCount++; } return(Util.ShortMin + ply); } if (EngineConstants.Assert) { Assert.IsTrue(bestMove != 0); } // set tt-flag var flag = TtUtil.FlagExact; if (bestScore >= beta) { flag = TtUtil.FlagLower; } else if (bestScore <= alphaOrig) { flag = TtUtil.FlagUpper; } if (IsRunning) { TtUtil.AddValue(cb.ZobristKey, bestScore, ply, depth, flag, bestMove); } Statistics.SetBestMove(cb, bestMove, ttMove, ttEntry, flag, counterMove, killer1Move, killer2Move); if (EngineConstants.TestTtValues) { SearchTestUtil.TestTtValues(score, bestScore, depth, bestMove, flag, ttEntry, ply); } return(bestScore); }
// Inspiration from VICE Chess Engine. public int AlphaBeta(int alpha, int beta, int depth, BoardState bs, SearchInfo sInfo) { if (depth == 0) { //return _eval.EvalPosition(bs); return(Quiescene(alpha, beta, bs, sInfo)); } // Check if time is up or interrupted by the GUI. if (sInfo.IsTimeUp()) { sInfo.Stopped = true; } sInfo.Nodes++; /*if (bs.BestPly != null) * { * if (bs.FiftyMoveRule >= 100 || _helper.IsRepetition(bs)) * { * return 0; * //Console.WriteLine("bestmove: {0}{1}, score {2}", Definitions.IndexToAlgebraic[bs.BestPly.GetFromToSquare()[0]], Definitions.IndexToAlgebraic[bs.BestPly.GetFromToSquare()[1]], bs.BestPly.Score); * } * }*/ var mg = new MoveGenerator(bs); var legalMoves = mg.AllLegalMoves(); Ply bestMove = null; var nMoves = legalMoves.Length; var oldAlpha = alpha; var moveNum = 0; if (bs.BestPlyAtLowerDepth != null && !bs.HaveSearched) { mg.MakeMove(bs.BestPlyAtLowerDepth); bs.BestPlyAtLowerDepth.Score = -AlphaBeta(-beta, -alpha, depth - 1, bs, sInfo); mg.UndoMove(bs.BestPlyAtLowerDepth); bs.HaveSearched = true; bestMove = bs.BestPlyAtLowerDepth; alpha = bestMove.Score; } for (moveNum = 0; moveNum < legalMoves.Length; moveNum++) { mg.MakeMove(legalMoves[moveNum]); legalMoves[moveNum].Score = -AlphaBeta(-beta, -alpha, depth - 1, bs, sInfo); mg.UndoMove(legalMoves[moveNum]); if (sInfo.Stopped) { return(Definitions.Stopped); } if (legalMoves[moveNum].Score >= beta) { if (nMoves == 1) { sInfo.Fhf++; } sInfo.Fh++; return(beta); // Fail hard beta-cutoff. } if (legalMoves[moveNum].Score > alpha) { alpha = legalMoves[moveNum].Score; // alpha acts like max in minimax. bestMove = legalMoves[moveNum]; } } if (nMoves == 0) { if (_helper.IsKingInCheck(bs.SideToMove, bs.BoardRepresentation, bs.KingSquares)) { return(-Definitions.MATE + bs.Ply); } // Stalemate. return(0); } if (alpha != oldAlpha) { bs.BestPly = bestMove; } return(alpha); }
public AlphaBetaAlgoritm(Field field, MoveGenerator moveGenerator = null, Estimator estimator = null) { Field = field; MoveGenerator = moveGenerator ?? new StandartMoveGenerator(field); Estimator = estimator ?? new Estimator(field); }
public void GetPawnMoves() => MoveGenerator.AddAllPawnMoves(_position, new List <Move>());
public UndoMoveTests() { target = new MoveGenerator(GameState.FromForsythEdwardsNotation("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -")); }
public void GetBishopMoves() => MoveGenerator.AddBishopMoves(_position, new List <Move>());
private async UniTask Run() { UniTask.Run(RedirectLog).Forget(); var tbpStartMessage = new TbpStartMessage { board = field.Rows.Take(40).Select(r => r.blocks.Select(b => b ? b.blockID : null).ToArray()).ToList(), back_to_back = field.BackToBack, combo = field.Ren + 1, hold = field.Hold != null ? MatchEnvironment.blockRegistry[(int)field.Hold].blockID : null, queue = field.Next.Select(i => MatchEnvironment.pieceRegistry[i].name).ToList(), randomizer = new SevenBagRandomizerStart(new List <string>(fb.Bag)) }; { var line = await stdout.ReadLineAsync(); LogBotMessage(line); if (line == null) { return; } var msg = JsonSerializer.Deserialize <TbpBotMessage>(line); if (msg.type != BotMessageType.info) { Debug.LogError("Killed bot because received message was unexpected. " + $"Expected: {nameof(BotMessageType.info)}, Actual: {msg.type}"); return; } BotInfo = JsonSerializer.Deserialize <TbpInfoMessage>(line); await Send(new TbpRulesMessage()); } { var line = await stdout.ReadLineAsync(); LogBotMessage(line); if (line == null) { return; } var msg = JsonSerializer.Deserialize <TbpBotMessage>(line); if (msg.type != BotMessageType.ready) { Debug.LogError("Killed bot because received message was unexpected. " + $"Expected: {nameof(BotMessageType.ready)}, Actual: {msg.type}"); return; } Status = BotStatus.Ready; } await UniTask.Yield(PlayerLoopTiming.FixedUpdate); await Send(tbpStartMessage); Status = BotStatus.Running; await UniTask.SwitchToThreadPool(); try { while (true) { var line = await stdout.ReadLineAsync(); LogBotMessage(line); if (line == null) { return; } var msg = JsonSerializer.Deserialize <TbpBotMessage>(line); if (msg.type == BotMessageType.suggestion) { ResponseTimeInMillisecond = suggestionTimer.ElapsedMilliseconds; if (moves == null) { await UniTask.WaitWhile(() => moves == null, PlayerLoopTiming.FixedUpdate); } var suggestion = JsonSerializer.Deserialize <TbpSuggestionMessage>(line); var success = false; PickedMoveIndex = 0; foreach (var candidate in suggestion.moves.Select(c => (Piece)c)) { foreach (var can in candidate.GetCanonicals()) { if (!moves !.locked.ContainsKey(can)) { continue; } await UniTask.WhenAll( Send(new TbpPlayMessage(can)).AsAsyncUnitUniTask(), UniTask.Run(() => pickedPath = moves.RebuildPath(can, candidate.kind != fb.currentPiece.content.kind))); moves = null; success = true; UniTask.Run(() => controller.MoveAsync(pickedPath !.Value.instructions, candidate.kind != fb.currentPiece.content.kind, fb.CancelTokenSource.Token)) .Forget(); goto then; } PickedMoveIndex++; } then: if (!success) { forfeit = true; return; } } else { Debug.LogError("Killed bot because received message was unexpected. " + $"Expected: {nameof(BotMessageType.suggestion)}, Actual: {msg.type}"); return; } } } finally { await Quit(); } }
public void GetQueenMoves() => MoveGenerator.AddQueenMoves(_position, new List <Move>());
public void addNode(double piece_in, int[] startPos_in, int[] endPos_in, double weight_in, int lvlID, MoveGenerator main) { while (current.levelID != lvlID - 1) //Adding in new nodes will need to be set in at the current level { if (current.levelID < lvlID - 1) // move down to the parent node { current = current.Back; current = current.Next[current.SIZE - 1]; current = current.Next[current.SIZE - 1]; } if (current.levelID > lvlID - 1) // move up to the parent node { current = current.Back; } } Node node = new Node(piece_in, startPos_in, endPos_in, weight_in, lvlID); // create the node current.Next.Add(node); // add the node to its parent current.SIZE++; // increment the parent's size previous = current; // parent is now previous current = node; // current is the node we just made current.Back = previous; // set the back option of this node to the parent we just left }
public MakeMoveTests() { target = new MoveGenerator(initial); }
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 virtual void Init(bool white) { isWhite = white; moveGenerator = new MoveGenerator (); }
public void Ctor_WillSetupPossibleMoves() { var moveGenerator = new MoveGenerator(); Assert.AreEqual(MoveGenerator.MaxMovesToGenerate, moveGenerator.PossibleMoves.Count); }
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))); }
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 Setup() { _moveGenerator = new MoveGenerator(); }
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; }
/// 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); }
/// 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; }
private void Awake() { moveGenerator = new MoveGenerator(); CreateBoardUI(); }
/// <summary> /// 主要变例搜索 /// </summary> /// <param name="depth">最底部的叶子结点为第0层</param> /// <param name="alpha"></param> /// <param name="beta"></param> /// <returns></returns> private int PVS(int depth, int alpha, int beta) { bool haveFoundPV = false; ulong boardKey = board.ZobristKey; // Zobrist.ZoristHash(board); BoardHistory[Ply] = boardKey; if (IsRepetition(boardKey, Ply, BoardHistory, HalfmoveClock[Ply])) { bool redRepCheck = true; bool blackRepCheck = true; Board b = new Board(board); for (int d = 0; d < HalfmoveClock[Ply]; d++) { if (Ply - d == 0) { break; } // TODO: 逻辑不合理的代码:关于将军的判断应该放在Board类中 MoveGenerator g = new MoveGenerator(b); if (b.IsRedTurn) { if (redRepCheck && g.IsBlackKingSafe()) { redRepCheck = false; } } else { if (blackRepCheck && g.IsRedKingSafe()) { blackRepCheck = false; } } b.UnmakeMove(MoveHistory[Ply - d]); if (board.ZobristKey == b.ZobristKey) { break; } } if (redRepCheck && blackRepCheck) { return(0); //双方都长将,平局分数 } if (redRepCheck) { return(-30000 + 100); } if (blackRepCheck) { return(30000 - 100); } return(0); // 双方都是允许着法时 } // 探查置换表 NodeOfSearch entry = transpositionTable.Probe(boardKey); // (depth=2) a // / \ // (depth=1) entry<b> c 这里的b是已经搜索过的节点,已经保存在置换表中,entry.Depth = 1 // / \ \ // (depth=0) d e [f] (当前搜索的节点是f) depth = 0 // .................. // 假设b是以前搜索过的节点,已经保存在置换表中,entry.depth=1 // 当前搜索的节点是f,depth=0,如果f与b的局面相同,由于b的评估值是经过了更深层的搜索得到的, // 所以f可以直接用b的评估值,这个结果只会好,不会差,所以应该判断entry.Depth >= depth if (entry.NodeType != NodeOfSearch.EMPTY_NODES && entry.Depth > depth) { switch (entry.NodeType) { case NodeOfSearch.PV_NODES: return(entry.Score); case NodeOfSearch.CUT_NODES: // -------------\ /-----------------\ // 评估值的范围 | | 当前搜索窗口 | //--------------+---------+-----------------+-- // entry.Score <= alpha beta if (entry.Score <= alpha) // 剪枝! { return(alpha); } //-------------------------\ // 评估值的范围 | // /-----------------+---------------\ // | 当前搜| 索窗口 | //-------+-----------------+---------------+------- // alpha entry.Score < beta if (entry.Score < beta) //调整beta即可 { beta = entry.Score; } break; case NodeOfSearch.ALL_NODES: // /-----------------\ /------------- // | 当前搜索窗口 | |评估值的范围 //------+-----------------+------+-------------- // alpha beta <= entry.Score if (beta <= entry.Score) // 剪枝! { return(beta); } // /----------------------- // | 评估值的范围 // /-----------------+---------------\ // | 当前搜| 索窗口 | //-------+-----------------+---------------+------- // alpha < entry.Score beta if (alpha < entry.Score) // 此时只要调整alpha即可 { alpha = entry.Score; } break; } } // 到达叶子节点 if (depth == 0) { int valueLeaf = Evaluator.EvaluateAllWithSide(board); NodeOfSearch nodeLeaf = new NodeOfSearch(boardKey, depth, NodeOfSearch.PV_NODES, valueLeaf); transpositionTable.RecordHash(nodeLeaf); return(valueLeaf); } int nodeType = NodeOfSearch.ALL_NODES; Move[] moveList = new Move[200]; int countMove = MoveGenerator.GenAllMoveList(board, moveList); // 无着可走,说明是终止局面,即被将死 if (countMove == 0) { int scoreEndStatus = Evaluator.MIN_EVAL_VALUE + Ply; NodeOfSearch nodeEnd = new NodeOfSearch(boardKey, depth, NodeOfSearch.PV_NODES, scoreEndStatus); transpositionTable.RecordHash(nodeEnd); return(scoreEndStatus); } // 利用了置换表中的历史评估数据,进行着法排序 // 局面"9/4a4/3k5/3N5/3N5/r8/9/9/9/4K4 w" // 用迭代加深算法来测试效果:迭代加深计算到第8层 // DEBUG // 不排序时1.7秒,探查并对着法排序时:17秒,代价很大 // Release // 不排序时0.7秒,探查并对着法排序时:7秒 // if(depth == 0) // SortMovelist(moveList, countMove); Move bestMove = null; for (int i = 0; i < countMove; i++) { ++Ply; // 胜利局面中需要这个变量, -MAX + ply MoveHistory[Ply] = moveList[i]; HalfmoveClock[Ply] = moveList[i].Irreversible ? 0 : HalfmoveClock[Ply - 1] + 1; board.MakeMove(moveList[i]); int score; if (haveFoundPV) { score = -PVS(depth - 1, -alpha - 1, -alpha); if ((score > alpha) && (score < beta)) { // 检查失败 score = -PVS(depth - 1, -beta, -alpha); } } else { score = -PVS(depth - 1, -beta, -alpha); } board.UnmakeMove(moveList[i]); --Ply; if (score >= beta) { //PrintDebugInfo("发生剪枝!bestScore >= beta: " + bestScore + " >= " + beta); NodeOfSearch nodeBeta = new NodeOfSearch(boardKey, depth, NodeOfSearch.CUT_NODES, beta, moveList[i]); transpositionTable.RecordHash(nodeBeta); return(beta); } if (score > alpha) { // 这时只是记录alpha值的变化情况,并不写置换表 nodeType = NodeOfSearch.PV_NODES; alpha = score; bestMove = moveList[i]; //PrintDebugInfo("修改alpha: " + alpha); } if (engine != null && stopWatch.ElapsedMilliseconds > engine.timeLimit) { break; } } NodeOfSearch node = new NodeOfSearch(boardKey, depth, nodeType, alpha, bestMove); transpositionTable.RecordHash(node); return(alpha); }