libchess/pkg/fen/fen.go

125 lines
2.6 KiB
Go
Raw Permalink Normal View History

2020-05-08 20:55:01 +00:00
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,
)
}