✨ Basic implementation of bitboard, board and move
This commit is contained in:
parent
07adc3474a
commit
c21a117a23
7 changed files with 988 additions and 0 deletions
82
pkg/board/bitboard.go
Normal file
82
pkg/board/bitboard.go
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
package board
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bitboard is defined as
|
||||||
|
type Bitboard uint64
|
||||||
|
|
||||||
|
// NewBitboard returns an initialized Bitboard.
|
||||||
|
func NewBitboard(m map[Square]bool) Bitboard {
|
||||||
|
s := ""
|
||||||
|
for sq := 0; sq < 64; sq++ {
|
||||||
|
if m[Square(sq)] {
|
||||||
|
s += "1"
|
||||||
|
} else {
|
||||||
|
s += "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bb, err := strconv.ParseUint(s, 2, 64)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return Bitboard(bb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map returns a map[Square]bool representation of this Bitboard.
|
||||||
|
func (bb Bitboard) Map() map[Square]bool {
|
||||||
|
m := map[Square]bool{}
|
||||||
|
for sq := 0; sq < 64; sq++ {
|
||||||
|
if bb&Square(sq).Bitboard() > 0 {
|
||||||
|
m[Square(sq)] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Squares returns a []Square of all bits in this Bitboard.
|
||||||
|
func (bb Bitboard) Squares() []Square {
|
||||||
|
squares := []Square{}
|
||||||
|
for sq := 0; sq < 64; sq++ {
|
||||||
|
if bb&Square(sq).Bitboard() > 0 {
|
||||||
|
squares = append(squares, Square(sq))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return squares
|
||||||
|
}
|
||||||
|
|
||||||
|
// Occupied returns true if the given Square is occupied.
|
||||||
|
func (bb Bitboard) Occupied(sq Square) bool {
|
||||||
|
return (uint64(bb) >> uint64(63-sq) & 1) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw returns visual representation of the bitboard useful for debugging.
|
||||||
|
func (bb Bitboard) 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++ {
|
||||||
|
sq := NewSquare(File(f), Rank(r))
|
||||||
|
if bb.Occupied(sq) {
|
||||||
|
s += "1"
|
||||||
|
} else {
|
||||||
|
s += "0"
|
||||||
|
}
|
||||||
|
s += " "
|
||||||
|
}
|
||||||
|
s += "\n"
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flip toggles the bit at the given square.
|
||||||
|
func (bb *Bitboard) Flip(sq Square) {
|
||||||
|
if !sq.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
oldBB := *bb
|
||||||
|
newBB := oldBB ^ sq.Bitboard()
|
||||||
|
*bb = newBB
|
||||||
|
}
|
303
pkg/board/board.go
Normal file
303
pkg/board/board.go
Normal file
|
@ -0,0 +1,303 @@
|
||||||
|
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
|
||||||
|
}
|
73
pkg/board/move.go
Normal file
73
pkg/board/move.go
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package board
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MoveProp is defined as
|
||||||
|
// <piece><disambiguity><captures><square><promote><check|mate>
|
||||||
|
type MoveProp uint16
|
||||||
|
|
||||||
|
// Available MoveProps
|
||||||
|
const (
|
||||||
|
QueenSideCastle MoveProp = 1 << iota
|
||||||
|
KingSideCastle
|
||||||
|
Capture
|
||||||
|
EnPassant
|
||||||
|
Check
|
||||||
|
Mate
|
||||||
|
inCheck
|
||||||
|
)
|
||||||
|
|
||||||
|
// Move is defined as
|
||||||
|
type Move struct {
|
||||||
|
Piece PieceType
|
||||||
|
FromRank *Rank
|
||||||
|
FromFile *File
|
||||||
|
To Square
|
||||||
|
PromoteTo PieceType
|
||||||
|
Props MoveProp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Move) String() string {
|
||||||
|
out := ""
|
||||||
|
if m.HasProp(KingSideCastle) {
|
||||||
|
out += "O-O"
|
||||||
|
} else if m.HasProp(QueenSideCastle) {
|
||||||
|
out += "O-O-O"
|
||||||
|
} else {
|
||||||
|
if m.Piece != NoPieceType && m.Piece != Pawn {
|
||||||
|
out += strings.ToUpper(m.Piece.String())
|
||||||
|
}
|
||||||
|
if m.FromFile != nil {
|
||||||
|
out += m.FromFile.String()
|
||||||
|
}
|
||||||
|
if m.FromRank != nil {
|
||||||
|
out += m.FromRank.String()
|
||||||
|
}
|
||||||
|
if m.HasProp(Capture) {
|
||||||
|
out += "x"
|
||||||
|
}
|
||||||
|
out += m.To.String()
|
||||||
|
if m.PromoteTo != NoPieceType {
|
||||||
|
out += fmt.Sprintf("=%s", m.PromoteTo.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m.HasProp(Check) {
|
||||||
|
out += "+"
|
||||||
|
} else if m.HasProp(Mate) {
|
||||||
|
out += "#"
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddProp adds the given MoveProp to this move.
|
||||||
|
func (m *Move) AddProp(prop MoveProp) {
|
||||||
|
m.Props = m.Props | prop
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasProp returns true if this move has the given MoveProp.
|
||||||
|
func (m Move) HasProp(prop MoveProp) bool {
|
||||||
|
return m.Props&prop > 0
|
||||||
|
}
|
205
pkg/board/pieces.go
Normal file
205
pkg/board/pieces.go
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
package board
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.c-base.org/gochess/libchess/pkg/runes"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PieceType defines the type of a piece
|
||||||
|
type PieceType uint8
|
||||||
|
|
||||||
|
// The following PieceTypes exist
|
||||||
|
const (
|
||||||
|
NoPieceType PieceType = iota
|
||||||
|
King
|
||||||
|
Queen
|
||||||
|
Rook
|
||||||
|
Bishop
|
||||||
|
Knight
|
||||||
|
Pawn
|
||||||
|
)
|
||||||
|
|
||||||
|
func (pt PieceType) String() string {
|
||||||
|
switch pt {
|
||||||
|
case King:
|
||||||
|
return "k"
|
||||||
|
case Queen:
|
||||||
|
return "q"
|
||||||
|
case Rook:
|
||||||
|
return "r"
|
||||||
|
case Bishop:
|
||||||
|
return "b"
|
||||||
|
case Knight:
|
||||||
|
return "n"
|
||||||
|
case Pawn:
|
||||||
|
return "p"
|
||||||
|
default:
|
||||||
|
return "-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PromotableTo returns true if a pawn can be promoted to the given PieceType.
|
||||||
|
func (pt PieceType) PromotableTo() bool {
|
||||||
|
switch pt {
|
||||||
|
case Queen, Rook, Bishop, Knight:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// PieceTypes returns a slice of all piece types.
|
||||||
|
func PieceTypes() [6]PieceType {
|
||||||
|
return [6]PieceType{King, Queen, Rook, Bishop, Knight, Pawn}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Piece is defined as
|
||||||
|
type Piece uint8
|
||||||
|
|
||||||
|
// The following Pieces exist:
|
||||||
|
const (
|
||||||
|
NoPiece Piece = iota
|
||||||
|
// White
|
||||||
|
WhiteKing
|
||||||
|
WhiteQueen
|
||||||
|
WhiteRook
|
||||||
|
WhiteBishop
|
||||||
|
WhiteKnight
|
||||||
|
WhitePawn
|
||||||
|
// Black
|
||||||
|
BlackKing
|
||||||
|
BlackQueen
|
||||||
|
BlackRook
|
||||||
|
BlackBishop
|
||||||
|
BlackKnight
|
||||||
|
BlackPawn
|
||||||
|
)
|
||||||
|
|
||||||
|
// AllPieces returns a []Piece containing with all available Pieces.
|
||||||
|
func AllPieces() []Piece {
|
||||||
|
return []Piece{
|
||||||
|
WhiteKing, WhiteQueen, WhiteRook, WhiteBishop, WhiteKnight, WhitePawn,
|
||||||
|
BlackKing, BlackQueen, BlackRook, BlackBishop, BlackKnight, BlackPawn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPiece returns an initialized Piece based on the given PieceType and Color.
|
||||||
|
func NewPiece(t PieceType, c Color) Piece {
|
||||||
|
for _, p := range AllPieces() {
|
||||||
|
if p.Color() == c && p.Type() == t {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NoPiece
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPieceByFEN returns an initialized piece based on the given FEN letter representation.
|
||||||
|
func NewPieceByFEN(c rune) Piece {
|
||||||
|
switch c {
|
||||||
|
case 'K':
|
||||||
|
return WhiteKing
|
||||||
|
case 'Q':
|
||||||
|
return WhiteQueen
|
||||||
|
case 'R':
|
||||||
|
return WhiteRook
|
||||||
|
case 'B':
|
||||||
|
return WhiteBishop
|
||||||
|
case 'N':
|
||||||
|
return WhiteKnight
|
||||||
|
case 'P':
|
||||||
|
return WhitePawn
|
||||||
|
case 'k':
|
||||||
|
return BlackKing
|
||||||
|
case 'q':
|
||||||
|
return BlackQueen
|
||||||
|
case 'r':
|
||||||
|
return BlackRook
|
||||||
|
case 'b':
|
||||||
|
return BlackBishop
|
||||||
|
case 'n':
|
||||||
|
return BlackKnight
|
||||||
|
case 'p':
|
||||||
|
return BlackPawn
|
||||||
|
case '1':
|
||||||
|
return NoPiece
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unable to parse piece: %s", string(c)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Piece) String() string {
|
||||||
|
s := p.Type().String()
|
||||||
|
if p.Color() == White {
|
||||||
|
s = strings.ToUpper(s)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rune returns the UTF-8 char for this piece.
|
||||||
|
func (p Piece) Rune() string {
|
||||||
|
var r rune
|
||||||
|
switch p.Color() {
|
||||||
|
case White:
|
||||||
|
switch p.Type() {
|
||||||
|
case King:
|
||||||
|
r = runes.WhiteKing
|
||||||
|
case Queen:
|
||||||
|
r = runes.WhiteQueen
|
||||||
|
case Rook:
|
||||||
|
r = runes.WhiteRook
|
||||||
|
case Bishop:
|
||||||
|
r = runes.WhiteBishop
|
||||||
|
case Knight:
|
||||||
|
r = runes.WhiteKnight
|
||||||
|
case Pawn:
|
||||||
|
r = runes.WhitePawn
|
||||||
|
}
|
||||||
|
case Black:
|
||||||
|
switch p.Type() {
|
||||||
|
case King:
|
||||||
|
r = runes.BlackKing
|
||||||
|
case Queen:
|
||||||
|
r = runes.BackQueen
|
||||||
|
case Rook:
|
||||||
|
r = runes.BlackRook
|
||||||
|
case Bishop:
|
||||||
|
r = runes.BlackBishop
|
||||||
|
case Knight:
|
||||||
|
r = runes.BlackKnight
|
||||||
|
case Pawn:
|
||||||
|
r = runes.BlackPawn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns the type of the piece.
|
||||||
|
func (p Piece) Type() PieceType {
|
||||||
|
switch p {
|
||||||
|
case WhiteKing, BlackKing:
|
||||||
|
return King
|
||||||
|
case WhiteQueen, BlackQueen:
|
||||||
|
return Queen
|
||||||
|
case WhiteRook, BlackRook:
|
||||||
|
return Rook
|
||||||
|
case WhiteBishop, BlackBishop:
|
||||||
|
return Bishop
|
||||||
|
case WhiteKnight, BlackKnight:
|
||||||
|
return Knight
|
||||||
|
case WhitePawn, BlackPawn:
|
||||||
|
return Pawn
|
||||||
|
}
|
||||||
|
return NoPieceType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color returns the color of the piece.
|
||||||
|
func (p Piece) Color() Color {
|
||||||
|
switch p {
|
||||||
|
case WhiteKing, WhiteQueen, WhiteRook, WhiteBishop, WhiteKnight, WhitePawn:
|
||||||
|
return White
|
||||||
|
case BlackKing, BlackQueen, BlackRook, BlackBishop, BlackKnight, BlackPawn:
|
||||||
|
return Black
|
||||||
|
}
|
||||||
|
return NoColor
|
||||||
|
}
|
268
pkg/board/squares.go
Normal file
268
pkg/board/squares.go
Normal file
|
@ -0,0 +1,268 @@
|
||||||
|
package board
|
||||||
|
|
||||||
|
// File represents a file of squares on the chess board.
|
||||||
|
type File int8
|
||||||
|
|
||||||
|
// The following files exist:
|
||||||
|
const (
|
||||||
|
FileA File = iota
|
||||||
|
FileB
|
||||||
|
FileC
|
||||||
|
FileD
|
||||||
|
FileE
|
||||||
|
FileF
|
||||||
|
FileG
|
||||||
|
FileH
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileChars contains the available file letters a-h.
|
||||||
|
const FileChars = "abcdefgh"
|
||||||
|
|
||||||
|
func (f File) String() string {
|
||||||
|
return FileChars[f : f+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next File.
|
||||||
|
func (f File) Next() File {
|
||||||
|
return File(f + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Previous returns the previous File.
|
||||||
|
func (f File) Previous() File {
|
||||||
|
return File(f - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift returns the theoretical File after shifting by the given int.
|
||||||
|
func (f File) Shift(i int) File {
|
||||||
|
return File(int(f) + i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid returns true if this file is valid.
|
||||||
|
func (f File) IsValid() bool {
|
||||||
|
return FileA <= f && f <= FileH
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rank represents a rank of squares on the chess board.
|
||||||
|
type Rank int8
|
||||||
|
|
||||||
|
// The following ranks exist:
|
||||||
|
const (
|
||||||
|
Rank1 Rank = iota
|
||||||
|
Rank2
|
||||||
|
Rank3
|
||||||
|
Rank4
|
||||||
|
Rank5
|
||||||
|
Rank6
|
||||||
|
Rank7
|
||||||
|
Rank8
|
||||||
|
)
|
||||||
|
|
||||||
|
// RankChars contains all rank chars
|
||||||
|
const RankChars = "12345678"
|
||||||
|
|
||||||
|
func (r Rank) String() string {
|
||||||
|
return RankChars[r : r+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next Rank.
|
||||||
|
func (r Rank) Next() Rank {
|
||||||
|
return Rank(r + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Previous returns the previous Rank.
|
||||||
|
func (r Rank) Previous() Rank {
|
||||||
|
return Rank(r - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift returns the theoretical Rank after shifting the given int.
|
||||||
|
func (r Rank) Shift(i int) Rank {
|
||||||
|
return Rank(int(r) + i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid returns true if this is a valid rank.
|
||||||
|
func (r Rank) IsValid() bool {
|
||||||
|
return Rank1 <= r && r <= Rank8
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color is defined as
|
||||||
|
type Color uint8
|
||||||
|
|
||||||
|
// Two colors exist
|
||||||
|
const (
|
||||||
|
NoColor Color = iota
|
||||||
|
White
|
||||||
|
Black
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c Color) String() string {
|
||||||
|
switch c {
|
||||||
|
case White:
|
||||||
|
return "w"
|
||||||
|
case Black:
|
||||||
|
return "b"
|
||||||
|
}
|
||||||
|
return "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the color.
|
||||||
|
func (c Color) Name() string {
|
||||||
|
switch c {
|
||||||
|
case White:
|
||||||
|
return "White"
|
||||||
|
case Black:
|
||||||
|
return "Black"
|
||||||
|
}
|
||||||
|
return "NoColor"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other returns the opposite color.
|
||||||
|
func (c Color) Other() Color {
|
||||||
|
switch c {
|
||||||
|
case White:
|
||||||
|
return Black
|
||||||
|
case Black:
|
||||||
|
return White
|
||||||
|
}
|
||||||
|
return NoColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Side is defined as
|
||||||
|
type Side uint8
|
||||||
|
|
||||||
|
// The sides of the board are .
|
||||||
|
const (
|
||||||
|
QueenSide Side = iota
|
||||||
|
KingSide
|
||||||
|
)
|
||||||
|
|
||||||
|
// Square represents a square on the chess board.
|
||||||
|
type Square int8
|
||||||
|
|
||||||
|
// The following Squares exist
|
||||||
|
const (
|
||||||
|
NoSquare Square = iota - 1
|
||||||
|
A1
|
||||||
|
B1
|
||||||
|
C1
|
||||||
|
D1
|
||||||
|
E1
|
||||||
|
F1
|
||||||
|
G1
|
||||||
|
H1
|
||||||
|
A2
|
||||||
|
B2
|
||||||
|
C2
|
||||||
|
D2
|
||||||
|
E2
|
||||||
|
F2
|
||||||
|
G2
|
||||||
|
H2
|
||||||
|
A3
|
||||||
|
B3
|
||||||
|
C3
|
||||||
|
D3
|
||||||
|
E3
|
||||||
|
F3
|
||||||
|
G3
|
||||||
|
H3
|
||||||
|
A4
|
||||||
|
B4
|
||||||
|
C4
|
||||||
|
D4
|
||||||
|
E4
|
||||||
|
F4
|
||||||
|
G4
|
||||||
|
H4
|
||||||
|
A5
|
||||||
|
B5
|
||||||
|
C5
|
||||||
|
D5
|
||||||
|
E5
|
||||||
|
F5
|
||||||
|
G5
|
||||||
|
H5
|
||||||
|
A6
|
||||||
|
B6
|
||||||
|
C6
|
||||||
|
D6
|
||||||
|
E6
|
||||||
|
F6
|
||||||
|
G6
|
||||||
|
H6
|
||||||
|
A7
|
||||||
|
B7
|
||||||
|
C7
|
||||||
|
D7
|
||||||
|
E7
|
||||||
|
F7
|
||||||
|
G7
|
||||||
|
H7
|
||||||
|
A8
|
||||||
|
B8
|
||||||
|
C8
|
||||||
|
D8
|
||||||
|
E8
|
||||||
|
F8
|
||||||
|
G8
|
||||||
|
H8
|
||||||
|
)
|
||||||
|
|
||||||
|
// StrToSquareMap maps all Squares to the string representation.
|
||||||
|
var StrToSquareMap = map[string]Square{
|
||||||
|
"a1": A1, "a2": A2, "a3": A3, "a4": A4, "a5": A5, "a6": A6, "a7": A7, "a8": A8,
|
||||||
|
"b1": B1, "b2": B2, "b3": B3, "b4": B4, "b5": B5, "b6": B6, "b7": B7, "b8": B8,
|
||||||
|
"c1": C1, "c2": C2, "c3": C3, "c4": C4, "c5": C5, "c6": C6, "c7": C7, "c8": C8,
|
||||||
|
"d1": D1, "d2": D2, "d3": D3, "d4": D4, "d5": D5, "d6": D6, "d7": D7, "d8": D8,
|
||||||
|
"e1": E1, "e2": E2, "e3": E3, "e4": E4, "e5": E5, "e6": E6, "e7": E7, "e8": E8,
|
||||||
|
"f1": F1, "f2": F2, "f3": F3, "f4": F4, "f5": F5, "f6": F6, "f7": F7, "f8": F8,
|
||||||
|
"g1": G1, "g2": G2, "g3": G3, "g4": G4, "g5": G5, "g6": G6, "g7": G7, "g8": G8,
|
||||||
|
"h1": H1, "h2": H2, "h3": H3, "h4": H4, "h5": H5, "h6": H6, "h7": H7, "h8": H8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSquare returns an initialized Square.
|
||||||
|
func NewSquare(f File, r Rank) Square {
|
||||||
|
return Square((int(r) * 8) + int(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color returns the color of this Square.
|
||||||
|
func (sq Square) Color() Color {
|
||||||
|
if ((sq / 8) % 2) == (sq % 2) {
|
||||||
|
return Black
|
||||||
|
}
|
||||||
|
return White
|
||||||
|
}
|
||||||
|
|
||||||
|
// File returns the File this Square is in.
|
||||||
|
func (sq Square) File() File {
|
||||||
|
return File(uint(sq) % 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rank returns the Rank the Square is in.
|
||||||
|
func (sq Square) Rank() Rank {
|
||||||
|
return Rank(uint(sq) / 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Side returns the side of board this square is on.
|
||||||
|
func (sq Square) Side() Side {
|
||||||
|
if sq.File() <= FileA && sq.File() <= FileD {
|
||||||
|
return QueenSide
|
||||||
|
}
|
||||||
|
return KingSide
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sq Square) String() string {
|
||||||
|
if sq == NoSquare {
|
||||||
|
return "-"
|
||||||
|
}
|
||||||
|
return sq.File().String() + sq.Rank().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid returns true if this is a valid square.
|
||||||
|
func (sq Square) IsValid() bool {
|
||||||
|
return int(A1) <= int(sq) && int(sq) <= int(H8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bitboard returns the Bitboard for this square.
|
||||||
|
func (sq Square) Bitboard() Bitboard {
|
||||||
|
return Bitboard(uint64(1) << (uint8(63) - uint8(sq)))
|
||||||
|
}
|
38
pkg/game/game.go
Normal file
38
pkg/game/game.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.c-base.org/gochess/libchess/pkg/board"
|
||||||
|
"code.c-base.org/gochess/libchess/pkg/fen"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tag serves as data-container for game tags.
|
||||||
|
type Tag struct {
|
||||||
|
Key string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Game represents a game of chess.
|
||||||
|
type Game struct {
|
||||||
|
Tags []Tag
|
||||||
|
Board *board.Board
|
||||||
|
Moves []*board.Move
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGame returns an initialized game.
|
||||||
|
func NewGame() *Game {
|
||||||
|
b, err := fen.Import(fen.DefaultSetup)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return &Game{
|
||||||
|
Tags: []Tag{},
|
||||||
|
Board: b,
|
||||||
|
Moves: []*board.Move{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Game) String() string {
|
||||||
|
return fmt.Sprintf("<Game(Tags: %q, Moves: %q)>", g.Tags, g.Moves)
|
||||||
|
}
|
19
pkg/runes/runes.go
Normal file
19
pkg/runes/runes.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package runes
|
||||||
|
|
||||||
|
// The relevant unicode runes for chess are:
|
||||||
|
const (
|
||||||
|
WhiteKing = '♔'
|
||||||
|
WhiteQueen = '♕'
|
||||||
|
WhiteRook = '♖'
|
||||||
|
WhiteBishop = '♗'
|
||||||
|
WhiteKnight = '♘'
|
||||||
|
WhitePawn = '♙'
|
||||||
|
BlackKing = '♚'
|
||||||
|
BackQueen = '♛'
|
||||||
|
BlackRook = '♜'
|
||||||
|
BlackBishop = '♝'
|
||||||
|
BlackKnight = '♞'
|
||||||
|
BlackPawn = '♟'
|
||||||
|
Check = '†'
|
||||||
|
Mate = '‡'
|
||||||
|
)
|
Loading…
Reference in a new issue