✨ FEN
This commit is contained in:
parent
c21a117a23
commit
80e9a0d890
1 changed files with 124 additions and 0 deletions
124
pkg/fen/fen.go
Normal file
124
pkg/fen/fen.go
Normal file
|
@ -0,0 +1,124 @@
|
|||
package fen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.c-base.org/gochess/libchess/pkg/board"
|
||||
)
|
||||
|
||||
// DefaultSetup holds the FEN to the default board setup.
|
||||
const DefaultSetup = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
|
||||
|
||||
// Import returns
|
||||
func Import(fen string) (*board.Board, error) {
|
||||
var (
|
||||
parts = strings.Split(fen, " ")
|
||||
pieces = map[board.Square]board.Piece{}
|
||||
fullMove uint64
|
||||
halfMove uint64
|
||||
ep *board.Square
|
||||
cr *board.CastleRights
|
||||
turn board.Color
|
||||
position string
|
||||
expandedFEN string
|
||||
err error
|
||||
)
|
||||
if len(parts) != 6 {
|
||||
return nil, fmt.Errorf("invalid fields in FEN: %s", fen)
|
||||
}
|
||||
if fullMove, err = strconv.ParseUint(parts[5], 10, 64); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if halfMove, err = strconv.ParseUint(parts[4], 10, 64); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if parts[3] != "-" {
|
||||
if square, ok := board.StrToSquareMap[strings.ToLower(parts[3])]; ok {
|
||||
ep = &square
|
||||
} else {
|
||||
return nil, fmt.Errorf("unable to parse e.p. square: %s", parts[3])
|
||||
}
|
||||
}
|
||||
if parts[2] != "-" {
|
||||
cr, err = board.NewCastleRights(parts[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
switch strings.ToLower(parts[1]) {
|
||||
case board.White.String():
|
||||
turn = board.White
|
||||
case board.Black.String():
|
||||
turn = board.Black
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to parse turn: %s", parts[2])
|
||||
}
|
||||
|
||||
position = parts[0]
|
||||
expandedFEN = expandFEN(position)
|
||||
parts = strings.Split(expandedFEN, "/")
|
||||
if len(parts) != 8 {
|
||||
return nil, fmt.Errorf("unable to parse position: %s", position)
|
||||
}
|
||||
for r := len(parts) - 1; r >= 0; r-- {
|
||||
for f := 0; f < 8; f++ {
|
||||
square := board.NewSquare(board.File(f), board.Rank(7-r))
|
||||
piece := board.NewPieceByFEN(rune(parts[r][f]))
|
||||
if piece != board.NoPiece {
|
||||
pieces[square] = piece
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b, err := board.NewBoard(pieces, turn, *cr, ep, halfMove, fullMove)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func expandFEN(s string) string {
|
||||
out := ""
|
||||
for _, c := range s {
|
||||
switch c {
|
||||
case 'K', 'Q', 'R', 'B', 'N', 'P', 'k', 'q', 'r', 'b', 'n', 'p', '/':
|
||||
out += string(c)
|
||||
case '1':
|
||||
out += "1"
|
||||
case '2':
|
||||
out += "11"
|
||||
case '3':
|
||||
out += "111"
|
||||
case '4':
|
||||
out += "1111"
|
||||
case '5':
|
||||
out += "11111"
|
||||
case '6':
|
||||
out += "111111"
|
||||
case '7':
|
||||
out += "1111111"
|
||||
case '8':
|
||||
out += "11111111"
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Export returns the FEN string of the given *board.Board{}.
|
||||
func Export(b *board.Board) string {
|
||||
ep := "-"
|
||||
if b.EnPassent != nil {
|
||||
ep = b.EnPassent.String()
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"%s %s %s %s %d %d",
|
||||
b.String(),
|
||||
b.Turn.String(),
|
||||
b.CastleRights.String(),
|
||||
ep,
|
||||
b.HalfMove,
|
||||
b.FullMove,
|
||||
)
|
||||
}
|
Loading…
Reference in a new issue