304 lines
7.1 KiB
Go
304 lines
7.1 KiB
Go
|
package board
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// CastleRights is a helper for maintaining castling rights.
|
||
|
type CastleRights struct {
|
||
|
WhiteKingSide bool
|
||
|
WhiteQueenSide bool
|
||
|
BlackKingSide bool
|
||
|
BlackQueenSide bool
|
||
|
}
|
||
|
|
||
|
// NewCastleRights returns an initialized CastleRights from the given string.
|
||
|
func NewCastleRights(s string) (*CastleRights, error) {
|
||
|
cr := &CastleRights{}
|
||
|
for _, c := range s {
|
||
|
switch c {
|
||
|
case 'K':
|
||
|
cr.WhiteKingSide = true
|
||
|
case 'Q':
|
||
|
cr.WhiteQueenSide = true
|
||
|
case 'k':
|
||
|
cr.BlackKingSide = true
|
||
|
case 'q':
|
||
|
cr.BlackQueenSide = true
|
||
|
default:
|
||
|
return nil, fmt.Errorf("invalid castle-rights: %s", s)
|
||
|
}
|
||
|
}
|
||
|
return cr, nil
|
||
|
}
|
||
|
|
||
|
func (cr CastleRights) String() string {
|
||
|
var out string
|
||
|
if cr.WhiteKingSide {
|
||
|
out += WhiteKing.String()
|
||
|
}
|
||
|
if cr.WhiteQueenSide {
|
||
|
out += WhiteQueen.String()
|
||
|
}
|
||
|
if cr.BlackKingSide {
|
||
|
out += BlackKing.String()
|
||
|
}
|
||
|
if cr.BlackQueenSide {
|
||
|
out += BlackQueen.String()
|
||
|
}
|
||
|
return out
|
||
|
}
|
||
|
|
||
|
// Board represents a chess board.
|
||
|
type Board struct {
|
||
|
// White
|
||
|
WhiteKing Bitboard
|
||
|
WhiteQueen Bitboard
|
||
|
WhiteBishop Bitboard
|
||
|
WhiteKnight Bitboard
|
||
|
WhiteRook Bitboard
|
||
|
WhitePawn Bitboard
|
||
|
// Black
|
||
|
BlackKing Bitboard
|
||
|
BlackQueen Bitboard
|
||
|
BlackBishop Bitboard
|
||
|
BlackKnight Bitboard
|
||
|
BlackRook Bitboard
|
||
|
BlackPawn Bitboard
|
||
|
// Convenience
|
||
|
WhitePieces Bitboard
|
||
|
BlackPieces Bitboard
|
||
|
FreeSquares Bitboard
|
||
|
|
||
|
Turn Color
|
||
|
CastleRights CastleRights
|
||
|
EnPassent *Square
|
||
|
HalfMove uint64
|
||
|
FullMove uint64
|
||
|
}
|
||
|
|
||
|
// NewBoard returns an initialized *Board.
|
||
|
func NewBoard(pieces map[Square]Piece, turn Color, cr CastleRights, ep *Square, halfMove, fullMove uint64) (*Board, error) {
|
||
|
board := new(Board)
|
||
|
board.WhiteKing = NewBitboard(nil)
|
||
|
board.WhiteQueen = NewBitboard(nil)
|
||
|
board.WhiteBishop = NewBitboard(nil)
|
||
|
board.WhiteKnight = NewBitboard(nil)
|
||
|
board.WhiteRook = NewBitboard(nil)
|
||
|
board.WhitePawn = NewBitboard(nil)
|
||
|
board.BlackKing = NewBitboard(nil)
|
||
|
board.BlackQueen = NewBitboard(nil)
|
||
|
board.BlackBishop = NewBitboard(nil)
|
||
|
board.BlackKnight = NewBitboard(nil)
|
||
|
board.BlackRook = NewBitboard(nil)
|
||
|
board.BlackPawn = NewBitboard(nil)
|
||
|
board.Turn = turn
|
||
|
board.CastleRights = cr
|
||
|
board.EnPassent = ep
|
||
|
board.HalfMove = halfMove
|
||
|
board.FullMove = fullMove
|
||
|
for square, piece := range pieces {
|
||
|
switch piece {
|
||
|
case WhiteKing:
|
||
|
board.WhiteKing = board.WhiteKing | NewBitboard(map[Square]bool{square: true})
|
||
|
case WhiteQueen:
|
||
|
board.WhiteQueen = board.WhiteQueen | NewBitboard(map[Square]bool{square: true})
|
||
|
case WhiteBishop:
|
||
|
board.WhiteBishop = board.WhiteBishop | NewBitboard(map[Square]bool{square: true})
|
||
|
case WhiteKnight:
|
||
|
board.WhiteKnight = board.WhiteKnight | NewBitboard(map[Square]bool{square: true})
|
||
|
case WhiteRook:
|
||
|
board.WhiteRook = board.WhiteRook | NewBitboard(map[Square]bool{square: true})
|
||
|
case WhitePawn:
|
||
|
board.WhitePawn = board.WhitePawn | NewBitboard(map[Square]bool{square: true})
|
||
|
case BlackKing:
|
||
|
board.BlackKing = board.BlackKing | NewBitboard(map[Square]bool{square: true})
|
||
|
case BlackQueen:
|
||
|
board.BlackQueen = board.BlackQueen | NewBitboard(map[Square]bool{square: true})
|
||
|
case BlackBishop:
|
||
|
board.BlackBishop = board.BlackBishop | NewBitboard(map[Square]bool{square: true})
|
||
|
case BlackKnight:
|
||
|
board.BlackKnight = board.BlackKnight | NewBitboard(map[Square]bool{square: true})
|
||
|
case BlackRook:
|
||
|
board.BlackRook = board.BlackRook | NewBitboard(map[Square]bool{square: true})
|
||
|
case BlackPawn:
|
||
|
board.BlackPawn = board.BlackPawn | NewBitboard(map[Square]bool{square: true})
|
||
|
default:
|
||
|
panic("invalid piece")
|
||
|
}
|
||
|
}
|
||
|
board.UpdateConvenienceBitboards()
|
||
|
return board, nil
|
||
|
}
|
||
|
|
||
|
// UpdateConvenienceBitboards updates the convenience bitboards.
|
||
|
func (b *Board) UpdateConvenienceBitboards() {
|
||
|
b.WhitePieces = b.WhiteKing | b.WhiteQueen | b.WhiteBishop | b.WhiteKnight | b.WhiteRook | b.WhitePawn
|
||
|
b.BlackPieces = b.BlackKing | b.BlackQueen | b.BlackBishop | b.BlackKnight | b.BlackRook | b.BlackPawn
|
||
|
b.FreeSquares = ^(b.WhitePieces | b.BlackPieces)
|
||
|
}
|
||
|
|
||
|
// IsOccupied returns true if the given field is occupied.
|
||
|
func (b Board) IsOccupied(sq Square) bool {
|
||
|
return sq.Bitboard()&b.FreeSquares == 0
|
||
|
}
|
||
|
|
||
|
// Piece returns the piece on the given field.
|
||
|
func (b Board) Piece(sq Square) Piece {
|
||
|
contains := func(bb Bitboard, sq Square) bool {
|
||
|
for _, square := range bb.Squares() {
|
||
|
if square == sq {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
if contains(b.WhiteKing, sq) {
|
||
|
return WhiteKing
|
||
|
}
|
||
|
if contains(b.WhiteQueen, sq) {
|
||
|
return WhiteQueen
|
||
|
}
|
||
|
if contains(b.WhiteBishop, sq) {
|
||
|
return WhiteBishop
|
||
|
}
|
||
|
if contains(b.WhiteKnight, sq) {
|
||
|
return WhiteKnight
|
||
|
}
|
||
|
if contains(b.WhiteRook, sq) {
|
||
|
return WhiteRook
|
||
|
}
|
||
|
if contains(b.WhitePawn, sq) {
|
||
|
return WhitePawn
|
||
|
}
|
||
|
if contains(b.BlackKing, sq) {
|
||
|
return BlackKing
|
||
|
}
|
||
|
if contains(b.BlackQueen, sq) {
|
||
|
return BlackQueen
|
||
|
}
|
||
|
if contains(b.BlackBishop, sq) {
|
||
|
return BlackBishop
|
||
|
}
|
||
|
if contains(b.BlackKnight, sq) {
|
||
|
return BlackKnight
|
||
|
}
|
||
|
if contains(b.BlackRook, sq) {
|
||
|
return BlackRook
|
||
|
}
|
||
|
if contains(b.BlackPawn, sq) {
|
||
|
return BlackPawn
|
||
|
}
|
||
|
return NoPiece
|
||
|
}
|
||
|
|
||
|
func (b Board) String() string {
|
||
|
fen := ""
|
||
|
for r := 7; r >= 0; r-- {
|
||
|
for f := 0; f < 8; f++ {
|
||
|
sq := NewSquare(File(f), Rank(r))
|
||
|
p := b.Piece(sq)
|
||
|
if p != NoPiece {
|
||
|
fen += p.String()
|
||
|
} else {
|
||
|
fen += "1"
|
||
|
}
|
||
|
}
|
||
|
if r != 0 {
|
||
|
fen += "/"
|
||
|
}
|
||
|
}
|
||
|
for i := 8; i > 1; i-- {
|
||
|
repeatStr := strings.Repeat("1", i)
|
||
|
countStr := strconv.Itoa(i)
|
||
|
fen = strings.Replace(fen, repeatStr, countStr, -1)
|
||
|
}
|
||
|
ep := "-"
|
||
|
if b.EnPassent != nil {
|
||
|
ep = b.EnPassent.String()
|
||
|
}
|
||
|
fen += fmt.Sprintf(
|
||
|
" %s %s %s %d %d",
|
||
|
b.Turn,
|
||
|
b.CastleRights,
|
||
|
ep,
|
||
|
b.HalfMove,
|
||
|
b.FullMove,
|
||
|
)
|
||
|
return fen
|
||
|
}
|
||
|
|
||
|
// Draw prints an ASCII art of this board to STDOUT.
|
||
|
func (b Board) Draw() string {
|
||
|
s := "\n A B C D E F G H\n"
|
||
|
for r := 7; r >= 0; r-- {
|
||
|
s += Rank(r).String()
|
||
|
s += " "
|
||
|
for f := 0; f < 8; f++ {
|
||
|
p := b.Piece(NewSquare(File(f), Rank(r)))
|
||
|
if p == NoPiece {
|
||
|
s += "."
|
||
|
} else {
|
||
|
s += p.Rune()
|
||
|
}
|
||
|
s += " "
|
||
|
}
|
||
|
s += "\n"
|
||
|
}
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
// UpdatePiece moves the a piece on the board from the given src to dest square.
|
||
|
func (b *Board) UpdatePiece(src, dest Square) error {
|
||
|
if !b.IsOccupied(src) {
|
||
|
return fmt.Errorf("can not move piece on vacant square: %s", src)
|
||
|
}
|
||
|
if b.IsOccupied(dest) {
|
||
|
return fmt.Errorf("can not move to occupied square: %s", dest)
|
||
|
}
|
||
|
switch b.Piece(src) {
|
||
|
case WhiteKing:
|
||
|
b.WhiteKing.Flip(src)
|
||
|
b.WhiteKing.Flip(dest)
|
||
|
case WhiteQueen:
|
||
|
b.WhiteQueen.Flip(src)
|
||
|
b.WhiteQueen.Flip(dest)
|
||
|
case WhiteRook:
|
||
|
b.WhiteRook.Flip(src)
|
||
|
b.WhiteRook.Flip(dest)
|
||
|
case WhiteBishop:
|
||
|
b.WhiteBishop.Flip(src)
|
||
|
b.WhiteBishop.Flip(dest)
|
||
|
case WhiteKnight:
|
||
|
b.WhiteKnight.Flip(src)
|
||
|
b.WhiteKnight.Flip(dest)
|
||
|
case WhitePawn:
|
||
|
b.WhitePawn.Flip(src)
|
||
|
b.WhitePawn.Flip(dest)
|
||
|
case BlackKing:
|
||
|
b.BlackKing.Flip(src)
|
||
|
b.BlackKing.Flip(dest)
|
||
|
case BlackQueen:
|
||
|
b.BlackQueen.Flip(src)
|
||
|
b.BlackQueen.Flip(dest)
|
||
|
case BlackRook:
|
||
|
b.BlackRook.Flip(src)
|
||
|
b.BlackRook.Flip(dest)
|
||
|
case BlackBishop:
|
||
|
b.BlackBishop.Flip(src)
|
||
|
b.BlackBishop.Flip(dest)
|
||
|
case BlackKnight:
|
||
|
b.BlackKnight.Flip(src)
|
||
|
b.BlackKnight.Flip(dest)
|
||
|
case BlackPawn:
|
||
|
b.BlackPawn.Flip(src)
|
||
|
b.BlackPawn.Flip(dest)
|
||
|
default:
|
||
|
panic("")
|
||
|
}
|
||
|
b.UpdateConvenienceBitboards()
|
||
|
return nil
|
||
|
}
|