I don’t really expect much, the code is working perfectly and also did some perft test with different positions and got the same results from the chessprogramming wiki.
This is actually my third engine the last two where using 2d array which was slower and also had a lot of problems with the perft result not getting the same result and currently I am using 1d array meaning its just array[64]
, I’m not really sure how I would increase the speed of move generation.
I created a plugin for the perft test since roblox does not allow multiple recrution. I am currently looking for ideas of how I would increase the speed of the move generation so the alpha beta(nega max) pruning would be able to reach deeper depth. Feel free testing the game out if you found anything please let me know
Chess.rbxl (72.8 KB)
Code
local Move = require(game.ReplicatedStorage.ModuleScripts.PieceMove)
local insert = table.insert
local remove = table.remove
local find = table.find
local floor = math.floor
local abs = math.abs
local min = math.min
local ChessEngine = {}
function ChessEngine:GameState(fen : string)
local self = {
FenString = fen,
Checkmate = false,
Stalemate = false,
InCheck = false,
Draw = false,
Piece = {
King = "K",
Pawn = "P",
Knight = "N",
Bishop = "B",
Rook = "R",
Queen = "Q",
White = "w",
Black = "b"
},
Pins = {},
Board = {},
Checks = {},
MoveLog = {},
CastleRightLog = {},
NumSquareToEdge = {},
PawnSquareInEdge = {},
KnightSquareInEdge = {},
EnpassantPossibleLog = {},
PromotionTypes = {"Q", "R", "B", "N"},
DirectionOffsets = {-1, 1, 8, -8, 7, -7, 9, -9},
KnightMoves = {15, 6, -10, -17, -15, -6, 10, 17},
}
function self.Initialize()
self.ImportFen()
self.PrecomputedMoveData()
self.PrecomputedPawnMoveData()
self.PrecumputedKnightMoveData()
self.PieceMoveFunctions = {
P = self.GetPawnMoves,
K = self.GetKingMoves,
N = self.GetKnightMoves,
B = self.GetSlidingMoves,
R = self.GetSlidingMoves,
Q = self.GetSlidingMoves
}
end
function self.ImportFen()
local pieceTypeFromSymbol = {
k = self.Piece.King,
p = self.Piece.Pawn,
n = self.Piece.Knight,
b = self.Piece.Bishop,
r = self.Piece.Rook,
q = self.Piece.Queen
}
local fen = self.FenString:split(" ")
local fenBoard = fen[1]:split("")
local fenTurn = fen[2]
local fenCastling = fen[3]:split("")
local fenEnpassant = fen[4]:split("")
local fen50Rule = tonumber(fen[5])
local file, rank = 1, 7
local tempEnpassant = {}
local tempCastleRight = {false, false, false, false}
local ranksToRows = {0, 1, 2, 3, 4, 5, 6, 7}
local filesToCols = {
a = 1,
b = 2,
c = 3,
d = 4,
e = 5,
f = 6,
g = 7,
h = 8
}
for _ = 1, 64 do
insert(self.Board, "--")
end
for _, char in fenBoard do
if char ~= "/" then
local isNumber = tonumber(char)
if isNumber then
file += isNumber
else
local pieceType = pieceTypeFromSymbol[char:lower()]
local pieceColor = char:upper() == char and self.Piece.White or self.Piece.Black
local square = rank * 8 + file
if char == "K" then
self.WhiteKingLocation = square
elseif char == "k" then
self.BlackKingLocation = square
end
self.Board[square] = pieceColor..pieceType
file += 1
end
else
file = 1
rank -= 1
end
end
if fenTurn == "w" then
self.WhiteToMove = true
elseif fenTurn == "b" then
self.WhiteToMove = false
end
for _, char in fenCastling do
if char == "K" then
tempCastleRight[1] = true
elseif char == "Q" then
tempCastleRight[2] = true
elseif char == "k" then
tempCastleRight[3] = true
elseif char == "q" then
tempCastleRight[4] = true
end
end
if fenEnpassant[1] ~= "-" then
self.EnpassantPossible = ranksToRows[tonumber(fenEnpassant[2])] * 8 + filesToCols[fenEnpassant[1]]
else
self.EnpassantPossible = 0
end
self.CurrentCastleRights = ChessEngine:CastleRights(
tempCastleRight[1], tempCastleRight[2],
tempCastleRight[3], tempCastleRight[4]
)
self.CastleRightLog.append(self.CurrentCastleRights)
self.EnpassantPossibleLog.append(self.EnpassantPossible)
end
function self.FlipTheBoard()
local board = {}
for i = 1, floor(#self.Board / 2) do
local j = #self.Board - i + 1
board[i], board[j] = self.Board[j], self.Board[i]
end
return board
end
function self.FlipValidMoves()
local moves = self.GetValidMoves()
for _, move in moves do
move.SqStart = abs(move.SqStart - 65)
move.SqTarget = abs(move.SqTarget - 65)
end
return moves
end
function self.MakeMove(move : any)
if move.SqStart == move.SqTarget then return end
self.Board[move.SqStart] = "--"
self.Board[move.SqTarget] = move.PieceMoved
self.WhiteToMove = not self.WhiteToMove
if move.PieceMoved == "wK" then
self.WhiteKingLocation = move.SqTarget
elseif move.PieceMoved == "bK" then
self.BlackKingLocation = move.SqTarget
end
if move.PieceMoved:split("")[2] == "P" and abs(move.SqStart - move.SqTarget) == 16 then
self.EnpassantPossible = floor((move.SqStart + move.SqTarget) / 2)
else
self.EnpassantPossible = 0
end
if move.IsEnpassantMove then
self.Board[move.EnpassantTarget] = "--"
elseif move.IsPawnPromotion then
self.Board[move.SqTarget] = move.PieceMoved:split("")[1]..move.PromotionType
elseif move.IsCastleMove then
self.Board[move.RookNewSquare] = self.Board[move.RookCurrentSquare]
self.Board[move.RookCurrentSquare] = "--"
end
self.UpdateCastleRights(move)
self.MoveLog.append(move)
self.CastleRightLog.append(self.CurrentCastleRights)
self.EnpassantPossibleLog.append(self.EnpassantPossible)
end
function self.UndoMove()
if #self.MoveLog ~= 0 then
local move = self.MoveLog.pop()
self.Board[move.SqStart] = move.PieceMoved
self.Board[move.SqTarget] = move.PieceCaptured
self.WhiteToMove = not self.WhiteToMove
self.Checkmate = false
self.Stalemate = false
self.Draw = false
if move.PieceMoved == "wK" then
self.WhiteKingLocation = move.SqStart
elseif move.PieceMoved == "bK" then
self.BlackKingLocation = move.SqStart
end
if move.IsEnpassantMove then
self.Board[move.EnpassantTarget] = move.PieceCaptured
self.Board[move.SqTarget] = "--"
elseif move.IsCastleMove then
self.Board[move.RookCurrentSquare] = self.Board[move.RookNewSquare]
self.Board[move.RookNewSquare] = "--"
end
self.CurrentCastleRights = self.CastleRightLog.pop()
self.EnpassantPossible = self.EnpassantPossibleLog.pop()
end
end
function self.MoveLog.pop() : any
return remove(self.MoveLog, #self.MoveLog)
end
function self.EnpassantPossibleLog.pop() : any
remove(self.EnpassantPossibleLog, #self.EnpassantPossibleLog)
return self.EnpassantPossibleLog[#self.EnpassantPossibleLog]
end
function self.CastleRightLog.pop() : any
remove(self.CastleRightLog, #self.CastleRightLog)
local castleRight = self.CastleRightLog[#self.CastleRightLog]
castleRight = ChessEngine:CastleRights(
castleRight.wK, castleRight.wQ,
castleRight.bK, castleRight.bQ
)
return castleRight
end
function self.MoveLog.append(move : any)
insert(self.MoveLog, move)
end
function self.EnpassantPossibleLog.append(enpassantPossible : number)
insert(self.EnpassantPossibleLog, enpassantPossible)
end
function self.CastleRightLog.append(castleRight : any)
insert(self.CastleRightLog, ChessEngine:CastleRights(
castleRight.wK, castleRight.wQ,
castleRight.bK, castleRight.bQ
))
end
function self.UpdateCastleRights(move : any)
local pieceMoved = move.PieceMoved
if pieceMoved == "wK" then
self.CurrentCastleRights.wK = false
self.CurrentCastleRights.wQ = false
elseif pieceMoved == "bK" then
self.CurrentCastleRights.bK = false
self.CurrentCastleRights.bQ = false
elseif pieceMoved == "wR" then
if move.SqStart == 8 then
self.CurrentCastleRights.wK = false
elseif move.SqStart == 1 then
self.CurrentCastleRights.wQ = false
end
elseif pieceMoved == "bR" then
if move.SqStart == 64 then
self.CurrentCastleRights.bK = false
elseif move.SqStart == 57 then
self.CurrentCastleRights.bQ = false
end
end
end
function self.GetValidMoves(debugging : boolean) : any
local moves = {}
local sqStart = self.WhiteToMove and self.WhiteKingLocation or self.BlackKingLocation
self.InCheck, self.Pins, self.Checks = self.CheckForPossiblePins()
if self.InCheck then
if #self.Checks == 1 then
moves = self.GetAllPossibleMoves()
local check = self.Checks[1]
local checkSquare = check[1]
local pieceChecking = self.Board[checkSquare]
local piece = pieceChecking:split("")
local validSquares = {}
if piece[2] == "N" then
validSquares = {checkSquare}
else
for i = 1, 8 do
local validSquare = sqStart + check[2] * i
insert(validSquares, validSquare)
if validSquare == checkSquare then
break
end
end
end
for i = #moves, 1, -1 do
local move = moves[i]
if move.PieceMoved:split("")[2] ~= "K" then
if not find(validSquares, move.SqTarget) then
if move.IsEnpassantMove then
local moveAmount = self.WhiteToMove and -8 or 8
if move.SqTarget + moveAmount ~= checkSquare then
remove(moves, i)
end
else
remove(moves, i)
end
end
end
end
else
self.GetKingMoves(sqStart, "K", moves)
end
else
moves = self.GetAllPossibleMoves()
end
if #moves == 0 then
if self.InCheck then
self.Checkmate = true
--print("checkmate")
else
self.Stalemate = true
--print("stalemate")
end
else
if not debugging then
for _, move in moves do
self.MakeMove(move)
local nextMoves = self.GetValidMoves(true)
if #nextMoves == 0 then
if self.Checkmate then
self.Checkmate = false
move.Checkmate = true
elseif self.Stalemate then
self.Checkmate = false
move.Stalemate = true
end
elseif self.InCheck then
self.InCheck = false
move.Check = true
end
self.UndoMove(move)
end
self.Checkmate = false
self.Stalemate = false
end
end
return moves
end
function self.CheckForPossiblePins() : any
local pins, checks = {}, {}
local inCheck = false
local sqStart, ally, enemy
if self.WhiteToMove then
sqStart = self.WhiteKingLocation
enemy = "b"
ally = "w"
else
sqStart = self.BlackKingLocation
enemy = "w"
ally = "b"
end
for directionIndex, direction in self.DirectionOffsets do
if self.NumSquareToEdge[sqStart][directionIndex] == 0 then continue end
local possiblePin = {}
for n = 1, self.NumSquareToEdge[sqStart][directionIndex] do
local sqTarget = sqStart + self.DirectionOffsets[directionIndex] * n
local pieceOnTargetSquare = self.Board[sqTarget]
local endPiece = pieceOnTargetSquare:split("")
if endPiece[1] == ally and endPiece[2] ~= "K" then
if #possiblePin == 0 then
possiblePin = {sqTarget, direction}
else
break
end
elseif endPiece[1] == enemy then
local Type = endPiece[2]
if (Type == "R" and (directionIndex >= 1 and directionIndex <= 4)) or
(Type == "B" and (directionIndex >= 5 and directionIndex <= 8)) or
(Type == "P" and n == 1 and
((ally == "w" and (directionIndex == 5 or directionIndex == 7)) or
((ally == "b" and (directionIndex == 6 or directionIndex == 8))))) or
(Type == "Q") or (Type == "K" and n == 1) then
if #possiblePin == 0 then
inCheck = true
insert(checks, {sqTarget, direction})
break
else
insert(pins, possiblePin)
end
else
break
end
end
end
end
for directionIndex, moveOffset in self.KnightMoves do
if self.KnightSquareInEdge[sqStart][directionIndex] then
local sqTarget = sqStart + moveOffset
local pieceOnTargetSquare = self.Board[sqTarget]
local endPiece = pieceOnTargetSquare:split("")
if endPiece[1] == enemy and endPiece[2] == "N" then
inCheck = true
insert(checks, {sqTarget, moveOffset})
end
end
end
return inCheck, pins, checks
end
function self.CheckSquareUnderAttack(sqStart : number, ally : string) : boolean
local enemy = ally == "w" and "b" or "w"
for directionIndex, direction in self.DirectionOffsets do
if self.NumSquareToEdge[sqStart][directionIndex] == 0 then continue end
local possiblePin = {}
for n = 1, self.NumSquareToEdge[sqStart][directionIndex] do
local sqTarget = sqStart + self.DirectionOffsets[directionIndex] * n
local pieceOnTargetSquare = self.Board[sqTarget]
local endPiece = pieceOnTargetSquare:split("")
if endPiece[1] == ally then
break
elseif endPiece[1] == enemy then
local Type = endPiece[2]
if (Type == "R" and (directionIndex >= 1 and directionIndex <= 4)) or
(Type == "B" and (directionIndex >= 5 and directionIndex <= 8)) or
(Type == "P" and n == 1 and
((ally == "w" and (directionIndex == 5 or directionIndex == 7)) or
((ally == "b" and (directionIndex == 6 or directionIndex == 8))))) or
(Type == "Q") or (Type == "K" and n == 1) then
return true
else
break
end
end
end
end
for directionIndex, moveOffset in self.KnightMoves do
if self.KnightSquareInEdge[sqStart][directionIndex] then
local sqTarget = sqStart + moveOffset
local pieceOnTargetSquare = self.Board[sqTarget]
local endPiece = pieceOnTargetSquare:split("")
if endPiece[1] == enemy and endPiece[2] == "N" then
return true
end
end
end
return false
end
function self.GetAllPossibleMoves() : any
local moves = {}
for i, piece in self.Board do
local piece = piece:split("")
if (self.WhiteToMove and piece[1] == "w") or (not self.WhiteToMove and piece[1] == "b") then
self.PieceMoveFunctions[piece[2]](i, piece[2], moves)
end
end
return moves
end
function self.PrecomputedMoveData()
for file = 0, 7 do
for rank = 0, 7 do
local numNorth = 7 - rank
local numSouth = rank
local numWest = file
local numEast = 7 - file
local squareIndex = rank * 8 + (file + 1)
self.NumSquareToEdge[squareIndex] = {
numWest,
numEast,
numNorth,
numSouth,
min(numNorth, numWest),
min(numSouth, numEast),
min(numNorth, numEast),
min(numSouth, numWest)
}
end
end
end
function self.PrecomputedPawnMoveData()
for row = 0, 7 do
for col = 1, 8 do
local squareIndex = row * 8 + col
if col == 1 then
self.PawnSquareInEdge[squareIndex] = {true, false}
elseif col == 8 then
self.PawnSquareInEdge[squareIndex] = {false, true}
else
self.PawnSquareInEdge[squareIndex] = {false, false}
end
end
end
end
function self.PrecumputedKnightMoveData()
for row = 0, 7 do
for col = 1, 8 do
local squareIndex = row * 8 + col
local north1 = row + 1
local south1 = row - 1
local north2 = row + 2
local south2 = row - 2
local west1 = col - 1
local east1 = col + 1
local west2 = col - 2
local east2 = col + 2
north1 = (north1 >= 0 and north1 <= 7)
south1 = (south1 >= 0 and south1 <= 7)
north2 = (north2 >= 0 and north2 <= 7)
south2 = (south2 >= 0 and south2 <= 7)
west1 = (west1 >= 1 and west1 <= 8)
east1 = (east1 >= 1 and east1 <= 8)
west2 = (west2 >= 1 and west2 <= 8)
east2 = (east2 >= 1 and east2 <= 8)
self.KnightSquareInEdge[squareIndex] = {
(north2 and west1),
(north1 and west2),
(south1 and west2),
(south2 and west1),
(south2 and east1),
(south1 and east2),
(north1 and east2),
(north2 and east1)
}
end
end
end
function self.GetSlidingMoves(sqStart : number, piece : string, moves : any)
local startDirIndex = piece == "B" and 5 or 1
local endDirIndex = piece == "R" and 4 or 8
local piecePinned = false
local pinDirection = 0
local enemy, ally
if self.WhiteToMove then
ally = "w"
enemy = "b"
else
ally = "b"
enemy = "w"
end
for i, pin in self.Pins do
if pin[1] == sqStart then
piecePinned = true
pinDirection = pin[2]
remove(self.Pins, i)
break
end
end
for directionIndex = startDirIndex, endDirIndex do
if self.NumSquareToEdge[sqStart][directionIndex] == 0 then continue end
for n = 1, self.NumSquareToEdge[sqStart][directionIndex] do
if not piecePinned or pinDirection == self.DirectionOffsets[directionIndex] or pinDirection == -self.DirectionOffsets[directionIndex] then
local sqTarget = sqStart + self.DirectionOffsets[directionIndex] * n
local pieceOnTargetSquare = self.Board[sqTarget]
if pieceOnTargetSquare:split("")[1] == ally then
break
end
insert(moves, Move:Add(sqStart, sqTarget, self.Board))
if pieceOnTargetSquare:split("")[1] == enemy then
break
end
end
end
end
end
function self.GetKingMoves(sqStart : number, piece : string, moves : any)
local enemy, ally
if self.WhiteToMove then
enemy = "b"
ally = "w"
else
enemy = "w"
ally = "b"
end
for directionIndex = 1, 8 do
if not (self.NumSquareToEdge[sqStart][directionIndex] == 0) then
local sqTarget = sqStart + self.DirectionOffsets[directionIndex]
local pieceOnTargetSquare = self.Board[sqTarget]
if pieceOnTargetSquare:split("")[1] == ally then
continue
end
if ally == "w" then
self.WhiteKingLocation = sqTarget
else
self.BlackKingLocation = sqTarget
end
local inCheck, pins, checks = self.CheckForPossiblePins()
if not inCheck then
insert(moves, Move:Add(sqStart, sqTarget, self.Board))
end
if ally == "w" then
self.WhiteKingLocation = sqStart
else
self.BlackKingLocation = sqStart
end
end
end
self.GetCastleMoves(sqStart, ally, moves)
end
function self.GetCastleMoves(sqStart : number, ally : string, moves : any)
local inCheck = self.CheckSquareUnderAttack(sqStart, ally)
if inCheck then return end
if (self.WhiteToMove and self.CurrentCastleRights.wK) or (not self.WhiteToMove and self.CurrentCastleRights.bK) then
self.GetKingSideCaslteMove(sqStart, ally, moves)
end
if (self.WhiteToMove and self.CurrentCastleRights.wQ) or (not self.WhiteToMove and self.CurrentCastleRights.bQ) then
self.GetQueenSideCaslteMove(sqStart, ally, moves)
end
end
function self.GetKingSideCaslteMove(sqStart : number, ally : string, moves : any)
if self.Board[sqStart + 1] == "--" and self.Board[sqStart + 2] == "--" and self.Board[sqStart + 3]:split("")[1] == ally then
if not self.CheckSquareUnderAttack(sqStart + 1, ally) and not self.CheckSquareUnderAttack(sqStart + 2, ally) then
local move = Move:Add(sqStart, sqStart + 2, self.Board, true)
move.RookCurrentSquare = sqStart + 3
move.RookNewSquare = sqStart + 1
insert(moves, move)
end
end
end
function self.GetQueenSideCaslteMove(sqStart : number, ally : string, moves : any)
if self.Board[sqStart - 1] == "--" and self.Board[sqStart - 2] == "--" and self.Board[sqStart - 3] == "--" and self.Board[sqStart - 4]:split("")[1] == ally then
if not self.CheckSquareUnderAttack(sqStart - 1, ally) and not self.CheckSquareUnderAttack(sqStart - 2, ally) then
local move = Move:Add(sqStart, sqStart - 2, self.Board, true)
move.RookCurrentSquare = sqStart - 4
move.RookNewSquare = sqStart - 1
insert(moves, move)
end
end
end
function self.GetKnightMoves(sqStart : number, piece : string, moves : any)
local piecePinned = false
local enemy, ally
if self.WhiteToMove then
ally = "w"
enemy = "b"
else
ally = "b"
enemy = "w"
end
for i, pin in self.Pins do
if pin[1] == sqStart then
piecePinned = true
remove(self.Pins, i)
break
end
end
for directionIndex, moveOffset in self.KnightMoves do
if self.KnightSquareInEdge[sqStart][directionIndex] then
if not piecePinned then
local sqTarget = sqStart + moveOffset
local pieceOnTargetSquare = self.Board[sqTarget]
if pieceOnTargetSquare:split("")[1] == ally then
continue
end
insert(moves, Move:Add(sqStart, sqTarget, self.Board))
end
end
end
end
function self.GetPawnMoves(sqStart : number, piece : string, moves : any)
local kingMinSquare, kingMaxSquare, kingLocation, kingInRowOfEnpassant, canDoublePush, moveAmount, enemy, ally
local piecePinned = false
local pinDirection = 0
if self.WhiteToMove then
ally = "w"
enemy = "b"
moveAmount = 8
kingMinSquare = 33
kingMaxSquare = 40
kingLocation = self.WhiteKingLocation
canDoublePush = (sqStart > 8 and sqStart <= 16)
kingInRowOfEnpassant = (kingLocation >= kingMinSquare and kingLocation <= kingMaxSquare)
else
ally = "b"
enemy = "w"
moveAmount = -8
kingMinSquare = 25
kingMaxSquare = 32
kingLocation = self.BlackKingLocation
canDoublePush = (sqStart > 48 and sqStart <= 56)
kingInRowOfEnpassant = (kingLocation >= kingMinSquare and kingLocation <= kingMaxSquare)
end
local singlePush = sqStart + moveAmount
local doublePush = sqStart + 2 * moveAmount
for i, pin in self.Pins do
if pin[1] == sqStart then
piecePinned = true
pinDirection = pin[2]
remove(self.Pins, i)
break
end
end
if self.Board[singlePush] == "--" then
if not piecePinned or sqStart - pinDirection == singlePush or sqStart + pinDirection == singlePush then
if (self.WhiteToMove and (singlePush > 55 and singlePush <= 64)) or (not self.WhiteToMove and (singlePush > 0 and singlePush <= 8)) then
for i, piece in self.PromotionTypes do
local move = Move:Add(sqStart, singlePush, self.Board)
move.IsPawnPromotion = true
move.PromotionType = piece
insert(moves, move)
end
else
insert(moves, Move:Add(sqStart, singlePush, self.Board))
if canDoublePush and self.Board[doublePush] == "--" then
insert(moves, Move:Add(sqStart, doublePush, self.Board))
end
end
end
end
for directionIndex = 1, 2 do
local attackDirection = self.DirectionOffsets[directionIndex]
local sqTarget = sqStart + moveAmount + attackDirection
if (attackDirection == -1 and self.PawnSquareInEdge[sqStart][1]) or (attackDirection == 1 and self.PawnSquareInEdge[sqStart][2]) then continue end
if not piecePinned or sqStart + pinDirection == sqTarget then
if self.Board[sqTarget]:split("")[1] == enemy then
if (self.WhiteToMove and (sqTarget > 55 and sqTarget <= 64)) or (not self.WhiteToMove and (sqTarget > 0 and sqTarget <= 8)) then
for i, piece in self.PromotionTypes do
local move = Move:Add(sqStart, sqTarget, self.Board)
move.IsPawnPromotion = true
move.PromotionType = piece
insert(moves, move)
end
else
insert(moves, Move:Add(sqStart, sqTarget, self.Board))
end
end
if self.EnpassantPossible == sqTarget and self.Board[self.EnpassantPossible - moveAmount]:split("")[1] == enemy then
local attackingPiece, blockingPiece = false, false
local inRange = {}
local outRange = {}
if kingInRowOfEnpassant then
if attackDirection == 1 then
if kingLocation < sqStart then
inRange = {kingLocation, sqStart - 1, 1}
outRange = {sqStart + 2, kingMaxSquare, 1}
else
inRange = {kingLocation, sqStart + 2, -1}
outRange = {sqStart - 1, kingMinSquare, -1}
end
else
if kingLocation < sqStart then
inRange = {kingLocation, sqStart - 2, 1}
outRange = {sqStart + 1, kingMaxSquare, 1}
else
inRange = {kingLocation, sqStart + 1, -1}
outRange = {sqStart - 2, kingMinSquare, -1}
end
end
for i = inRange[1], inRange[2], inRange[3] do
local piece = self.Board[i]
if piece ~= "--" and piece ~= ally.."K" then
blockingPiece = true
end
end
for i = outRange[1], outRange[2], outRange[3] do
local piece = self.Board[i]:split("")
if piece[1] == enemy and (piece[2] == "Q" or piece[2] == "R") then
attackingPiece = true
break
elseif piece[1] ~= "-" then
blockingPiece = true
break
end
end
end
if not attackingPiece or blockingPiece then
local move = Move:Add(sqStart, sqTarget, self.Board, true)
move.EnpassantTarget = self.EnpassantPossible - moveAmount
insert(moves, move)
end
end
end
end
end
self.Initialize()
return self
end
function ChessEngine:CastleRights(wK : boolean, wQ : boolean, bK : boolean, bQ : boolean)
local self = {
wK = wK,
wQ = wQ,
bK = bK,
bQ = bQ
}
return self
end
return ChessEngine