makeStandardMove method
Implementation
BishopState? makeStandardMove(BishopState state, Move move) {
if (move is! StandardMove && move is! DropMove) {
return null;
}
Square fromSq =
move.from >= Bishop.boardStart ? state.board[move.from] : Bishop.empty;
List<int> board = [...state.board];
if ((move.from != Bishop.hand && !size.onBoard(move.from)) ||
!size.onBoard(move.to)) {
return null;
}
int hash = state.hash;
hash ^= zobrist.table[zobrist.turn][Zobrist.meta];
List<Hand>? hands = state.hands != null
? List.generate(state.hands!.length, (i) => List.from(state.hands![i]))
: null;
List<List<int>> virginFiles = List.generate(
state.virginFiles.length,
(i) => List.from(state.virginFiles[i]),
);
List<int> pieces = List.from(state.pieces);
GameResult? result;
// TODO: more validation?
final int fromRank = size.rank(move.from);
final PieceType fromPiece = variant.pieces[fromSq.type].type;
if (fromSq.isNotEmpty &&
(fromSq.colour != state.turn &&
fromSq.colour != Bishop.neutralPassive)) {
return null;
}
final int colour = turn;
// Remove the moved piece, if this piece came from on the board.
if (move.from >= Bishop.boardStart) {
hash ^= zobrist.table[move.from][fromSq.piece];
if (move.promotion) {
pieces[fromSq.piece]--;
}
board[move.from] = Bishop.empty;
// Mark the file as touched.
if ((fromRank == 0 && colour == Bishop.white) ||
(fromRank == size.v - 1 && colour == Bishop.black)) {
virginFiles[colour].remove(size.file(move.from));
}
}
// Add captured piece to hand
if (variant.addCapturesToHand && move.capture) {
int piece = move.capturedPiece!.hasInternalType
? move.capturedPiece!.internalType
: move.capturedPiece!.type;
hands![colour].add(piece);
pieces[makePiece(piece, colour)]++;
}
// Remove captured piece from hash and pieces list
if (move.capture && !move.enPassant) {
int p = board[move.to].piece;
hash ^= zobrist.table[move.to][p];
pieces[p]--;
}
if (!move.castling && !move.promotion) {
// Move the piece to the new square
int putPiece = move.from >= Bishop.boardStart
? fromSq.setInitialState(false)
: makePiece(move.dropPiece!, colour);
hash ^= zobrist.table[move.to][putPiece.piece];
board[move.to] = putPiece;
// note that it's possible to have a drop move without hands (e.g. duck chess)
if (move.from == Bishop.hand) hands?[colour].remove(move.dropPiece!);
} else if (move.promotion) {
// Place the promoted piece
board[move.to] =
makePiece(move.promoPiece!, state.turn, internalType: fromSq.type);
hash ^= zobrist.table[move.to][board[move.to].piece];
pieces[board[move.to].piece]++;
}
// Manage halfmove counter
int halfMoves = state.halfMoves;
if (move.capture || fromPiece.promoOptions.canPromote) {
halfMoves = 0;
} else {
halfMoves++;
}
int castlingRights = state.castlingRights;
List<int> royalSquares = List.from(state.royalSquares);
if (move.enPassant) {
// Remove the captured ep piece
int captureSq = state.move?.to ??
(move.to + Bishop.playerDirection[colour.opponent] * size.north);
hash ^= zobrist.table[captureSq][board[captureSq].piece];
pieces[board[captureSq].piece]--;
board[captureSq] = Bishop.empty;
}
int? epSquare;
if (move.setEnPassant) {
// Set the new ep square
int dir = (move.to - move.from) ~/ 2;
epSquare = move.from + dir;
hash ^= zobrist.table[epSquare][Zobrist.meta];
} else {
epSquare = null;
}
if (state.epSquare != null) {
// XOR the old ep square away from the hash
hash ^= zobrist.table[state.epSquare!][Zobrist.meta];
}
if (move.castling) {
move = move as StandardMove;
bool kingside = move.castlingDir == Castling.k;
int castlingFile = kingside
? variant.castlingOptions.kTarget!
: variant.castlingOptions.qTarget!;
int rookFile = kingside ? castlingFile - 1 : castlingFile + 1;
int rookSq = size.square(rookFile, fromRank);
int kingSq = size.square(castlingFile, fromRank);
int rook = board[move.castlingPieceSquare!];
hash ^= zobrist.table[move.castlingPieceSquare!][rook.piece];
if (board[kingSq].isNotEmpty) {
hash ^= zobrist.table[kingSq][board[kingSq].piece];
}
hash ^= zobrist.table[kingSq][fromSq.piece];
if (board[rookSq].isNotEmpty) {
hash ^= zobrist.table[rookSq][board[rookSq].piece];
}
hash ^= zobrist.table[rookSq][rook.piece];
board[move.castlingPieceSquare!] = Bishop.empty;
board[kingSq] = fromSq.setInitialState(false);
board[rookSq] = rook;
castlingRights = castlingRights.remove(colour);
royalSquares[colour] = kingSq;
} else if (fromPiece.royal) {
// king moved
castlingRights = castlingRights.remove(colour);
royalSquares[colour] = move.to;
} else {
// If the player's rook moved, remove relevant castling rights
if (fromSq.type == variant.castlingPiece) {
int fromFile = size.file(move.from);
bool onFirstRank = size.rank(move.from) == size.firstRank(colour);
int ks = colour == Bishop.white ? Castling.k : Castling.bk;
int qs = colour == Bishop.white ? Castling.q : Castling.bq;
if (fromFile == castlingTargetK &&
onFirstRank &&
castlingRights.hasRight(ks)) {
castlingRights = castlingRights.flip(ks);
} else if (fromFile == castlingTargetQ &&
onFirstRank &&
castlingRights.hasRight(qs)) {
castlingRights = castlingRights.flip(qs);
}
}
// If the opponent's rook was captured, remove relevant castling rights
if (move.capture && move.capturedPiece!.type == variant.castlingPiece) {
// rook captured
int toFile = size.file(move.to);
int opponent = colour.opponent;
bool onFirstRank = size.rank(move.to) == size.firstRank(opponent);
int ks = opponent == Bishop.white ? Castling.k : Castling.bk;
int qs = opponent == Bishop.white ? Castling.q : Castling.bq;
if (toFile == castlingTargetK &&
onFirstRank &&
castlingRights.hasRight(ks)) {
castlingRights = castlingRights.flip(ks);
} else if (toFile == castlingTargetQ &&
onFirstRank &&
castlingRights.hasRight(qs)) {
castlingRights = castlingRights.flip(qs);
}
}
}
if (castlingRights != state.castlingRights) {
hash ^= zobrist.table[zobrist.castling][state.castlingRights];
hash ^= zobrist.table[zobrist.castling][castlingRights];
}
if (variant.hasWinRegions) {
int p = board[move.to].piece;
if (variant.pieceHasWinRegions(p) && variant.inWinRegion(p, move.to)) {
result = WonGameEnteredRegion(winner: state.turn, square: move.to);
}
}
BishopState newState = BishopState(
board: board,
move: move,
turn: 1 - state.turn,
halfMoves: halfMoves,
fullMoves:
state.turn == Bishop.black ? state.fullMoves + 1 : state.fullMoves,
castlingRights: castlingRights,
royalSquares: royalSquares,
virginFiles: virginFiles,
epSquare: epSquare,
hash: hash,
hands: hands,
gates: state.gates,
pieces: pieces,
checks: List.from(state.checks),
result: result,
);
return newState;
}