132 lines
3.8 KiB
JavaScript
132 lines
3.8 KiB
JavaScript
import { LETTER_PIECE_NAME_LOOKUP, PIECE_NAME_LETTER_LOOKUP, files } from './constants.js'
|
|
|
|
const parseCastlingAvailability = castlingAvailability => {
|
|
let castlingAvailabilityObj = {whiteKing: false, whiteQueen: false, blackKing: false, blackQueen: false}
|
|
if (castlingAvailability !== "-") {
|
|
for (const char of castlingAvailability) {
|
|
if (char === "K") {
|
|
castlingAvailabilityObj.whiteKing = true
|
|
} else if (char === "Q") {
|
|
castlingAvailabilityObj.whiteQueen = true
|
|
} else if (char === "k") {
|
|
castlingAvailabilityObj.blackKing = true
|
|
} else if (char === "q") {
|
|
castlingAvailabilityObj.blackQueen = true
|
|
}
|
|
}
|
|
}
|
|
return castlingAvailabilityObj
|
|
}
|
|
|
|
const castlingAvailabilityToFEN = castlingAvailabilityObj => {
|
|
let FEN = ""
|
|
if (castlingAvailabilityObj.whiteKing) {
|
|
FEN += "K"
|
|
}
|
|
if (castlingAvailabilityObj.whiteQueen) {
|
|
FEN += "Q"
|
|
}
|
|
if (castlingAvailabilityObj.blackKing) {
|
|
FEN += "k"
|
|
}
|
|
if (castlingAvailabilityObj.blackQueen) {
|
|
FEN += "q"
|
|
}
|
|
FEN = FEN || "-"
|
|
return FEN
|
|
}
|
|
|
|
const parsePositions = positions => {
|
|
let board = {}
|
|
const ranks = positions.split('/')
|
|
ranks.forEach((tokens, rankIndex) => {
|
|
rankIndex = Math.abs(rankIndex - 7)
|
|
let rank = rankIndex + 1
|
|
Array.from(tokens).forEach((token, fileIndex) => {
|
|
let parsed = parseInt(token)
|
|
if (isNaN(parsed)) {
|
|
let file = files[fileIndex]
|
|
board[`${file}${rank}`] = LETTER_PIECE_NAME_LOOKUP[token]
|
|
}
|
|
})
|
|
})
|
|
return board
|
|
}
|
|
|
|
const boardToFEN = board => {
|
|
let FEN = ""
|
|
let blanks = 0
|
|
let files = Array.from("abcdefgh")
|
|
let ranks = Array.from("12345678").reverse()
|
|
ranks.forEach((rank, rankIndex) => {
|
|
if (rankIndex) {
|
|
if (blanks) {
|
|
FEN += blanks
|
|
blanks = 0
|
|
}
|
|
FEN += "/"
|
|
}
|
|
files.forEach((file, charIndex) => {
|
|
let cell = `${file}${rank}`
|
|
if (board.hasOwnProperty(cell)) {
|
|
if (blanks) {
|
|
// it's insane this works, JS casts `blanks` to a String
|
|
FEN += blanks
|
|
blanks = 0
|
|
}
|
|
FEN += PIECE_NAME_LETTER_LOOKUP[board[cell]]
|
|
} else {
|
|
blanks++
|
|
}
|
|
})
|
|
})
|
|
return FEN
|
|
}
|
|
|
|
export const toFEN = gameState => {
|
|
let {
|
|
board,
|
|
whoseTurn,
|
|
castlingAvailability,
|
|
enPassantTargetSquare,
|
|
halfMoveClock,
|
|
fullMoveNum
|
|
} = gameState
|
|
|
|
enPassantTargetSquare = !enPassantTargetSquare ? "-" : enPassantTargetSquare
|
|
|
|
return [
|
|
boardToFEN(board),
|
|
whoseTurn[0],
|
|
castlingAvailabilityToFEN(castlingAvailability),
|
|
enPassantTargetSquare,
|
|
halfMoveClock,
|
|
fullMoveNum,
|
|
].join(" ")
|
|
}
|
|
|
|
export const getGameStateFromFEN = FEN => {
|
|
// https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
|
|
let [
|
|
positions,
|
|
whoseTurn,
|
|
castlingAvailability,
|
|
enPassantTargetSquare,
|
|
halfMoveClock,
|
|
fullMoveNum
|
|
] = FEN.split(" ")
|
|
whoseTurn = whoseTurn === "w" ? "white" : "black"
|
|
enPassantTargetSquare = enPassantTargetSquare === "-" ? null : enPassantTargetSquare
|
|
const castlingAvailabilityObj = parseCastlingAvailability(castlingAvailability)
|
|
fullMoveNum = parseInt(fullMoveNum)
|
|
halfMoveClock = parseInt(halfMoveClock)
|
|
return {
|
|
board: parsePositions(positions),
|
|
whoseTurn,
|
|
fullMoveNum,
|
|
halfMoveClock,
|
|
enPassantTargetSquare,
|
|
castlingAvailability: castlingAvailabilityObj,
|
|
}
|
|
}
|