Fix algebraicToLocation and added getPiece function
This commit is contained in:
parent
4410adb40b
commit
d52c1e3526
|
@ -13,12 +13,17 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
import ../util/matrix
|
import ../util/matrix
|
||||||
|
|
||||||
|
export matrix
|
||||||
|
|
||||||
import std/strutils
|
import std/strutils
|
||||||
import std/strformat
|
import std/strformat
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
|
Location = tuple[row, col: int]
|
||||||
|
Pieces = tuple[king: Location, queen: Location, rooks: array[2, Location],
|
||||||
|
bishops: array[2, Location], knights: array[2, Location],
|
||||||
|
pawns: array[8, Location]]
|
||||||
PieceColor* = enum
|
PieceColor* = enum
|
||||||
None = 0,
|
None = 0,
|
||||||
White,
|
White,
|
||||||
|
@ -36,10 +41,10 @@ type
|
||||||
kind*: PieceKind
|
kind*: PieceKind
|
||||||
Position* = object
|
Position* = object
|
||||||
piece*: Piece
|
piece*: Piece
|
||||||
location*: tuple[row, col: int]
|
location*: Location
|
||||||
ChessBoard* = ref object
|
ChessBoard* = ref object
|
||||||
## A chess board object
|
## A chess board object
|
||||||
grid: Matrix[Piece]
|
grid*: Matrix[Piece]
|
||||||
# Currently active color
|
# Currently active color
|
||||||
turn: PieceColor
|
turn: PieceColor
|
||||||
# Number of half moves since
|
# Number of half moves since
|
||||||
|
@ -55,6 +60,11 @@ type
|
||||||
# If en passant is not possible, both the row and
|
# If en passant is not possible, both the row and
|
||||||
# column of the position will be set to -1
|
# column of the position will be set to -1
|
||||||
enPassantSquare: Position
|
enPassantSquare: Position
|
||||||
|
# Locations of all pieces
|
||||||
|
pieces: tuple[white: Pieces, black: Pieces]
|
||||||
|
# Locations of all attacked squares
|
||||||
|
attacked: tuple[white: seq[Location], black: seq[Location]]
|
||||||
|
|
||||||
|
|
||||||
# Initialized only once, copied every time
|
# Initialized only once, copied every time
|
||||||
var empty: seq[Piece] = @[]
|
var empty: seq[Piece] = @[]
|
||||||
|
@ -62,12 +72,46 @@ for _ in countup(0, 63):
|
||||||
empty.add(Piece(kind: Empty, color: None))
|
empty.add(Piece(kind: Empty, color: None))
|
||||||
|
|
||||||
|
|
||||||
proc algebraicToPosition(s: string): tuple[row, col: int] {.inline.} =
|
proc algebraicToPosition(s: string): Location {.inline.} =
|
||||||
## Converts a square location from algebraic
|
## Converts a square location from algebraic
|
||||||
## notation to its corresponding row and column
|
## notation to its corresponding row and column
|
||||||
## in the chess grid (0 indexed)
|
## in the chess grid (0 indexed)
|
||||||
assert len(s) == 2
|
if len(s) != 2:
|
||||||
result = (int(uint8(s[0]) - (uint8('a') - 1)), int(uint8(s[1]) - uint8('0')))
|
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 file = int(uint8(s[0]) - uint8('a'))
|
||||||
|
var rank: int
|
||||||
|
case s[1]:
|
||||||
|
of '1':
|
||||||
|
rank = 7
|
||||||
|
of '2':
|
||||||
|
rank = 6
|
||||||
|
of '3':
|
||||||
|
rank = 5
|
||||||
|
of '4':
|
||||||
|
rank = 4
|
||||||
|
of '5':
|
||||||
|
rank = 3
|
||||||
|
of '6':
|
||||||
|
rank = 2
|
||||||
|
of '7':
|
||||||
|
rank = 1
|
||||||
|
of '8':
|
||||||
|
rank = 0
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
return (rank, file)
|
||||||
|
|
||||||
|
proc getPiece*(self: ChessBoard, square: string): Piece =
|
||||||
|
## Gets the piece on the given square
|
||||||
|
## in algebraic notation
|
||||||
|
let loc = square.algebraicToPosition()
|
||||||
|
return self.grid[loc.row, loc.col]
|
||||||
|
|
||||||
|
|
||||||
proc `$`*(self: ChessBoard): string =
|
proc `$`*(self: ChessBoard): string =
|
||||||
|
@ -84,12 +128,33 @@ proc `$`*(self: ChessBoard): string =
|
||||||
result &= "\n- - - - - - - -"
|
result &= "\n- - - - - - - -"
|
||||||
|
|
||||||
|
|
||||||
|
proc newChessboard: ChessBoard =
|
||||||
|
## Returns a new, empty chessboard
|
||||||
|
new(result)
|
||||||
|
# Initialize all positions to a known default state.
|
||||||
|
# This is useful in newChessBoardFromFEN
|
||||||
|
result.pieces.black.king = (-1, -1)
|
||||||
|
result.pieces.white.king = (-1, -1)
|
||||||
|
result.pieces.black.queen = (-1, -1)
|
||||||
|
result.pieces.white.queen = (-1, -1)
|
||||||
|
result.pieces.black.rooks = [(-1, -1), (-1, -1)]
|
||||||
|
result.pieces.white.rooks = [(-1, -1), (-1, -1)]
|
||||||
|
result.pieces.black.bishops = [(-1, -1), (-1, -1)]
|
||||||
|
result.pieces.white.bishops = [(-1, -1), (-1, -1)]
|
||||||
|
result.pieces.black.knights = [(-1, -1), (-1, -1)]
|
||||||
|
result.pieces.white.knights = [(-1, -1), (-1, -1)]
|
||||||
|
result.pieces.black.pawns = [(-1, -1), (-1, -1), (-1, -1), (-1, -1), (-1, -1), (-1, -1), (-1, -1), (-1, -1)]
|
||||||
|
result.pieces.white.pawns = [(-1, -1), (-1, -1), (-1, -1), (-1, -1), (-1, -1), (-1, -1), (-1, -1), (-1, -1)]
|
||||||
|
# Turns our flat sequence into an 8x8 grid
|
||||||
|
result.grid = newMatrixFromSeq[Piece](empty, (8, 8))
|
||||||
|
result.attacked = (@[], @[])
|
||||||
|
result.enPassantSquare = Position(piece: Piece(kind: Empty, color: None), location: (-1, -1))
|
||||||
|
|
||||||
|
|
||||||
proc newChessboardFromFEN*(state: string): ChessBoard =
|
proc newChessboardFromFEN*(state: string): ChessBoard =
|
||||||
## Initializes a chessboard with the
|
## Initializes a chessboard with the
|
||||||
## state encoded by the given FEN string
|
## state encoded by the given FEN string
|
||||||
new(result)
|
result = newChessboard()
|
||||||
# Turns our flat sequence into an 8x8 grid
|
|
||||||
result.grid = newMatrixFromSeq[Piece](empty, (8, 8))
|
|
||||||
var
|
var
|
||||||
# Current location in the grid
|
# Current location in the grid
|
||||||
row = 0
|
row = 0
|
||||||
|
@ -98,6 +163,8 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
|
||||||
section = 0
|
section = 0
|
||||||
# Current index into the FEN string
|
# Current index into the FEN string
|
||||||
index = 0
|
index = 0
|
||||||
|
# Temporary variable to store the piece
|
||||||
|
piece: Piece
|
||||||
# See https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
|
# See https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
|
||||||
while index <= state.high():
|
while index <= state.high():
|
||||||
var c = state[index]
|
var c = state[index]
|
||||||
|
@ -110,13 +177,82 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
|
||||||
of 0:
|
of 0:
|
||||||
# Piece placement data
|
# Piece placement data
|
||||||
case c.toLowerAscii():
|
case c.toLowerAscii():
|
||||||
|
# Piece
|
||||||
of 'r', 'n', 'b', 'q', 'k', 'p':
|
of 'r', 'n', 'b', 'q', 'k', 'p':
|
||||||
result.grid[row, column] = Piece(kind: PieceKind(c.toLowerAscii()), color: if c.isUpperAscii(): White else: Black)
|
# We know for a fact these values are in our
|
||||||
|
# enumeration, so all is good
|
||||||
|
{.push.}
|
||||||
|
{.warning[HoleEnumConv]:off.}
|
||||||
|
piece = Piece(kind: PieceKind(c.toLowerAscii()), color: if c.isUpperAscii(): White else: Black)
|
||||||
|
{.pop.}
|
||||||
|
case piece.color:
|
||||||
|
of Black:
|
||||||
|
case piece.kind:
|
||||||
|
of Pawn:
|
||||||
|
# Find first empty slot in the pieces array
|
||||||
|
for i, e in result.pieces.black.pawns:
|
||||||
|
if e == (-1, -1):
|
||||||
|
result.pieces.black.pawns[i] = (row, column)
|
||||||
|
break
|
||||||
|
of Bishop:
|
||||||
|
if result.pieces.black.bishops[0] == (-1, -1):
|
||||||
|
result.pieces.black.bishops[0] = (row, column)
|
||||||
|
else:
|
||||||
|
result.pieces.black.bishops[1] = (row, column)
|
||||||
|
of Knight:
|
||||||
|
if result.pieces.black.knights[0] == (-1, -1):
|
||||||
|
result.pieces.black.knights[0] = (row, column)
|
||||||
|
else:
|
||||||
|
result.pieces.black.knights[1] = (row, column)
|
||||||
|
of Rook:
|
||||||
|
if result.pieces.black.rooks[0] == (-1, -1):
|
||||||
|
result.pieces.black.rooks[0] = (row, column)
|
||||||
|
else:
|
||||||
|
result.pieces.black.rooks[1] = (row, column)
|
||||||
|
of Queen:
|
||||||
|
result.pieces.black.queen = (row, column)
|
||||||
|
of King:
|
||||||
|
result.pieces.black.king = (row, column)
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
of White:
|
||||||
|
case piece.kind:
|
||||||
|
of Pawn:
|
||||||
|
for i, e in result.pieces.white.pawns:
|
||||||
|
if e == (-1, -1):
|
||||||
|
result.pieces.white.pawns[i] = (row, column)
|
||||||
|
break
|
||||||
|
of Bishop:
|
||||||
|
if result.pieces.white.bishops[0] == (-1, -1):
|
||||||
|
result.pieces.white.bishops[0] = (row, column)
|
||||||
|
else:
|
||||||
|
result.pieces.white.bishops[1] = (row, column)
|
||||||
|
of Knight:
|
||||||
|
if result.pieces.white.knights[0] == (-1, -1):
|
||||||
|
result.pieces.white.knights[0] = (row, column)
|
||||||
|
else:
|
||||||
|
result.pieces.white.knights[1] = (row, column)
|
||||||
|
of Rook:
|
||||||
|
if result.pieces.white.rooks[0] == (-1, -1):
|
||||||
|
result.pieces.white.rooks[0] = (row, column)
|
||||||
|
else:
|
||||||
|
result.pieces.white.rooks[1] = (row, column)
|
||||||
|
of Queen:
|
||||||
|
result.pieces.white.queen = (row, column)
|
||||||
|
of King:
|
||||||
|
result.pieces.white.king = (row, column)
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
result.grid[row, column] = piece
|
||||||
inc(column)
|
inc(column)
|
||||||
of '/':
|
of '/':
|
||||||
|
# Next row
|
||||||
inc(row)
|
inc(row)
|
||||||
column = 0
|
column = 0
|
||||||
of '0'..'9':
|
of '0'..'9':
|
||||||
|
# Skip x columns
|
||||||
let x = int(uint8(c) - uint8('0')) - 1
|
let x = int(uint8(c) - uint8('0')) - 1
|
||||||
if x > 7:
|
if x > 7:
|
||||||
raise newException(ValueError, "invalid skip value (> 8) in FEN string")
|
raise newException(ValueError, "invalid skip value (> 8) in FEN string")
|
||||||
|
@ -154,7 +290,8 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
|
||||||
# En passant target square
|
# En passant target square
|
||||||
case c:
|
case c:
|
||||||
of '-':
|
of '-':
|
||||||
result.enPassantSquare.location = (-1, -1)
|
# Field is already uninitialized to the correct state
|
||||||
|
discard
|
||||||
else:
|
else:
|
||||||
result.enPassantSquare.location = state[index..index+1].algebraicToPosition()
|
result.enPassantSquare.location = state[index..index+1].algebraicToPosition()
|
||||||
# Just for cleanliness purposes, we fill in the other positional metadata as
|
# Just for cleanliness purposes, we fill in the other positional metadata as
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
import board
|
import board
|
||||||
|
|
||||||
|
|
||||||
|
var b = newDefaultChessboard()
|
||||||
|
echo b
|
||||||
|
echo b.getPiece("a2")
|
||||||
|
echo b.getPiece("a7")
|
||||||
|
echo b.getPiece("a1")
|
||||||
|
echo b.getPiece("a8")
|
||||||
|
|
||||||
echo newChessboardFromFEN("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1")[]
|
|
||||||
|
|
Loading…
Reference in New Issue