Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Dani4kor/e1e8b439115878f8c6dcf127a4ed5d3e to your computer and use it in GitHub Desktop.
Save Dani4kor/e1e8b439115878f8c6dcf127a4ed5d3e to your computer and use it in GitHub Desktop.
Python FEN chess Validation with regular expression
def fenPass(fen):
"""
"""
regexMatch=re.match('\s*^(((?:[rnbqkpRNBQKP1-8]+\/){7})[rnbqkpRNBQKP1-8]+)\s([b|w])\s([K|Q|k|q]{1,4})\s(-|[a-h][1-8])\s(\d+\s\d+)$', fen)
if regexMatch:
regexList = regexMatch.groups()
fen = regexList[0].split("/")
if len(fen) != 8:
raise ValueError("expected 8 rows in position part of fen: {0}".format(repr(fen)))
for fenPart in fen:
field_sum = 0
previous_was_digit, previous_was_piece = False,False
for c in fenPart:
if c in ["1", "2", "3", "4", "5", "6", "7", "8"]:
if previous_was_digit:
raise ValueError("two subsequent digits in position part of fen: {0}".format(repr(fen)))
field_sum += int(c)
previous_was_digit = True
previous_was_piece = False
elif c == "~":
if not previous_was_piece:
raise ValueError("~ not after piece in position part of fen: {0}".format(repr(fen)))
previous_was_digit, previous_was_piece = False,False
elif c.lower() in ["p", "n", "b", "r", "q", "k"]:
field_sum += 1
previous_was_digit = False
previous_was_piece = True
else:
raise ValueError("invalid character in position part of fen: {0}".format(repr(fen)))
if field_sum != 8:
raise ValueError("expected 8 columns per row in position part of fen: {0}".format(repr(fen)))
else: raise ValueError("fen doesn`t match follow this example: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 ")
def main():
try:
fenPass('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1')
except ValueError as e:
print e.message
if __name__ == '__main__':
main()
@InputBlackBoxOutput
Copy link

This is really helpful. Thanks!

@glandre
Copy link

glandre commented Jul 15, 2021

Great work! Thanks for sharing!

One missing piece that I noticed, is that on the Castle portion of the FEN regex, there's also the option of no Castle at all, which would be '-'.
To fix that, just add the -| like in the following two portions. Here is the fixed regex:

\s*^(((?:[rnbqkpRNBQKP1-8]+\/){7})[rnbqkpRNBQKP1-8]+)\s([b|w])\s(-|[K|Q|k|q]{1,4})\s(-|[a-h][1-8])\s(\d+\s\d+)$

Here is a FEN that can be used to test that:

r4rk1/pbqn1pp1/1pn1p2p/2ppP2Q/3P4/2PBP1B1/PP1N2PP/R4RK1 w - - 0 14

@PanCave
Copy link

PanCave commented Dec 12, 2021

I'm really confused by the lines 22-25.
In no documentation I can find something about a valid character "~".
Can you explain, whats it meaning is?

@InputBlackBoxOutput
Copy link

@PanCave You can remove the condition since it is redundant. The regex expression matching will never allow ~ in the FEN notation

@johndoknjas
Copy link

johndoknjas commented Aug 17, 2022

For regexList = regexMatch.groups(), it may be better to replace this with regexList = fen.split().

For this FEN: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", regexMatch.groups() sets regexList equal to:

["rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/", "w", "KQkq", "-", "0 1"]

In the gist provided this doesn't make any difference, but if someone wanted to test more than the first portion of their FEN, the subsequent elements might not be what they expect. In terms of FEN notation, the second element shouldn't exist, and the "0 1" should be two different elements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment