// Takes ownership of all arrays passed to it; they should not be changed after the position is created. public Position(Piece[] pieceSquares, Color sideToMove, ulong castlingRights, ulong enPassant, int fiftyMoveCounter, int fullMove) { _pieceBitboards = new BitboardArray(); _squares = new PieceSquareArray(); // safer to go by the length the PieceSquareArray, because the Piece[] will always error on index out of bounds for (int i = 0; i < _squares.Length; i++) { _squares[i] = pieceSquares[i]; } PopulatePieceBitboardsFromSquares(ref _pieceBitboards, pieceSquares); _occupiedWhite = CalculateOccupied(Color.White); _occupiedBlack = CalculateOccupied(Color.Black); _occupied = _occupiedWhite | _occupiedBlack; Parent = null; SideToMove = sideToMove; CastlingRights = castlingRights; EnPassant = enPassant; FiftyMoveCounter = fiftyMoveCounter; GamePly = GamePlyFromFullMove(fullMove, sideToMove); _historyPly = 0; RepetitionNumber = 1; ZobristHash = ZobristHashing.CalculateFullHash(this); }
public static Position MakeMove(Position shell, Move move, Position parent) { // copy data which doesn't depend on performing the move parent.CopyPieceBitboards(ref shell._pieceBitboards); parent.CopySquares(ref shell._squares); shell.ZobristHash = parent.ZobristHash; shell.Parent = parent; shell._inCheckWhite = null; shell._inCheckBlack = null; shell.SideToMove = parent.SideToMove.Other(); shell.EnPassant = 0; // this gets set for double moves shell.FiftyMoveCounter = CalculateFiftyMoveCounter(parent, move); shell.GamePly = parent.GamePly + 1; shell._historyPly = parent._historyPly + 1; switch (move.MoveType) { case MoveType.Normal | MoveType.Quiet: shell.MakeNormalQuietMove(move); break; case MoveType.Normal | MoveType.Capture: shell.MakeNormalCaptureMove(move); break; case MoveType.DoubleMove | MoveType.Quiet: shell.MakeDoublePawnMove(move); break; case MoveType.EnPassant | MoveType.Capture: shell.MakeEnPassantMove(move); break; case MoveType.Promotion | MoveType.Quiet: shell.MakePromotionQuietMove(move); break; case MoveType.Promotion | MoveType.Capture: shell.MakePromotionCaptureMove(move); break; case MoveType.Castling | MoveType.Quiet: shell.MakeCastlingMove(move); break; default: throw new Exception($"Invalid move type: {move.MoveType}"); } shell.CastlingRights = parent.CastlingRights & CastlingTables.GetCastlingUpdateMask(move); shell._occupiedWhite = shell.CalculateOccupied(Color.White); shell._occupiedBlack = shell.CalculateOccupied(Color.Black); shell._occupied = shell._occupiedWhite | shell._occupiedBlack; shell.ZobristHash ^= ZobristHashing.OtherHashDiff(parent, shell); shell.RepetitionNumber = CalculateRepetitionNumber(shell); return(shell); }
private void RemovePiece(int ix, Color color, PieceType pieceType) { Debug.Assert(_squares[ix] == new Piece(color, pieceType)); _pieceBitboards[PieceBitboardIndex(color, pieceType)] &= ~ValueFromIx(ix); _squares[ix] = Piece.None; ZobristHash ^= ZobristHashing.CalculatePieceBitboardHashDiff(ValueFromIx(ix), color, pieceType); // TODO: material hash update // TODO: pawn hash update }
private void MovePiece(int srcIx, int dstIx, Color color, PieceType pieceType) { Debug.Assert(_squares[srcIx] == new Piece(color, pieceType)); Debug.Assert(_squares[dstIx].PieceType == PieceType.None); _pieceBitboards[PieceBitboardIndex(color, pieceType)] &= ~ValueFromIx(srcIx); _pieceBitboards[PieceBitboardIndex(color, pieceType)] |= ValueFromIx(dstIx); _squares[srcIx] = Piece.None; _squares[dstIx] = new Piece(color, pieceType); ZobristHash ^= ZobristHashing.CalculatePieceBitboardHashDiff( ValueFromIx(srcIx) | ValueFromIx(dstIx), color, pieceType); // No material hash update needed // TODO: pawn hash update }
public static BoardState Parse(string fen, bool allocateStacks) { var split = fen.Split(' '); var boardState = split[0]; var colorState = split[1]; var castlingState = split[2]; var enPassantState = split[3]; var halfmoveClock = 0; var movesCount = 0; if (split.Length > 4) { int.TryParse(split[4], out halfmoveClock); } if (split.Length > 5) { int.TryParse(split[5], out movesCount); } var result = new BoardState(allocateStacks); var currentColor = ParseCurrentColor(colorState); ParseBoardState(boardState, result); ParseCastlingState(castlingState, result); ParseEnPassantState(enPassantState, result); result.RecalculateEvaluationDependentValues(); result.CalculatePieceTable(result.PieceTable); result.MovesCount = movesCount; result.IrreversibleMovesCount = halfmoveClock; result.ColorToMove = currentColor; result.Hash = ZobristHashing.CalculateHash(result); result.PawnHash = ZobristHashing.CalculatePawnHash(result); result.CastlingDone[Color.White] = (result.Castling & Castling.WhiteCastling) == 0; result.CastlingDone[Color.Black] = (result.Castling & Castling.BlackCastling) == 0; return(result); }
public void NoTwoKeysShouldBeTheSame() { var hash = new ZobristHashing(); for (int i = 0; i < hash.PieceKeys.Length; i++) { for (int j = i + 1; j < hash.PieceKeys.Length; j++) { (hash.PieceKeys[i] == hash.PieceKeys[j]).Should().BeFalse(); } for (int j = 0; j < hash.CastleKeys.Length; j++) { (hash.PieceKeys[i] == hash.CastleKeys[j]).Should().BeFalse(); } for (int j = 0; j < hash.EnPassantKeys.Length; j++) { (hash.PieceKeys[i] == hash.EnPassantKeys[j]).Should().BeFalse(); } (hash.PieceKeys[i] == hash.SideKey).Should().BeFalse(); } for (int i = 0; i < hash.CastleKeys.Length; i++) { for (int j = i + 1; j < hash.CastleKeys.Length; j++) { (hash.CastleKeys[i] == hash.CastleKeys[j]).Should().BeFalse(); } for (int j = 0; j < hash.EnPassantKeys.Length; j++) { (hash.CastleKeys[i] == hash.EnPassantKeys[j]).Should().BeFalse(); } (hash.CastleKeys[i] == hash.SideKey).Should().BeFalse(); } for (int i = 0; i < hash.EnPassantKeys.Length; i++) { for (int j = i + 1; j < hash.EnPassantKeys.Length; j++) { (hash.EnPassantKeys[i] == hash.EnPassantKeys[j]).Should().BeFalse(); } (hash.EnPassantKeys[i] == hash.SideKey).Should().BeFalse(); } }
private static bool VerifyBoard(BoardState board) { if (board.Material[Color.White] != board.CalculateMaterial(Color.White) || board.Material[Color.Black] != board.CalculateMaterial(Color.Black)) { return(false); } if (board.Hash != ZobristHashing.CalculateHash(board)) { return(false); } if (board.PawnHash != ZobristHashing.CalculatePawnHash(board)) { return(false); } if (board.Position[Color.White][GamePhase.Opening] != board.CalculatePosition(Color.White, GamePhase.Opening) || board.Position[Color.White][GamePhase.Ending] != board.CalculatePosition(Color.White, GamePhase.Ending) || board.Position[Color.Black][GamePhase.Opening] != board.CalculatePosition(Color.Black, GamePhase.Opening) || board.Position[Color.Black][GamePhase.Ending] != board.CalculatePosition(Color.Black, GamePhase.Ending)) { return(false); } var pieceTable = new int[64]; board.CalculatePieceTable(pieceTable); for (var fieldIndex = 0; fieldIndex < 64; fieldIndex++) { if (board.PieceTable[fieldIndex] != pieceTable[fieldIndex]) { return(false); } } return(true); }
private void ZobristTestHelper(Position position, int depth) { Assert.AreEqual(position.ZobristHash, ZobristHashing.CalculateFullHash(position)); if (depth <= 0) { return; } var moves = new List <Move>(); _moveGenerator.Generate(moves, position); foreach (var move in moves) { var updatedBoard = Position.MakeMove(new Position(), move, position); if (!_moveGenerator.OnlyLegalMoves && updatedBoard.MovedIntoCheck()) { continue; } ZobristTestHelper(updatedBoard, depth - 1); } }
public static BoardState Parse(string fen) { var split = fen.Split(' '); var boardState = split[0]; var colorState = split[1]; var castlingState = split[2]; var enPassantState = split[3]; var halfmoveClock = split.Length > 4 ? int.Parse(split[4]) : 0; var movesCount = split.Length > 5 ? int.Parse(split[5]) : 0; var result = new BoardState(); var currentColor = ParseCurrentColor(colorState); ParseBoardState(boardState, result); ParseCastlingState(castlingState, result); ParseEnPassantState(enPassantState, result); result.Material[Color.White] = result.CalculateMaterial(Color.White); result.Material[Color.Black] = result.CalculateMaterial(Color.Black); result.Position[Color.White][GamePhase.Opening] = result.CalculatePosition(Color.White, GamePhase.Opening); result.Position[Color.White][GamePhase.Ending] = result.CalculatePosition(Color.White, GamePhase.Ending); result.Position[Color.Black][GamePhase.Opening] = result.CalculatePosition(Color.Black, GamePhase.Opening); result.Position[Color.Black][GamePhase.Ending] = result.CalculatePosition(Color.Black, GamePhase.Ending); result.CalculatePieceTable(result.PieceTable); result.MaterialAtOpening = result.CalculateMaterialAtOpening(); result.MovesCount = movesCount; result.IrreversibleMovesCount = halfmoveClock; result.ColorToMove = currentColor; result.Hash = ZobristHashing.CalculateHash(result); result.PawnHash = ZobristHashing.CalculatePawnHash(result); return(result); }