96 lines
3.2 KiB
Nim
96 lines
3.2 KiB
Nim
## Low-level handling of squares, board indeces and pieces
|
|
import std/strutils
|
|
import std/strformat
|
|
|
|
|
|
type
|
|
Square* = tuple[rank, file: int8]
|
|
## A square
|
|
|
|
PieceColor* = enum
|
|
## A piece color enumeration
|
|
None = 0'i8,
|
|
White,
|
|
Black
|
|
|
|
PieceKind* = enum
|
|
## A chess piece enumeration
|
|
Empty = 0'i8, # No piece
|
|
Bishop = 'b',
|
|
King = 'k'
|
|
Knight = 'n',
|
|
Pawn = 'p',
|
|
Queen = 'q',
|
|
Rook = 'r',
|
|
|
|
Direction* = enum
|
|
## A move direction enumeration
|
|
Forward,
|
|
Backward,
|
|
Left,
|
|
Right
|
|
ForwardLeft,
|
|
ForwardRight,
|
|
BackwardLeft,
|
|
BackwardRight
|
|
|
|
Piece* = object
|
|
## A chess piece
|
|
color*: PieceColor
|
|
kind*: PieceKind
|
|
|
|
|
|
func coordToIndex*(row, col: SomeInteger): int {.inline.} = (row * 8) + col
|
|
func coordToIndex*(square: Square): int {.inline.} = coordToIndex(square.rank, square.file)
|
|
func indexToCoord*(index: SomeInteger): Square {.inline.} = ((index div 8).int8, (index mod 8).int8)
|
|
func nullPiece*: Piece {.inline.} = Piece(kind: Empty, color: None)
|
|
func nullSquare*: Square {.inline.} = (-1 , -1)
|
|
func opposite*(c: PieceColor): PieceColor {.inline.} = (if c == White: Black else: White)
|
|
func `+`*(a, b: Square): Square {.inline.} = (a.rank + b.rank, a.file + b.file)
|
|
func `-`*(a: Square): Square {.inline.} = (-a.rank, -a.file)
|
|
func `-`*(a, b: Square): Square{.inline.} = (a.rank - b.rank, a.file - b.file)
|
|
func isValid*(a: Square): bool {.inline.} = a.rank in 0..7 and a.file in 0..7
|
|
func isLightSquare*(a: Square): bool {.inline.} = (a.rank + a.file and 2) == 0
|
|
func makeSquare*(rank, file: SomeInteger): Square = (rank: rank.int8, file: file.int8)
|
|
|
|
|
|
func fileToColumn*(file: int): int8 {.inline.} =
|
|
## Converts a chess file (1-indexed)
|
|
## into a 0-indexed column value for our
|
|
## board. This converter is necessary because
|
|
## chess positions are indexed differently with
|
|
## respect to our internal representation
|
|
const indeces: array[8, int8] = [7, 6, 5, 4, 3, 2, 1, 0]
|
|
return indeces[file - 1]
|
|
|
|
|
|
func rowToRank*(row: int): int8 {.inline.} =
|
|
## Converts a row into our grid into
|
|
## a chess rank
|
|
const indeces: array[8, int8] = [8, 7, 6, 5, 4, 3, 2, 1]
|
|
return indeces[row]
|
|
|
|
|
|
func algebraicToSquare*(s: string): Square =
|
|
## Converts a square square from algebraic
|
|
## notation to its corresponding row and column
|
|
## in the chess grid (0 indexed)
|
|
if len(s) != 2:
|
|
raise newException(ValueError, "algebraic position must be of length 2")
|
|
|
|
var s = s.toLowerAscii()
|
|
if s[0] notin 'a'..'h':
|
|
raise newException(ValueError, &"algebraic position has invalid first character ('{s[0]}')")
|
|
if s[1] notin '1'..'8':
|
|
raise newException(ValueError, &"algebraic position has invalid second character ('{s[1]}')")
|
|
|
|
let rank = int8(uint8(s[0]) - uint8('a'))
|
|
# Convert the file character to a number
|
|
let file = fileToColumn(int8(uint8(s[1]) - uint8('0')))
|
|
return (file, rank)
|
|
|
|
|
|
func squareToAlgebraic*(square: Square): string {.inline.} =
|
|
## Converts a square from our internal rank/file
|
|
## notation to a square in algebraic notation
|
|
return &"{rowToRank(square.rank)}{char(uint8(square.file) + uint8('a'))}" |