## Method to interpret the third field of a FEN string in a program playing FRC

Discussion of chess software programming and technical issues.

Moderators: hgm, Harvey Williamson, bob

Forum rules
This textbox is used to restore diagrams posted with the [d] tag before the upgrade.
Roland Chastain
Posts: 164
Joined: Sat Jun 08, 2013 8:07 am
Location: Dakar (Senegal)
Full name: Roland Chastain
Contact:

### Method to interpret the third field of a FEN string in a program playing FRC

Hello!

I tried to solve the following problem. For a program playing Fischer random chess, how to read the third field of a FEN string, without knowing what the notation style will be ("KQkq" or "HAha").

Here is my solution. I would be glad to know what you think about the method I found. I feel that it is too complicated, but I didn't find something simpler.

The function returns for each color and each side the position of the king and of the rook, or nil value if the castling isn't available.

Code: Select all

``````-- Lua function for an engine playing Fischer random chess.
-- Extracts castling data from a FEN string, accepting both notation style.

function EncodeCastling(
AFen3,
ABoard -- The chessboard is needed as second parameter
)

function GetRookFile(
AColor,   -- Piece color
APattern, -- Regular expression for character detection
AFrom,    -- Where to start searching for the rook
ATo,      -- Where to stop
AStep,    -- In which direction to search
AKey      -- K, Q, k, q
)
local LResult = 0
for LMatch in string.gmatch(AFen3, APattern) do
local LRook = AColor and 'r' or 'R'
local LFile = AColor and 'a' or 'A'
local LRank = AColor and  8  or  1
for x = AFrom, ATo, AStep do
if LResult == 0 then
if (ABoard[x][LRank] == LRook) and (
(LMatch == string.char(string.byte(LFile) + x - 1)) -- B-H, A-G, b-h, a-g
or (LMatch == AKey)                                 -- K, Q, k, q
) then
LResult = x
break
end
end
end
end
return LResult
end

local K, Q, k, q, X = nil, nil, nil, nil, nil

local IsWhiteCastlingAvailable = string.match(AFen3, "[ABCDEFGHKQ]")
if IsWhiteCastlingAvailable then
for x = 1, 8 do
if ABoard[x][1] == 'K' then
X = x
break
end
end
end

local IsBlackCastlingAvailable = string.match(AFen3, "[abcdefghkq]")
if IsBlackCastlingAvailable and (X == nil) then
for x = 1, 8 do
if ABoard[x][8] == 'k' then
X = x
break
end
end
end

if IsWhiteCastlingAvailable then
K = GetRookFile(false, "[BCDEFGHK]", 8, X, -1, "K")
Q = GetRookFile(false, "[ABCDEFGQ]", 1, X,  1, "Q")
end

if IsBlackCastlingAvailable then
k = GetRookFile(true,  "[bcdefghk]", 8, X, -1, "k")
q = GetRookFile(true,  "[abcdefgq]", 1, X,  1, "q")
end

return {K = K, Q = Q, k = k, q = q, X = X}
end

-- demo

require('chess')

function Test(AFen)
local LPos = EncodePosition(AFen)
local LCastling = EncodeCastling(LPos.castlingAvailability, LPos.piecePlacement)
print("white k. side", LCastling.K) -- Rook file or nil when no castling is available
print("white q. side", LCastling.Q) -- Idem
print("black k. side", LCastling.k) -- Idem
print("black q. side", LCastling.q) -- Idem
print("king", LCastling.X)          -- King file or nil when no castling is available
end

local LSample = {
"rknbbqnr/pppppppp/8/8/8/8/PPPPPPPP/RKNBBQNR w HAha - 0 1",
"nrbkqbnr/pppppppp/8/8/8/8/PPPPPPPP/NRBKQBNR w KQkq - 0 1",
"qrbknbrn/pppppppp/8/8/8/8/PPPPPPPP/QRBKNBRN w GBgb - 0 1",
"nrbkqrnb/pppppppp/8/8/8/8/PPPPPPPP/NRBKQRNB w FBfb - 0 1",
"qnrbbknr/pppppppp/8/8/8/8/PPPPPPPP/QNRBBKNR w HChc - 0 1",
"rnb1k1nr/p1pp1ppp/4p3/1p6/1P6/P1N1PN2/2P2P1P/R1BQKB1q b Qkq - 1 10",
"rnb1k2r/pppp1pp1/4p2p/8/8/2bPPN2/P2B1PqP/R2QKR2 b Qkq - 1 13"
}

for i = 1, #LSample do
Test(LSample[i])
end``````

Code: Select all

``````white k. side   8
white q. side   1
black k. side   8
black q. side   1
king    2
white k. side   8
white q. side   2
black k. side   8
black q. side   2
king    4
white k. side   7
white q. side   2
black k. side   7
black q. side   2
king    4
white k. side   6
white q. side   2
black k. side   6
black q. side   2
king    4
white k. side   8
white q. side   3
black k. side   8
black q. side   3
king    6
white k. side   0
white q. side   1
black k. side   8
black q. side   1
king    5
white k. side   0
white q. side   1
black k. side   8
black q. side   1
king    5``````
If you want to try the Lua script, you need the module chess.lua from the Luciole projet.

Thank you.

mar
Posts: 1960
Joined: Fri Nov 26, 2010 1:00 pm
Location: Czech Republic
Full name: Martin Sedlak

### Re: Method to interpret the third field of a FEN string in a program playing FRC

Well, in X-FEN you don't specify the rook file (AHah - that's Shredder notation if I'm not mistaken), so you simply have to scan for the rooks to determine rook file.

I think this is probably what you do.
It's a bit trickier if you have both rooks on either side, for more information: https://en.wikipedia.org/wiki/X-FEN
Martin Sedlak

