ref: e82dcee1a3d54f14c93a19f8fe21965ea72c56ce
dir: /moves.go/
package main
import (
"fmt"
"strings"
)
// Error
type MoveParsingError struct {
move string
}
func (e MoveParsingError) Error() string {
return fmt.Sprintf("move '%s' invalid", e.move)
}
type AmbiguousMoveError struct {
move string
}
func (e AmbiguousMoveError) Error() string {
return fmt.Sprintf("move '%s' is ambiguous", e.move)
}
type PieceParsingError struct {
piece string
}
func (e PieceParsingError) Error() string {
return fmt.Sprintf("piece '%s' invalid", e.piece)
}
type CoordinateParsingError struct {
coord string
}
func (e CoordinateParsingError) Error() string {
return fmt.Sprintf("coordinate '%s' invalid", e.coord)
}
// Chess move types
type MoveType uint8
const (
Basic = iota
Capture
Promotion
DrawOffer
Castling
GenericMove
)
// Coordinate
type Coordinate struct {
X int
Y int
}
// Chess move
type Move struct {
Tp MoveType
Notation string
Piece Piece
Takes Piece
PromotionPiece Piece
From *Coordinate
To *Coordinate
}
// Parsing utils
func pieceNotation(piece string) (Piece, error) {
switch (piece) {
case "K":
return King, nil
case "Q":
return Queen, nil
case "R":
return Rook, nil
case "B":
return Bishop, nil
case "N":
return Knight, nil
default:
return NoPiece, PieceParsingError { piece: piece }
}
}
func coordinateNotation(notation string) (*Coordinate, error) {
if len(notation) != 2 {
return nil, CoordinateParsingError { coord: notation }
}
if []byte(notation)[0] < byte(97) || []byte(notation)[0] > byte(122) {
return nil, CoordinateParsingError { coord: notation }
}
if []byte(notation)[1] < byte(48) || []byte(notation)[1] > byte(57) {
return nil, CoordinateParsingError { coord: notation }
}
column := []byte(notation)[0] - 97
row := 7 - ([]byte(notation)[1] - 48 - 1)
return &Coordinate {
X: int(column),
Y: int(row),
}, nil
}
func NewMove(sac string, b *Board) (*Move, error) {
var ac = sac
var takenPiece Piece = AnyPiece
var promotionPiece Piece = NoPiece
var piece Piece
var to *Coordinate
var from *Coordinate
var err error
var moveType MoveType = Basic
acPromote := strings.Split(ac, "=")
if len(acPromote) > 2 {
return nil, MoveParsingError { move: ac }
}
if len(acPromote) == 2 {
ac = acPromote[0]
moveType = Promotion
promotionPiece, err = pieceNotation(string(ac[1]))
if err != nil {
return nil, err
}
}
switch (len(ac)) {
case 2:
piece = Pawn
to, err = coordinateNotation(ac)
if err != nil {
return nil, err
}
case 3:
piece, err = pieceNotation(ac[0:1])
if err != nil {
return nil, err
}
to, err = coordinateNotation(ac[1:])
if err != nil {
return nil, err
}
case 4:
piece = Pawn
from, err = coordinateNotation(ac[0:2])
if err != nil {
return nil, err
}
to, err = coordinateNotation(ac[2:4])
if err != nil {
return nil, err
}
case 5:
piece, err = pieceNotation(ac[0:1])
if err != nil {
return nil, err
}
from, err = coordinateNotation(ac[1:3])
if err != nil {
return nil, err
}
to, err = coordinateNotation(ac[3:5])
if err != nil {
return nil, err
}
case 6:
piece, err = pieceNotation(ac[0:1])
if err != nil {
return nil, err
}
to, err = coordinateNotation(ac[1:3])
if err != nil {
return nil, err
}
from, err = coordinateNotation(ac[3:5])
if err != nil {
return nil, err
}
takenPiece, err = pieceNotation(ac[5:6])
if err != nil {
return nil, err
}
default:
return nil, MoveParsingError { move: ac }
}
if from == nil {
from = b.FindSingleMove(piece, to)
}
if from == nil {
return nil, AmbiguousMoveError {}
}
if takenPiece != AnyPiece {
moveType = Capture
}
move := &Move {
Tp: moveType,
Notation: sac,
Piece: piece,
Takes: takenPiece,
PromotionPiece: promotionPiece,
From: from,
To: to,
}
return move, nil
}