Skip to content
This repository has been archived by the owner on Dec 29, 2023. It is now read-only.

Commit

Permalink
Store checkers field in position
Browse files Browse the repository at this point in the history
  • Loading branch information
mhib committed Nov 17, 2021
1 parent 8fc28e0 commit dc8d1c6
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 64 deletions.
16 changes: 16 additions & 0 deletions chess/bitboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ var Files_BB = [...]uint64{FileA_BB, FileB_BB, FileC_BB, FileD_BB, FileE_BB, Fil
var KingFlank_BB = [8]uint64{QueenSide_BB ^ FileD_BB, QueenSide_BB, QueenSide_BB,
CenterFiles_BB, CenterFiles_BB, KingSide_BB, KingSide_BB, KingSide_BB ^ FileE_BB}

var Between_BB [64][64]uint64

func File(id int) int {
return id & 7
}
Expand Down Expand Up @@ -403,4 +405,18 @@ func init() {
initBishopMoveBoard(bishopBlockerBoard)
initBishopMagicIndex(bishopBlockerBoard)
initBishopAttacks(bishopBlockerBoard)

for y := 0; y < 64; y++ {
for x := 0; x < 64; x++ {
bishopAttacks := BishopAttacks(y, SquareMask[x])
if bishopAttacks&SquareMask[x] != 0 {
Between_BB[y][x] = bishopAttacks & BishopAttacks(x, SquareMask[y])
}

rookAttacks := RookAttacks(y, SquareMask[x])
if rookAttacks&SquareMask[x] != 0 {
Between_BB[y][x] = rookAttacks & RookAttacks(x, SquareMask[y])
}
}
}
}
2 changes: 2 additions & 0 deletions chess/fen.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ func ParseFen(input string) Position {

HashPosition(&res)

res.Checkers = res.AllSquareAttackers(BitScan(res.Pieces[King]&res.Colours[res.SideToMove]), res.SideToMove^1)

return res
}

Expand Down
116 changes: 69 additions & 47 deletions chess/move_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,70 +26,71 @@ func GenerateQuiet(pos *Position, buffer []EvaledMove) (size uint8) {
theirOccupation := pos.Colours[sideToMove^1]
allOccupation := ourOccupation | theirOccupation
forward := forwardByColor[sideToMove]
target := ^allOccupation
if pos.Checkers != 0 {
target &= Between_BB[BitScan(pos.Pieces[King]&pos.Colours[pos.SideToMove])][BitScan(pos.Checkers)]
if pos.Checkers&(pos.Pieces[Pawn]|pos.Pieces[Knight]) != 0 || MoreThanOne(pos.Checkers) || target == 0 {
goto kings
}
}
for fromBB = pos.Pieces[Pawn] & ourOccupation & ^promotion_BB[sideToMove]; fromBB > 0; fromBB &= (fromBB - 1) {
fromId = BitScan(fromBB)
toId = fromId + forward
toMask = SquareMask[toId]
if allOccupation&toMask == 0 {
buffer[size].Move = NewMove(fromId, toId, Pawn, None, 0)
size++
if target&toMask != 0 {
buffer[size].Move = NewMove(fromId, toId, Pawn, None, 0)
size++
}

// Double pawn push
toId += forward
toMask = SquareMask[toId]
if Rank(fromId) == secondRank[sideToMove] && allOccupation&toMask == 0 {
if Rank(fromId) == secondRank[sideToMove] && target&toMask != 0 {
buffer[size].Move = NewMove(fromId, toId, Pawn, None, NewType(0, 0, 0, 1))
size++
}
}
}

// Castling
if pos.SideToMove == White {
if allOccupation&WhiteKingCastleBlock_BB == 0 && pos.Flags&WhiteKingSideCastleFlag == 0 && !pos.IsSquareAttacked(E1, Black) && !pos.IsSquareAttacked(F1, Black) {
buffer[size].Move = WhiteKingSideCastle
size++
}
if allOccupation&WhiteQueenCastleBlock_BB == 0 && pos.Flags&WhiteQueenSideCastleFlag == 0 && !pos.IsSquareAttacked(E1, Black) && !pos.IsSquareAttacked(D1, Black) {
buffer[size].Move = WhiteQueenSideCastle
size++
}
} else {
if allOccupation&BlackKingCastleBlock_BB == 0 && pos.Flags&BlackKingSideCastleFlag == 0 && !pos.IsSquareAttacked(E8, White) && !pos.IsSquareAttacked(F8, White) {
buffer[size].Move = BlackKingSideCastle
size++
}
if allOccupation&BlackQueenCastleBlock_BB == 0 && pos.Flags&BlackQueenSideCastleFlag == 0 && !pos.IsSquareAttacked(E8, White) && !pos.IsSquareAttacked(D8, White) {
buffer[size].Move = BlackQueenSideCastle
size++
if pos.Checkers == 0 {
// Castling
if pos.SideToMove == White {
if allOccupation&WhiteKingCastleBlock_BB == 0 && pos.Flags&WhiteKingSideCastleFlag == 0 && !pos.IsSquareAttacked(E1, Black) && !pos.IsSquareAttacked(F1, Black) {
buffer[size].Move = WhiteKingSideCastle
size++
}
if allOccupation&WhiteQueenCastleBlock_BB == 0 && pos.Flags&WhiteQueenSideCastleFlag == 0 && !pos.IsSquareAttacked(E1, Black) && !pos.IsSquareAttacked(D1, Black) {
buffer[size].Move = WhiteQueenSideCastle
size++
}
} else {
if allOccupation&BlackKingCastleBlock_BB == 0 && pos.Flags&BlackKingSideCastleFlag == 0 && !pos.IsSquareAttacked(E8, White) && !pos.IsSquareAttacked(F8, White) {
buffer[size].Move = BlackKingSideCastle
size++
}
if allOccupation&BlackQueenCastleBlock_BB == 0 && pos.Flags&BlackQueenSideCastleFlag == 0 && !pos.IsSquareAttacked(E8, White) && !pos.IsSquareAttacked(D8, White) {
buffer[size].Move = BlackQueenSideCastle
size++
}
}

}

// Knights
for fromBB = pos.Pieces[Knight] & ourOccupation; fromBB != 0; fromBB &= (fromBB - 1) {
fromId = BitScan(fromBB)
for toBB = KnightAttacks[fromId] & ^allOccupation; toBB != 0; toBB &= (toBB - 1) {
for toBB = KnightAttacks[fromId] & target; toBB != 0; toBB &= (toBB - 1) {
toId = BitScan(toBB)
buffer[size].Move = NewMove(fromId, toId, Knight, None, NewType(0, 0, 0, 0))
size++
}
}
// end of knights

// Kings
fromId = BitScan(pos.Pieces[King] & ourOccupation)
for toBB = KingAttacks[fromId] & ^allOccupation; toBB != 0; toBB &= (toBB - 1) {
toId = BitScan(toBB)
buffer[size].Move = NewMove(fromId, toId, King, None, NewType(0, 0, 0, 0))
size++
}
// end of Kings

// Rooks
for fromBB = pos.Pieces[Rook] & ourOccupation; fromBB != 0; fromBB &= (fromBB - 1) {
fromId = BitScan(fromBB)
for toBB = RookAttacks(fromId, allOccupation) & ^allOccupation; toBB != 0; toBB &= (toBB - 1) {
for toBB = RookAttacks(fromId, allOccupation) & target; toBB != 0; toBB &= (toBB - 1) {
toId = BitScan(toBB)
buffer[size].Move = NewMove(fromId, toId, Rook, None, NewType(0, 0, 0, 0))
size++
Expand All @@ -100,7 +101,7 @@ func GenerateQuiet(pos *Position, buffer []EvaledMove) (size uint8) {
// Bishops
for fromBB = pos.Pieces[Bishop] & ourOccupation; fromBB != 0; fromBB &= (fromBB - 1) {
fromId = BitScan(fromBB)
for toBB = BishopAttacks(fromId, allOccupation) & ^allOccupation; toBB != 0; toBB &= (toBB - 1) {
for toBB = BishopAttacks(fromId, allOccupation) & target; toBB != 0; toBB &= (toBB - 1) {
toId = BitScan(toBB)
buffer[size].Move = NewMove(fromId, toId, Bishop, None, NewType(0, 0, 0, 0))
size++
Expand All @@ -111,29 +112,49 @@ func GenerateQuiet(pos *Position, buffer []EvaledMove) (size uint8) {
// Queens
for fromBB = pos.Pieces[Queen] & ourOccupation; fromBB != 0; fromBB &= (fromBB - 1) {
fromId = BitScan(fromBB)
for toBB = QueenAttacks(fromId, allOccupation) & ^allOccupation; toBB != 0; toBB &= (toBB - 1) {
for toBB = QueenAttacks(fromId, allOccupation) & target; toBB != 0; toBB &= (toBB - 1) {
toId = BitScan(toBB)
buffer[size].Move = NewMove(fromId, toId, Queen, None, NewType(0, 0, 0, 0))
size++
}
}
// end of Queens

// Kings
kings:
fromId = BitScan(pos.Pieces[King] & ourOccupation)
for toBB = KingAttacks[fromId] & ^allOccupation; toBB != 0; toBB &= (toBB - 1) {
toId = BitScan(toBB)
buffer[size].Move = NewMove(fromId, toId, King, None, NewType(0, 0, 0, 0))
size++
}
// end of Kings

return
}

func GenerateNoisy(pos *Position, buffer []EvaledMove) (size uint8) {
var fromBB, toBB uint64
var fromId, toId, what int
var fromId, toId, what, forward int

sideToMove := pos.SideToMove
ourOccupation := pos.Colours[sideToMove]
theirOccupation := pos.Colours[sideToMove^1]
allOccupation := ourOccupation | theirOccupation

target := theirOccupation
var between uint64
if pos.Checkers != 0 {
target = pos.Checkers
between = Between_BB[BitScan(pos.Pieces[King]&pos.Colours[pos.SideToMove])][BitScan(pos.Checkers)]
if MoreThanOne(pos.Checkers) {
goto kings
}
}

// PAWNS
forward := forwardByColor[sideToMove]
if pos.EpSquare != 0 {
forward = forwardByColor[sideToMove]
if pos.EpSquare != 0 && ((SquareMask[pos.EpSquare] | pos.Checkers) == SquareMask[pos.EpSquare]) {
fromBB = (SquareMask[uint(pos.EpSquare)-1] | SquareMask[uint(pos.EpSquare)+1]) &
epRank_BB[sideToMove] & pos.Pieces[Pawn] & ourOccupation
for ; fromBB > 0; fromBB &= (fromBB - 1) {
Expand All @@ -143,26 +164,26 @@ func GenerateNoisy(pos *Position, buffer []EvaledMove) (size uint8) {
}
}
if sideToMove == White {
fromBB = BlackPawnsAttacks(theirOccupation) | Rank7_BB
fromBB = BlackPawnsAttacks(target) | Rank7_BB
} else {
fromBB = WhitePawnsAttacks(theirOccupation) | Rank2_BB
fromBB = WhitePawnsAttacks(target) | Rank2_BB
}
for fromBB &= pos.Pieces[Pawn] & ourOccupation; fromBB != 0; fromBB &= fromBB - 1 {
fromId = BitScan(fromBB)
if Rank(fromId) == secondRank[sideToMove^1] {
if SquareMask[fromId+forward]&allOccupation == 0 {
if SquareMask[fromId+forward]&allOccupation == 0 && (pos.Checkers == 0 || between&SquareMask[fromId+forward] != 0) {
toId = fromId + forward
addPromotions(NewMove(fromId, toId, Pawn, None, 0), buffer[size:])
size += 4
}
for toBB = PawnAttacks[sideToMove][fromId] & theirOccupation; toBB > 0; toBB &= (toBB - 1) {
for toBB = PawnAttacks[sideToMove][fromId] & target; toBB > 0; toBB &= (toBB - 1) {
toId = BitScan(toBB)
what = pos.TypeOnSquare(SquareMask[uint(toId)])
addPromotions(NewMove(fromId, toId, Pawn, what, 1), buffer[size:])
size += 4
}
} else {
for toBB = PawnAttacks[sideToMove][fromId] & theirOccupation; toBB > 0; toBB &= (toBB - 1) {
for toBB = PawnAttacks[sideToMove][fromId] & target; toBB > 0; toBB &= (toBB - 1) {
toId = BitScan(toBB)
what = pos.TypeOnSquare(SquareMask[uint(toId)])
buffer[size].Move = NewMove(fromId, toId, Pawn, what, NewType(1, 0, 0, 0))
Expand All @@ -175,7 +196,7 @@ func GenerateNoisy(pos *Position, buffer []EvaledMove) (size uint8) {
// Knights
for fromBB = pos.Pieces[Knight] & ourOccupation; fromBB != 0; fromBB &= (fromBB - 1) {
fromId = BitScan(fromBB)
for toBB = KnightAttacks[fromId] & theirOccupation; toBB != 0; toBB &= (toBB - 1) {
for toBB = KnightAttacks[fromId] & target; toBB != 0; toBB &= (toBB - 1) {
toId = BitScan(toBB)
what = pos.TypeOnSquare(SquareMask[uint(toId)])
buffer[size].Move = NewMove(fromId, toId, Knight, what, NewType(1, 0, 0, 0))
Expand All @@ -187,7 +208,7 @@ func GenerateNoisy(pos *Position, buffer []EvaledMove) (size uint8) {
// Bishops
for fromBB = pos.Pieces[Bishop] & ourOccupation; fromBB != 0; fromBB &= (fromBB - 1) {
fromId = BitScan(fromBB)
for toBB = BishopAttacks(fromId, allOccupation) & theirOccupation; toBB != 0; toBB &= (toBB - 1) {
for toBB = BishopAttacks(fromId, allOccupation) & target; toBB != 0; toBB &= (toBB - 1) {
toId = BitScan(toBB)
what = pos.TypeOnSquare(SquareMask[uint(toId)])
buffer[size].Move = NewMove(fromId, toId, Bishop, what, NewType(1, 0, 0, 0))
Expand All @@ -199,7 +220,7 @@ func GenerateNoisy(pos *Position, buffer []EvaledMove) (size uint8) {
// Rooks
for fromBB = pos.Pieces[Rook] & ourOccupation; fromBB != 0; fromBB &= (fromBB - 1) {
fromId = BitScan(fromBB)
for toBB = RookAttacks(fromId, allOccupation) & theirOccupation; toBB != 0; toBB &= (toBB - 1) {
for toBB = RookAttacks(fromId, allOccupation) & target; toBB != 0; toBB &= (toBB - 1) {
toId = BitScan(toBB)
what = pos.TypeOnSquare(SquareMask[uint(toId)])
buffer[size].Move = NewMove(fromId, toId, Rook, what, NewType(1, 0, 0, 0))
Expand All @@ -211,7 +232,7 @@ func GenerateNoisy(pos *Position, buffer []EvaledMove) (size uint8) {
// Queens
for fromBB = pos.Pieces[Queen] & ourOccupation; fromBB != 0; fromBB &= (fromBB - 1) {
fromId = BitScan(fromBB)
for toBB = QueenAttacks(fromId, allOccupation) & theirOccupation; toBB != 0; toBB &= (toBB - 1) {
for toBB = QueenAttacks(fromId, allOccupation) & target; toBB != 0; toBB &= (toBB - 1) {
toId = BitScan(toBB)
what = pos.TypeOnSquare(SquareMask[uint(toId)])
buffer[size].Move = NewMove(fromId, toId, Queen, what, NewType(1, 0, 0, 0))
Expand All @@ -221,6 +242,7 @@ func GenerateNoisy(pos *Position, buffer []EvaledMove) (size uint8) {
// end of Queens

// Kings
kings:
fromBB = pos.Pieces[King] & ourOccupation
fromId = BitScan(fromBB)
for toBB = KingAttacks[fromId] & theirOccupation; toBB != 0; toBB &= (toBB - 1) {
Expand Down
5 changes: 5 additions & 0 deletions chess/perft_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ func TestPerft(t *testing.T) {
depth: 5,
nodes: 164075551,
},
{
fen: "8/8/b2N4/8/7p/5P1P/3pr1P1/R3k1K1 b - - 1 1",
depth: 6,
nodes: 14304486,
},
}
for i, test := range tests {
var p = ParseFen(test.fen)
Expand Down
14 changes: 13 additions & 1 deletion chess/position.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type Position struct {
Pieces [King + 1]uint64
Key uint64
PawnKey uint64
Checkers uint64
SideToMove int
EpSquare int
FiftyMove int
Expand Down Expand Up @@ -103,6 +104,7 @@ func (pos *Position) MakeNullMove(res *Position) {
res.Pieces[King] = pos.Pieces[King]
res.SideToMove = pos.SideToMove ^ 1
res.Flags = pos.Flags
res.Checkers = 0
res.Key = pos.Key ^ zobristColor ^ zobristEpSquare[pos.EpSquare]
res.PawnKey = pos.PawnKey

Expand Down Expand Up @@ -172,12 +174,22 @@ func (pos *Position) MakeMove(move Move, res *Position) bool {

res.Key ^= zobristFlags[res.Flags]
res.SideToMove = pos.SideToMove ^ 1
res.Checkers = res.AllSquareAttackers(BitScan(res.Colours[res.SideToMove]&res.Pieces[King]), res.SideToMove^1)
res.LastMove = move
return true
}

func (pos *Position) IsInCheck() bool {
return pos.IsSquareAttacked(BitScan(pos.Colours[pos.SideToMove]&pos.Pieces[King]), pos.SideToMove^1)
return pos.Checkers != 0
}

func (pos *Position) AllSquareAttackers(square, side int) uint64 {
theirOccupation := pos.Colours[side]
return (PawnAttacks[side^1][square] & pos.Pieces[Pawn] & theirOccupation) |
(KnightAttacks[square] & theirOccupation & pos.Pieces[Knight]) |
(KingAttacks[square] & pos.Pieces[King] & theirOccupation) |
(BishopAttacks(square, pos.Colours[Black]|pos.Colours[White]) & (pos.Pieces[Bishop] | pos.Pieces[Queen]) & theirOccupation) |
(RookAttacks(square, pos.Colours[Black]|pos.Colours[White]) & (pos.Pieces[Queen] | pos.Pieces[Rook]) & theirOccupation)
}

func (pos *Position) IsSquareAttacked(square, side int) bool {
Expand Down
Loading

0 comments on commit dc8d1c6

Please sign in to comment.