Roland Chastain
Posts: 164
Joined: Sat Jun 08, 2013 8:07 am
Location: Dakar (Senegal)
Full name: Roland Chastain
Contact:

### Re: Method to interpret the third field of a FEN string in a program playing FRC

mar wrote:
Thu May 16, 2019 9:01 pm
Well, in X-FEN you don't specify the rook file (AHah - that's Shredder notation if I'm not mistaken), so you simply have to scan for the rooks to determine rook file.

I think this is probably what you do.
It's a bit trickier if you have both rooks on either side, for more information: https://en.wikipedia.org/wiki/X-FEN
Thank you for your answer and for the useful link. It seems that I followed the good method (if not wrote the most efficient and short code).

I completed and cleaned up my script. Now it can also convert internal castling data to FEN string (X-FEN or Shredder-FEN style).

Code: Select all

``````function FileLetter(AFile, AColor)
return string.char(string.byte(AColor and 'a' or 'A') + AFile - 1)
end

function EncodeCastling(AFen3, ABoard)

function RookFile(
AColor,
APattern,
AFrom,
ATo,
AStep,
AKey
)
local LResult = nil
for LMatch in string.gmatch(AFen3, APattern) do
local LRook = AColor and 'r' or 'R'
local LRank = AColor and  8  or  1
for x = AFrom, ATo, AStep do
if LResult == nil then
if (ABoard[x][LRank] == LRook) and ((LMatch == FileLetter(x, AColor)) or (LMatch == AKey)) then
LResult = x
break
end
end
end
end
return LResult
end

local K, Q, k, q, X = nil, nil, nil, nil, nil
local WhiteCastling = string.match(AFen3, "[ABCDEFGHKQ]")
local BlackCastling = string.match(AFen3, "[abcdefghkq]")

if WhiteCastling then
for x = 1, 8 do
if ABoard[x][1] == 'K' then
X = x
break
end
end
K = RookFile(false, "[BCDEFGHK]", 8, X, -1, "K")
Q = RookFile(false, "[ABCDEFGQ]", 1, X,  1, "Q")
end

if BlackCastling then
if X == nil then
for x = 1, 8 do
if ABoard[x][8] == 'k' then
X = x
break
end
end
end
k = RookFile(true,  "[bcdefghk]", 8, X, -1, "k")
q = RookFile(true,  "[abcdefgq]", 1, X,  1, "q")
end

return {K = K, Q = Q, k = k, q = q, X = X}
end

function RookInFront(ABoard, AColor, AFrom, ATo, AStep)
local LRook = AColor and 'r' or 'R'
local LRank = AColor and  8  or  1
for x = AFrom, ATo - AStep, AStep do
if ABoard[x][LRank] == LRook then
return true
end
end
return false
end

function DecodeCastling(ACastling, ABoard, AAlwaysFileLetter)
local LResult = ""
if AAlwaysFileLetter then
if ACastling.K then LResult = LResult .. FileLetter(ACastling.K, false) end
if ACastling.Q then LResult = LResult .. FileLetter(ACastling.Q, false) end
if ACastling.k then LResult = LResult .. FileLetter(ACastling.k,  true) end
if ACastling.q then LResult = LResult .. FileLetter(ACastling.q,  true) end
else
if ACastling.K then LResult = LResult .. (RookInFront(ABoard, false, 8, ACastling.K,-1) and FileLetter(ACastling.K, false) or "K") end
if ACastling.Q then LResult = LResult .. (RookInFront(ABoard, false, 1, ACastling.Q, 1) and FileLetter(ACastling.Q, false) or "Q") end
if ACastling.k then LResult = LResult .. (RookInFront(ABoard, true,  8, ACastling.k,-1) and FileLetter(ACastling.k, true)  or "k") end
if ACastling.q then LResult = LResult .. (RookInFront(ABoard, true,  1, ACastling.q, 1) and FileLetter(ACastling.q, true)  or "q") end
end
return LResult
end

require('chess')

function Test(AFen)
local LPos = EncodePosition(AFen)
local LCastling = EncodeCastling(LPos.castlingAvailability, LPos.piecePlacement)
print("-- " .. AFen)
print("rook file white k. side:", LCastling.K)
print("          white q. side:", LCastling.Q)
print("          black k. side:", LCastling.k)
print("          black q. side:", LCastling.q)
print("king file              :", LCastling.X)
print("X-FEN                  :", DecodeCastling(LCastling, LPos.piecePlacement, false))
print("S-FEN                  :", DecodeCastling(LCastling, LPos.piecePlacement, true)) -- Shredder-FEN
end

local LSample = {
"rknbbqnr/pppppppp/8/8/8/8/PPPPPPPP/RKNBBQNR w HAha - 0 1",
"nrbkqbnr/pppppppp/8/8/8/8/PPPPPPPP/NRBKQBNR w KQkq - 0 1",
"qrbknbrn/pppppppp/8/8/8/8/PPPPPPPP/QRBKNBRN w GBgb - 0 1",
"nrbkqrnb/pppppppp/8/8/8/8/PPPPPPPP/NRBKQRNB w FBfb - 0 1",
"qnrbbknr/pppppppp/8/8/8/8/PPPPPPPP/QNRBBKNR w HChc - 0 1",
"rnb1k1nr/p1pp1ppp/4p3/1p6/1P6/P1N1PN2/2P2P1P/R1BQKB1q b Qkq - 1 10",
"rnb1k2r/pppp1pp1/4p2p/8/8/2bPPN2/P2B1PqP/R2QKR2 b Qkq - 1 13",
"rn2k1r1/ppp1pp1p/3p2p1/5bn1/P7/2N2B2/1PPPPP2/2BNK1RR w Gkq - 4 11"
}

for i = 1, #LSample do
Test(LSample[i])
end
``````