diff --git a/internal/chess/attackers.go b/internal/chess/attackers.go index 3cc8d82..8a79cee 100644 --- a/internal/chess/attackers.go +++ b/internal/chess/attackers.go @@ -9,12 +9,12 @@ func generatePawnAttacks(position Position, color Color) []Move { square := Square(pawnBB.PopLsb()) if square.File() != 1 { - move := NewMove(square, square+Square(direction)+Square(west), NormalMove, NoMoveFlag) + move := NewMove(square, square+Square(direction)+Square(west), CaptureMove) attacks = append(attacks, move) } if square.File() != 8 { - move := NewMove(square, square+Square(direction)+Square(east), NormalMove, NoMoveFlag) + move := NewMove(square, square+Square(direction)+Square(east), CaptureMove) attacks = append(attacks, move) } } @@ -33,7 +33,7 @@ func generateKnightAttacks(position Position, color Color) []Move { for moveBB > 0 { toSquare := Square(moveBB.PopLsb()) - move := NewMove(square, toSquare, NormalMove, NoMoveFlag) + move := NewMove(square, toSquare, CaptureMove) attacks = append(attacks, move) } } @@ -50,7 +50,7 @@ func generateBishopAttacks(pieceBB BitBoard, occupied BitBoard) []Move { attackBB := getBishopAttacks(occupied, fromSquare) for attackBB > 0 { toSquare := Square(attackBB.PopLsb()) - move := NewMove(fromSquare, toSquare, NormalMove, NoMoveFlag) + move := NewMove(fromSquare, toSquare, CaptureMove) attacks = append(attacks, move) } } @@ -67,7 +67,7 @@ func generateRookAttacks(pieceBB BitBoard, occupied BitBoard) []Move { attackBB := getRookAttacks(occupied, fromSquare) for attackBB > 0 { toSquare := Square(attackBB.PopLsb()) - move := NewMove(fromSquare, toSquare, NormalMove, NoMoveFlag) + move := NewMove(fromSquare, toSquare, CaptureMove) attacks = append(attacks, move) } } @@ -98,7 +98,7 @@ func generateKingAttacks(position Position, color Color) []Move { moveBB := kingMoves[fromSquare] for moveBB > 0 { toSquare := Square(moveBB.PopLsb()) - move := NewMove(fromSquare, toSquare, NormalMove, NoMoveFlag) + move := NewMove(fromSquare, toSquare, CaptureMove) attacks = append(attacks, move) } } diff --git a/internal/chess/move.go b/internal/chess/move.go index 882e124..2fe188e 100644 --- a/internal/chess/move.go +++ b/internal/chess/move.go @@ -8,16 +8,19 @@ import ( type MoveType uint8 const ( - NormalMove MoveType = iota - CastleMove + QuietMove MoveType = iota + CaptureMove EnPassantMove + CastleMove Null ) func (t MoveType) String() string { switch t { - case NormalMove: - return "Normal" + case QuietMove: + return "Quiet" + case CaptureMove: + return "Capture" case CastleMove: return "Castle" case EnPassantMove: @@ -29,14 +32,11 @@ func (t MoveType) String() string { return "" } -type MoveFlag uint16 +type MoveFlag uint8 const ( - NoMoveFlag MoveFlag = 0x0000 - QuietMoveFlag MoveFlag = 0x0001 - CaputureMoveFlag MoveFlag = 0x0010 - PawnPushMoveFlag MoveFlag = 0x0100 - PromotionMoveFlag MoveFlag = 0x1000 + NoMoveFlag MoveFlag = 0b0000 + PawnPushMoveFlag MoveFlag = 0b0001 ) type Move struct { @@ -49,15 +49,15 @@ type Move struct { promotionPiece Piece } -var NullMove = NewMove(A1, A1, Null, NoMoveFlag) +var NullMove = NewMove(A1, A1, Null) // NewMove creates a new move with the given from and to. -func NewMove(from, to Square, moveType MoveType, flags MoveFlag) Move { +func NewMove(from, to Square, moveType MoveType) Move { return Move{ from: from, to: to, moveType: moveType, - flags: flags, + flags: NoMoveFlag, promotionPiece: EmptyPiece, } } @@ -74,10 +74,13 @@ func (m Move) To() Square { // WithPromotion sets that the move will result with the moving piece being promoted to the given piece. func (m *Move) WithPromotion(piece Piece) { - m.flags |= PromotionMoveFlag m.promotionPiece = piece } +func (m *Move) WithFlags(flags MoveFlag) { + m.flags |= flags +} + // Type returns the type of the move. func (m Move) Type() MoveType { return m.moveType @@ -111,7 +114,7 @@ func (m Move) IsPromotion() bool { // IsCapture returns whether the move results in a capture. func (m Move) IsCapture() bool { - return m.HasFlag(CaputureMoveFlag) + return m.Type() == CaptureMove || m.Type() == EnPassantMove } // HasFlag checks if the move has that flag set. @@ -124,7 +127,7 @@ func (m Move) HasFlag(flag MoveFlag) bool { // Moves such as pawn moves and captures can't be reversed once done meaning // the position before the was made is no longer possible. func (m Move) IsIrreversible() bool { - return m.HasFlag(PawnPushMoveFlag) || m.HasFlag(CaputureMoveFlag) || m.HasFlag(PromotionMoveFlag) + return m.HasFlag(PawnPushMoveFlag) || m.IsCapture() || m.IsPromotion() } func (m Move) String() string { diff --git a/internal/chess/movegen.go b/internal/chess/movegen.go index a80d9d3..08ab853 100644 --- a/internal/chess/movegen.go +++ b/internal/chess/movegen.go @@ -21,19 +21,24 @@ func generatePawnMoves(position Position, genType MoveGenerationType) []Move { if toSquare.Rank() == pawnPromotionRank(position.Turn()) { for _, pieceType := range promotablePieces { - move := NewMove(square, toSquare, NormalMove, QuietMoveFlag|PawnPushMoveFlag) + move := NewMove(square, toSquare, QuietMove) + move.WithFlags(PawnPushMoveFlag) move.WithPromotion(NewPiece(pieceType, position.turn)) moves = append(moves, move) } } else { - moves = append(moves, NewMove(square, toSquare, NormalMove, QuietMoveFlag|PawnPushMoveFlag)) + move := NewMove(square, toSquare, QuietMove) + move.WithFlags(PawnPushMoveFlag) + moves = append(moves, move) } if square.Rank() == pawnStartingRank(position.turn) { toSquare := square + (dir * 2) if !position.IsSquareOccupied(toSquare) { - moves = append(moves, NewMove(square, toSquare, NormalMove, QuietMoveFlag|PawnPushMoveFlag)) + move := NewMove(square, toSquare, QuietMove) + move.WithFlags(PawnPushMoveFlag) + moves = append(moves, move) } } } @@ -54,12 +59,12 @@ func generatePawnMoves(position Position, genType MoveGenerationType) []Move { if capturePiece != EmptyPiece && capturePiece.Color() != position.turn { if captureSquare.Rank() == pawnPromotionRank(position.Turn()) { for _, pieceType := range promotablePieces { - move := NewMove(square, captureSquare, NormalMove, CaputureMoveFlag) + move := NewMove(square, captureSquare, CaptureMove) move.WithPromotion(NewPiece(pieceType, position.Turn())) moves = append(moves, move) } } else { - move := NewMove(square, captureSquare, NormalMove, CaputureMoveFlag) + move := NewMove(square, captureSquare, CaptureMove) moves = append(moves, move) } } @@ -71,7 +76,7 @@ func generatePawnMoves(position Position, genType MoveGenerationType) []Move { capturePiece, _ := position.GetPieceAt(captureSquare) if capturePiece.Type() == Pawn && capturePiece.Color() == position.Turn().OpposingSide() { - move := NewMove(square, position.EnPassant(), EnPassantMove, CaputureMoveFlag) + move := NewMove(square, position.EnPassant(), EnPassantMove) moves = append(moves, move) } } @@ -97,7 +102,7 @@ func generateKnightMoves(position Position, genType MoveGenerationType) []Move { moveBB := attackBB & ^occupied for moveBB > 0 { toSquare := Square(moveBB.PopLsb()) - move := NewMove(fromSquare, toSquare, NormalMove, QuietMoveFlag) + move := NewMove(fromSquare, toSquare, QuietMove) moves = append(moves, move) } } @@ -105,7 +110,7 @@ func generateKnightMoves(position Position, genType MoveGenerationType) []Move { capturesBB := attackBB & opponent for capturesBB > 0 { toSquare := Square(capturesBB.PopLsb()) - move := NewMove(fromSquare, toSquare, NormalMove, CaputureMoveFlag) + move := NewMove(fromSquare, toSquare, CaptureMove) moves = append(moves, move) } } @@ -128,7 +133,7 @@ func generateBishopMoves(position Position, pieceBB BitBoard, genType MoveGenera moveBB := attackBB & ^occupied for moveBB > 0 { toSquare := Square(moveBB.PopLsb()) - move := NewMove(fromSquare, toSquare, NormalMove, QuietMoveFlag) + move := NewMove(fromSquare, toSquare, QuietMove) moves = append(moves, move) } } @@ -136,7 +141,7 @@ func generateBishopMoves(position Position, pieceBB BitBoard, genType MoveGenera capturesBB := attackBB & opponent for capturesBB > 0 { toSquare := Square(capturesBB.PopLsb()) - move := NewMove(fromSquare, toSquare, NormalMove, CaputureMoveFlag) + move := NewMove(fromSquare, toSquare, CaptureMove) moves = append(moves, move) } } @@ -159,7 +164,7 @@ func generateRookMoves(position Position, pieceBB BitBoard, genType MoveGenerati moveBB := attacks & ^occupied for moveBB > 0 { toSquare := Square(moveBB.PopLsb()) - move := NewMove(fromSquare, toSquare, NormalMove, QuietMoveFlag) + move := NewMove(fromSquare, toSquare, QuietMove) moves = append(moves, move) } } @@ -167,7 +172,7 @@ func generateRookMoves(position Position, pieceBB BitBoard, genType MoveGenerati capturesBB := attacks & opponent for capturesBB > 0 { toSquare := Square(capturesBB.PopLsb()) - move := NewMove(fromSquare, toSquare, NormalMove, CaputureMoveFlag) + move := NewMove(fromSquare, toSquare, CaptureMove) moves = append(moves, move) } } @@ -201,7 +206,7 @@ func generateKingMoves(position Position, genType MoveGenerationType, includeCas moveBB := attacks & ^occupied for moveBB > 0 { toSquare := Square(moveBB.PopLsb()) - move := NewMove(kingSquare, toSquare, NormalMove, QuietMoveFlag) + move := NewMove(kingSquare, toSquare, QuietMove) moves = append(moves, move) } } @@ -209,29 +214,29 @@ func generateKingMoves(position Position, genType MoveGenerationType, includeCas capturesBB := attacks & opponent for capturesBB > 0 { toSquare := Square(capturesBB.PopLsb()) - move := NewMove(kingSquare, toSquare, NormalMove, CaputureMoveFlag) + move := NewMove(kingSquare, toSquare, CaptureMove) moves = append(moves, move) } if includeCastling { if position.turn == White { if position.HasCastlingRights(WhiteCastleKingside) && position.squaresEmpty([]Square{F1, G1}) { - move := NewMove(kingSquare, kingSquare+Square(east*2), CastleMove, QuietMoveFlag) + move := NewMove(kingSquare, kingSquare+Square(east*2), CastleMove) moves = append(moves, move) } if position.HasCastlingRights(WhiteCastleQueenside) && position.squaresEmpty([]Square{D1, C1, B1}) { - move := NewMove(kingSquare, kingSquare+Square(west*2), CastleMove, QuietMoveFlag) + move := NewMove(kingSquare, kingSquare+Square(west*2), CastleMove) moves = append(moves, move) } } else { if position.HasCastlingRights(BlackCastleKingside) && position.squaresEmpty([]Square{F8, G8}) { - move := NewMove(kingSquare, kingSquare+Square(east*2), CastleMove, QuietMoveFlag) + move := NewMove(kingSquare, kingSquare+Square(east*2), CastleMove) moves = append(moves, move) } if position.HasCastlingRights(BlackCastleQueenside) && position.squaresEmpty([]Square{D8, C8, B8}) { - move := NewMove(kingSquare, kingSquare+Square(west*2), CastleMove, QuietMoveFlag) + move := NewMove(kingSquare, kingSquare+Square(west*2), CastleMove) moves = append(moves, move) } } diff --git a/internal/chess/position.go b/internal/chess/position.go index 7931e50..05b0822 100644 --- a/internal/chess/position.go +++ b/internal/chess/position.go @@ -582,7 +582,7 @@ func (p *Position) MakeMove(move Move) error { p.fiftyMoveClock++ // increment the fifty move clock, this will be cleared later if needed switch move.Type() { - case NormalMove: + case QuietMove: if movingPiece.Type() == Pawn && move.RankDifference() == 2 { opposingSide := p.turn.OpposingSide() @@ -619,35 +619,30 @@ func (p *Position) MakeMove(move Move) error { } } - if move.IsCapture() { - p.clearPiece(to) - - if capturePiece.Type() == Rook { - switch to { - case A1: - p.castlingRights &= ^WhiteCastleQueenside - break - case A8: - p.castlingRights &= ^BlackCastleQueenside - break - case H1: - p.castlingRights &= ^WhiteCastleKingside - break - case H8: - p.castlingRights &= ^BlackCastleKingside - break - } - } - } - p.clearPiece(from) p.setPiece(to, movingPiece) + break + case CaptureMove: + p.clearPiece(to) + p.clearPiece(from) - if move.IsPromotion() { - p.clearPiece(to) // remove the original piece + p.setPiece(to, movingPiece) - // place the newly promoted piece - p.setPiece(to, move.PromotionPiece()) + if capturePiece.Type() == Rook { + switch to { + case A1: + p.castlingRights &= ^WhiteCastleQueenside + break + case A8: + p.castlingRights &= ^BlackCastleQueenside + break + case H1: + p.castlingRights &= ^WhiteCastleKingside + break + case H8: + p.castlingRights &= ^BlackCastleKingside + break + } } break case CastleMove: @@ -696,8 +691,15 @@ func (p *Position) MakeMove(move Move) error { break } + if move.IsPromotion() { + p.clearPiece(to) // remove the original piece + + // place the newly promoted piece + p.setPiece(to, move.PromotionPiece()) + } + // clear the fifty move clock, a pawn has moved or a capture has happened - if movingPiece.Type() == Pawn || capturePiece.Type() != None { + if movingPiece.Type() == Pawn || move.IsCapture() { p.fiftyMoveClock = 0 } @@ -748,30 +750,36 @@ func (p *Position) MakeUciMove(uci string) error { return err } - moveType := NormalMove - - switch uci { - case "e1g1", "e1c1", "e8g8", "e8c8": - moveType = CastleMove - break - } - movingPiece, err := p.GetPieceAt(from) if err != nil { return err } - if movingPiece.Type() == Pawn && to == p.enPassant { + moveType := QuietMove + flags := NoMoveFlag + + if movingPiece.Type() == King { + switch uci { + case "e1g1", "e1c1", "e8g8", "e8c8": + moveType = CastleMove + break + } + } else if movingPiece.Type() == Pawn && to == p.enPassant { moveType = EnPassantMove + } else if movingPiece.Type() == Pawn && FileDistance(to, from) == 0 { // if a pawn is moving and not moving to another file then it's a pawn push + flags |= PawnPushMoveFlag } - flag := QuietMoveFlag capturePiece, _ := p.GetPieceAt(to) if capturePiece != EmptyPiece { - flag = CaputureMoveFlag + moveType = CaptureMove } - move := NewMove(from, to, moveType, flag) + move := NewMove(from, to, moveType) + + if flags != NoMoveFlag { + move.WithFlags(flags) + } if len(uci) > 4 { switch uci[4] { diff --git a/internal/search/negamax.go b/internal/search/negamax.go index a60c9c0..92d9853 100644 --- a/internal/search/negamax.go +++ b/internal/search/negamax.go @@ -213,7 +213,7 @@ func (s *NegamaxSearcher) doSearch(position *chess.Position, alpha int, beta int if score >= beta { nodeType = LowerNode - if !move.HasFlag(chess.CaputureMoveFlag) { + if !move.IsCapture() { turn := position.Turn() length := len(s.killerMoves[turn]) if length >= maxNumberKillerMoves {