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);
		}
Пример #2
0
	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();
			}
		
		}
	}
Пример #3
0
        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)));
        }
Пример #4
0
	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 ();
	}
Пример #5
0
        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);
        }
Пример #6
0
	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();
		}
Пример #9
0
        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);
        }
Пример #10
0
        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()));
        }
Пример #11
0
        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);
        }
Пример #12
0
        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));
        }
Пример #13
0
        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);
        }
Пример #14
0
    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;
                }
            }
        }
    }
Пример #15
0
        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);
        }
Пример #16
0
        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);
        }
Пример #18
0
        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);
        }
Пример #19
0
        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);
        }
Пример #20
0
    /// <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)]);
    }
Пример #21
0
        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);
            }
        }
Пример #22
0
 public void GetCastlingMoves() => MoveGenerator.AddCastlingMoves(_position, new List <Move>());
Пример #23
0
 public void GetRookMoves() => MoveGenerator.AddRookMoves(_position, new List <Move>());
Пример #24
0
 public void GetKnightMoves() => MoveGenerator.AddKnightMoves(_position, new List <Move>());
Пример #25
0
        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);
        }
Пример #26
0
    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);
    }
Пример #27
0
        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);
		}
Пример #29
0
        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);

		}
Пример #31
0
        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);
        }
Пример #32
0
        // 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);
        }
Пример #33
0
 public AlphaBetaAlgoritm(Field field, MoveGenerator moveGenerator = null, Estimator estimator = null)
 {
     Field = field;
     MoveGenerator = moveGenerator ?? new StandartMoveGenerator(field);
     Estimator = estimator ?? new Estimator(field);
 }
Пример #34
0
 public void GetPawnMoves() => MoveGenerator.AddAllPawnMoves(_position, new List <Move>());
Пример #35
0
 public UndoMoveTests()
 {
     target = new MoveGenerator(GameState.FromForsythEdwardsNotation("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -"));
 }
Пример #36
0
 public void GetBishopMoves() => MoveGenerator.AddBishopMoves(_position, new List <Move>());
Пример #37
0
        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();
            }
        }
Пример #38
0
 public void GetQueenMoves() => MoveGenerator.AddQueenMoves(_position, new List <Move>());
Пример #39
0
        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
        }
Пример #40
0
 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);
		}
Пример #42
0
	public virtual void Init(bool white) {

		isWhite = white;
		moveGenerator = new MoveGenerator ();
	}
Пример #43
0
        public void Ctor_WillSetupPossibleMoves()
        {
            var moveGenerator = new MoveGenerator();

            Assert.AreEqual(MoveGenerator.MaxMovesToGenerate, moveGenerator.PossibleMoves.Count);
        }
Пример #44
0
        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");
		}
Пример #46
0
 public void Setup()
 {
     _moveGenerator = new MoveGenerator();
 }
Пример #47
0
	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;
	}
Пример #48
0
    /// 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);
    }
Пример #49
0
	/// 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;
	}
Пример #50
0
 private void Awake()
 {
     moveGenerator = new MoveGenerator();
     CreateBoardUI();
 }
Пример #51
0
        /// <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);
        }