func (m Move) San(b *Board) string {
	return m.algebraicNotation(b, PieceLetters)
}
San returns the move in Standard Algebraic Notation.
package chess
import (
	"errors"
	"strings"
)
type Move struct {
	From      Sq
	To        Sq
	Promotion Piece
}
var NullMove = Move{}
func (m Move) isLegal(b *Board) bool {
	b = b.MakeMove(m)
	_, illegal := b.pseudoLegalMoves()
	return !illegal
}
func (b *Board) ParseMove(s string) (Move, error) {
	if s == "--" {
		return NullMove, nil
	}
	var (
		f0, r0    = -1, -1  
		f1, r1    = -1, -1  
		piece     = NoPiece
		promotion = NoPiece
		castle    = -1
		err       = errors.New("invalid move")
	)
	if len(s) < 2 {
		return NullMove, err
	}
	switch {
	case strings.HasPrefix(s, "O-O-O") || strings.HasPrefix(s, "0-0-0"):
		castle = queenSide
	case strings.HasPrefix(s, "O-O") || strings.HasPrefix(s, "0-0"):
		castle = kingSide
	default:
		if p := pieceFromChar(rune(s[0])); p != NoPiece {
			if s[0] != 'b' || (len(s) > 2 && s[1] >= 'a' && s[1] <= 'h') {
				piece = p.Type()
				s = s[1:]
			}
		}
		for _, c := range s {
			if promotion == Bishop &&
				((c >= 'a' && c <= 'h') || (c >= '1' && c <= '8')) {
				f0, f1 = f1, FileB
				promotion = NoPiece
			}
			switch c {
			case 'b', 'n', 'r', 'q', 'B', 'N', 'R', 'Q':
				promotion = pieceFromChar(c).Type()
			case 'a', 'c', 'd', 'e', 'f', 'g', 'h':
				f0, f1 = f1, int(c-'a')
			case '1', '2', '3', '4', '5', '6', '7', '8':
				r0, r1 = r1, int(c-'1')
			}
		}
		if piece == NoPiece && (f0 == -1 || r0 == -1) {
			piece = Pawn
		}
		if f0 != -1 && f1 != -1 && r0 != -1 && r1 != -1 {
			from, to := Square(f0, r0), Square(f1, r1)
			if b.Piece[from] == b.my(King) && (b.Piece[to] == b.my(Rook) ||
				to == from+2 || to == from-2) {
				if to < from {
					castle = queenSide
				} else {
					castle = kingSide
				}
			}
		}
	}
	if castle != -1 {
		rook, king, _, _, _, _ := b.castleSquares(castle)
		if rook == NoSquare || king == NoSquare {
			return NullMove, err
		}
		f0, r0, f1, r1 = king.File(), king.Rank(), rook.File(), rook.Rank()
	}
	move := NullMove
	moves, _ := b.pseudoLegalMoves()
	for _, m := range moves {
		if (piece == NoPiece || b.Piece[m.From].Type() == piece) &&
			(f0 == -1 || f0 == m.From.File()) &&
			(r0 == -1 || r0 == m.From.Rank()) &&
			(f1 == -1 || f1 == m.To.File()) &&
			(r1 == -1 || r1 == m.To.Rank()) &&
			m.Promotion.Type() == promotion &&
			m.isLegal(b) {
			if move != NullMove {
				return NullMove, err  
			}
			move = m
		}
	}
	if move == NullMove {
		return NullMove, err
	}
	return move, nil
}
func (m Move) Uci(b *Board) string {
	if m == NullMove {
		return "0000"
	}
	var buf strings.Builder
	buf.WriteRune(rune('a' + m.From.File()))
	buf.WriteRune(rune('1' + m.From.Rank()))
	buf.WriteRune(rune('a' + m.To.File()))
	buf.WriteRune(rune('1' + m.To.Rank()))
	if m.Promotion != NoPiece {
		buf.WriteRune(PieceLetters[Black|m.Promotion.Type()])
	}
	return buf.String()
}