shithub: chessfs

ref: e82dcee1a3d54f14c93a19f8fe21965ea72c56ce
dir: /moves.go/

View raw version
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
}