You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
156 lines
3.5 KiB
156 lines
3.5 KiB
4 years ago
|
package chess
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// Decodes FEN notation into a GameState. An error is returned
|
||
|
// if there is a parsing error. FEN notation format:
|
||
|
// rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
|
||
|
func decodeFEN(fen string) (*Position, error) {
|
||
|
fen = strings.TrimSpace(fen)
|
||
|
parts := strings.Split(fen, " ")
|
||
|
if len(parts) != 6 {
|
||
|
return nil, fmt.Errorf("chess: fen invalid notiation %s must have 6 sections", fen)
|
||
|
}
|
||
|
b, err := fenBoard(parts[0])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
turn, ok := fenTurnMap[parts[1]]
|
||
|
if !ok {
|
||
|
return nil, fmt.Errorf("chess: fen invalid turn %s", parts[1])
|
||
|
}
|
||
|
rights, err := formCastleRights(parts[2])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
sq, err := formEnPassant(parts[3])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
halfMoveClock, err := strconv.Atoi(parts[4])
|
||
|
if err != nil || halfMoveClock < 0 {
|
||
|
return nil, fmt.Errorf("chess: fen invalid half move clock %s", parts[4])
|
||
|
}
|
||
|
moveCount, err := strconv.Atoi(parts[5])
|
||
|
if err != nil || moveCount < 1 {
|
||
|
return nil, fmt.Errorf("chess: fen invalid move count %s", parts[5])
|
||
|
}
|
||
|
return &Position{
|
||
|
board: b,
|
||
|
turn: turn,
|
||
|
castleRights: rights,
|
||
|
enPassantSquare: sq,
|
||
|
halfMoveClock: halfMoveClock,
|
||
|
moveCount: moveCount,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// generates board from fen format: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR
|
||
|
func fenBoard(boardStr string) (*Board, error) {
|
||
|
rankStrs := strings.Split(boardStr, "/")
|
||
|
if len(rankStrs) != 8 {
|
||
|
return nil, fmt.Errorf("chess: fen invalid board %s", boardStr)
|
||
|
}
|
||
|
m := map[Square]Piece{}
|
||
|
for i, rankStr := range rankStrs {
|
||
|
rank := Rank(7 - i)
|
||
|
fileMap, err := fenFormRank(rankStr)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
for file, piece := range fileMap {
|
||
|
m[getSquare(file, rank)] = piece
|
||
|
}
|
||
|
}
|
||
|
return NewBoard(m), nil
|
||
|
}
|
||
|
|
||
|
func fenFormRank(rankStr string) (map[File]Piece, error) {
|
||
|
count := 0
|
||
|
m := map[File]Piece{}
|
||
|
err := fmt.Errorf("chess: fen invalid rank %s", rankStr)
|
||
|
for _, r := range rankStr {
|
||
|
c := fmt.Sprintf("%c", r)
|
||
|
piece := fenPieceMap[c]
|
||
|
if piece == NoPiece {
|
||
|
skip, err := strconv.Atoi(c)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
count += skip
|
||
|
continue
|
||
|
}
|
||
|
m[File(count)] = piece
|
||
|
count++
|
||
|
}
|
||
|
if count != 8 {
|
||
|
return nil, err
|
||
|
}
|
||
|
return m, nil
|
||
|
}
|
||
|
|
||
|
func formCastleRights(castleStr string) (CastleRights, error) {
|
||
|
// check for duplicates aka. KKkq right now is valid
|
||
|
for _, s := range []string{"K", "Q", "k", "q", "-"} {
|
||
|
if strings.Count(castleStr, s) > 1 {
|
||
|
return "-", fmt.Errorf("chess: fen invalid castle rights %s", castleStr)
|
||
|
}
|
||
|
}
|
||
|
for _, r := range castleStr {
|
||
|
c := fmt.Sprintf("%c", r)
|
||
|
switch c {
|
||
|
case "K", "Q", "k", "q", "-":
|
||
|
default:
|
||
|
return "-", fmt.Errorf("chess: fen invalid castle rights %s", castleStr)
|
||
|
}
|
||
|
}
|
||
|
return CastleRights(castleStr), nil
|
||
|
}
|
||
|
|
||
|
func formEnPassant(enPassant string) (Square, error) {
|
||
|
if enPassant == "-" {
|
||
|
return NoSquare, nil
|
||
|
}
|
||
|
sq := strToSquareMap[enPassant]
|
||
|
if sq == NoSquare || !(sq.Rank() == Rank3 || sq.Rank() == Rank6) {
|
||
|
return NoSquare, fmt.Errorf("chess: fen invalid En Passant square %s", enPassant)
|
||
|
}
|
||
|
return sq, nil
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
fenSkipMap = map[int]string{
|
||
|
1: "1",
|
||
|
2: "2",
|
||
|
3: "3",
|
||
|
4: "4",
|
||
|
5: "5",
|
||
|
6: "6",
|
||
|
7: "7",
|
||
|
8: "8",
|
||
|
}
|
||
|
fenPieceMap = map[string]Piece{
|
||
|
"K": WhiteKing,
|
||
|
"Q": WhiteQueen,
|
||
|
"R": WhiteRook,
|
||
|
"B": WhiteBishop,
|
||
|
"N": WhiteKnight,
|
||
|
"P": WhitePawn,
|
||
|
"k": BlackKing,
|
||
|
"q": BlackQueen,
|
||
|
"r": BlackRook,
|
||
|
"b": BlackBishop,
|
||
|
"n": BlackKnight,
|
||
|
"p": BlackPawn,
|
||
|
}
|
||
|
|
||
|
fenTurnMap = map[string]Color{
|
||
|
"w": White,
|
||
|
"b": Black,
|
||
|
}
|
||
|
)
|