def applyFen(self, fenstr): """ Applies the fenstring to the board. If the string is not properly written a SyntaxError will be raised, having its message ending in Pos(%d) specifying the string index of the problem. if an error is found, no changes will be made to the board. """ assert not self.fen_was_applied, "The applyFen() method can be used on new LBoard objects only!" # Set board to empty on Black's turn (which Polyglot-hashes to 0) self.blocker = 0 self.friends = [0, 0] self.kings = [-1, -1] self.boards = ([0] * 7, [0] * 7) self.enpassant = None # cord which can be captured by enpassant or None self.color = BLACK self.castling = 0 # The castling availability in the position self.hasCastled = [False, False] self.fifty = 0 # A ply counter for the fifty moves rule self.plyCount = 0 self.checked = None self.opchecked = None self.arBoard = [0] * 64 self.hash = 0 self.pawnhash = 0 # Data from the position's history: self.hist_move = [] # The move that was applied to get the position self.hist_tpiece = [] # The piece the move captured, == EMPTY for normal moves self.hist_enpassant = [] self.hist_castling = [] self.hist_hash = [] self.hist_fifty = [] self.hist_checked = [] self.hist_opchecked = [] # piece counts self.pieceCount = ([0] * 7, [0] * 7) # initial cords of rooks and kings for castling in Chess960 if self.variant == FISCHERRANDOMCHESS: self.ini_kings = [None, None] self.ini_rooks = ([None, None], [None, None]) elif self.variant in (WILDCASTLECHESS, WILDCASTLESHUFFLECHESS): self.ini_kings = [None, None] self.fin_kings = ([None, None], [None, None]) self.fin_rooks = ([None, None], [None, None]) elif self.variant in DROP_VARIANTS: self.iniHouse() elif self.variant == ATOMICCHESS: self.iniAtomic() elif self.variant == CAMBODIANCHESS: self.iniCambodian() # Get information parts = fenstr.split() castChr = "-" epChr = "-" fiftyChr = "0" moveNoChr = "1" if STRICT_FEN and len(parts) != 6: raise SyntaxError(_("FEN needs 6 data fields. \n\n%s") % fenstr) elif len(parts) < 2: raise SyntaxError( _("FEN needs at least 2 data fields in fenstr. \n\n%s") % fenstr) elif len(parts) >= 6: pieceChrs, colChr, castChr, epChr, fiftyChr, moveNoChr = parts[:6] elif len(parts) == 5: pieceChrs, colChr, castChr, epChr, fiftyChr = parts elif len(parts) == 4: if parts[2].isdigit() and parts[3].isdigit(): # xboard FEN usage for asian variants pieceChrs, colChr, fiftyChr, moveNoChr = parts else: pieceChrs, colChr, castChr, epChr = parts elif len(parts) == 3: pieceChrs, colChr, castChr = parts else: pieceChrs, colChr = parts # Try to validate some information # TODO: This should be expanded and perhaps moved slashes = pieceChrs.count("/") if slashes < 7: raise SyntaxError( _("Needs 7 slashes in piece placement field. \n\n%s") % fenstr) if not colChr.lower() in ("w", "b"): raise SyntaxError( _("Active color field must be one of w or b. \n\n%s") % fenstr) if castChr != "-": for Chr in castChr: valid_chars = "ABCDEFGHKQ" if self.variant == FISCHERRANDOMCHESS else "KQ" if Chr.upper() not in valid_chars: if self.variant == CAMBODIANCHESS: pass # sjaakii uses DEde in cambodian starting fen to indicate # that queens and kings are virgins (not moved yet) else: raise SyntaxError( _("Castling availability field is not legal. \n\n%s" ) % fenstr) if epChr != "-" and epChr not in cordDic: raise SyntaxError( _("En passant cord is not legal. \n\n%s") % fenstr) # Parse piece placement field promoted = False # if there is a holding within [] we change it to BFEN style first if pieceChrs.endswith("]"): pieceChrs = pieceChrs[:-1].replace("[", "/") for r, rank in enumerate(pieceChrs.split("/")): cord = (7 - r) * 8 for char in rank: if r > 7: # After the 8.rank BFEN can contain holdings (captured pieces) # "~" after a piece letter denotes promoted piece if r == 8 and self.variant in DROP_VARIANTS: color = char.islower() and BLACK or WHITE piece = chrU2Sign[char.upper()] self.holding[color][piece] += 1 self.hash ^= holdingHash[color][piece][ self.holding[color][piece]] continue else: break if char.isdigit(): cord += int(char) elif char == "~": promoted = True else: color = char.islower() and BLACK or WHITE piece = chrU2Sign[char.upper()] self._addPiece(cord, piece, color) self.pieceCount[color][piece] += 1 if self.variant in DROP_VARIANTS and promoted: self.promoted[cord] = 1 promoted = False if self.variant == CAMBODIANCHESS: if piece == KING and self.kings[ color] != self.ini_kings[color]: self.is_first_move[KING][color] = False if piece == QUEEN and cord != self.ini_queens[color]: self.is_first_move[QUEEN][color] = False cord += 1 if self.variant == FISCHERRANDOMCHESS: # Save ranks fo find outermost rooks # if KkQq was used in castling rights if r == 0: rank8 = rank elif r == 7: rank1 = rank # Parse active color field if colChr.lower() == "w": self.setColor(WHITE) else: self.setColor(BLACK) # Parse castling availability castling = 0 for char in castChr: if self.variant == FISCHERRANDOMCHESS: if char in reprFile: if char < reprCord[self.kings[BLACK]][0]: castling |= B_OOO self.ini_rooks[1][0] = reprFile.index(char) + 56 else: castling |= B_OO self.ini_rooks[1][1] = reprFile.index(char) + 56 elif char in [c.upper() for c in reprFile]: if char < reprCord[self.kings[WHITE]][0].upper(): castling |= W_OOO self.ini_rooks[0][0] = reprFile.index(char.lower()) else: castling |= W_OO self.ini_rooks[0][1] = reprFile.index(char.lower()) elif char == "K": castling |= W_OO self.ini_rooks[0][1] = rank1.rfind('R') elif char == "Q": castling |= W_OOO self.ini_rooks[0][0] = rank1.find('R') elif char == "k": castling |= B_OO self.ini_rooks[1][1] = rank8.rfind('r') + 56 elif char == "q": castling |= B_OOO self.ini_rooks[1][0] = rank8.find('r') + 56 else: if char == "K": castling |= W_OO elif char == "Q": castling |= W_OOO elif char == "k": castling |= B_OO elif char == "q": castling |= B_OOO if self.variant in (WILDCASTLECHESS, WILDCASTLESHUFFLECHESS, FISCHERRANDOMCHESS): self.ini_kings[WHITE] = self.kings[WHITE] self.ini_kings[BLACK] = self.kings[BLACK] if self.variant in (WILDCASTLECHESS, WILDCASTLESHUFFLECHESS): if self.ini_kings[WHITE] == D1 and self.ini_kings[BLACK] == D8: self.fin_kings = ([B1, F1], [B8, F8]) self.fin_rooks = ([C1, E1], [C8, E8]) elif self.ini_kings[WHITE] == D1: self.fin_kings = ([B1, F1], [C8, G8]) self.fin_rooks = ([C1, E1], [D8, F8]) elif self.ini_kings[BLACK] == D8: self.fin_kings = ([C1, G1], [B8, F8]) self.fin_rooks = ([D1, F1], [C8, E8]) else: self.fin_kings = ([C1, G1], [C8, G8]) self.fin_rooks = ([D1, F1], [D8, F8]) self.setCastling(castling) # Parse en passant target sqaure if epChr == "-": self.setEnpassant(None) else: self.setEnpassant(cordDic[epChr]) # Parse halfmove clock field if fiftyChr.isdigit(): self.fifty = int(fiftyChr) else: self.fifty = 0 # Parse fullmove number if moveNoChr.isdigit(): movenumber = max(int(moveNoChr), 1) * 2 - 2 if self.color == BLACK: movenumber += 1 self.plyCount = movenumber else: self.plyCount = 1 self.fen_was_applied = True
def applyFen(self, fenstr): """ Applies the fenstring to the board. If the string is not properly written a SyntaxError will be raised, having its message ending in Pos(%d) specifying the string index of the problem. if an error is found, no changes will be made to the board. """ assert not self.fen_was_applied, "The applyFen() method can be used on new LBoard objects only!" # Set board to empty on Black's turn (which Polyglot-hashes to 0) self.blocker = 0 self.friends = [0] * 2 self.kings = [-1] * 2 self.boards = [[0] * 7 for i in range(2)] self.enpassant = None # cord which can be captured by enpassant or None self.color = BLACK self.castling = 0 # The castling availability in the position self.hasCastled = [False, False] self.fifty = 0 # A ply counter for the fifty moves rule self.plyCount = 0 self.checked = None self.opchecked = None self.arBoard = [0] * 64 self.hash = 0 self.pawnhash = 0 # Data from the position's history: self.hist_move = [] # The move that was applied to get the position self.hist_tpiece = [ ] # The piece the move captured, == EMPTY for normal moves self.hist_enpassant = [] self.hist_castling = [] self.hist_hash = [] self.hist_fifty = [] self.hist_checked = [] self.hist_opchecked = [] # piece counts self.pieceCount = [[0] * 7, [0] * 7] # initial cords of rooks and kings for castling in Chess960 if self.variant == FISCHERRANDOMCHESS: self.ini_kings = [None, None] self.ini_rooks = ([None, None], [None, None]) elif self.variant in (WILDCASTLECHESS, WILDCASTLESHUFFLECHESS): self.ini_kings = [None, None] self.fin_kings = ([None, None], [None, None]) self.fin_rooks = ([None, None], [None, None]) elif self.variant in DROP_VARIANTS: self.iniHouse() elif self.variant == ATOMICCHESS: self.iniAtomic() elif self.variant == CAMBODIANCHESS: self.iniCambodian() # Get information parts = fenstr.split() castChr = "-" epChr = "-" fiftyChr = "0" moveNoChr = "1" if STRICT_FEN and len(parts) != 6: raise SyntaxError(_("FEN needs 6 data fields. \n\n%s") % fenstr) elif len(parts) < 2: raise SyntaxError( _("FEN needs at least 2 data fields in fenstr. \n\n%s") % fenstr) elif len(parts) >= 6: pieceChrs, colChr, castChr, epChr, fiftyChr, moveNoChr = parts[:6] elif len(parts) == 5: pieceChrs, colChr, castChr, epChr, fiftyChr = parts elif len(parts) == 4: if parts[2].isdigit() and parts[3].isdigit(): # xboard FEN usage for asian variants pieceChrs, colChr, fiftyChr, moveNoChr = parts else: pieceChrs, colChr, castChr, epChr = parts elif len(parts) == 3: pieceChrs, colChr, castChr = parts else: pieceChrs, colChr = parts # Try to validate some information # TODO: This should be expanded and perhaps moved slashes = pieceChrs.count("/") if slashes < 7: raise SyntaxError( _("Needs 7 slashes in piece placement field. \n\n%s") % fenstr) if not colChr.lower() in ("w", "b"): raise SyntaxError( _("Active color field must be one of w or b. \n\n%s") % fenstr) if castChr != "-": for Chr in castChr: valid_chars = "ABCDEFGHKQ" if self.variant == FISCHERRANDOMCHESS else "KQ" if Chr.upper() not in valid_chars: if self.variant == CAMBODIANCHESS: pass # sjaakii uses DEde in cambodian starting fen to indicate # that queens and kings are virgins (not moved yet) else: raise SyntaxError(_("Castling availability field is not legal. \n\n%s") % fenstr) if epChr != "-" and epChr not in cordDic: raise SyntaxError(_("En passant cord is not legal. \n\n%s") % fenstr) # Parse piece placement field promoted = False # if there is a holding within [] we change it to BFEN style first if pieceChrs.endswith("]"): pieceChrs = pieceChrs[:-1].replace("[", "/") for r, rank in enumerate(pieceChrs.split("/")): cord = (7 - r) * 8 for char in rank: if r > 7: # After the 8.rank BFEN can contain holdings (captured pieces) # "~" after a piece letter denotes promoted piece if r == 8 and self.variant in DROP_VARIANTS: color = char.islower() and BLACK or WHITE piece = chrU2Sign[char.upper()] self.holding[color][piece] += 1 continue else: break if char.isdigit(): cord += int(char) elif char == "~": promoted = True else: color = char.islower() and BLACK or WHITE piece = chrU2Sign[char.upper()] self._addPiece(cord, piece, color) self.pieceCount[color][piece] += 1 if self.variant in DROP_VARIANTS and promoted: self.promoted[cord] = 1 promoted = False if self.variant == CAMBODIANCHESS: if piece == KING and self.kings[ color] != self.ini_kings[color]: self.is_first_move[KING][color] = False if piece == QUEEN and cord != self.ini_queens[color]: self.is_first_move[QUEEN][color] = False cord += 1 if self.variant == FISCHERRANDOMCHESS: # Save ranks fo find outermost rooks # if KkQq was used in castling rights if r == 0: rank8 = rank elif r == 7: rank1 = rank # Parse active color field if colChr.lower() == "w": self.setColor(WHITE) else: self.setColor(BLACK) # Parse castling availability castling = 0 for char in castChr: if self.variant == FISCHERRANDOMCHESS: if char in reprFile: if char < reprCord[self.kings[BLACK]][0]: castling |= B_OOO self.ini_rooks[1][0] = reprFile.index(char) + 56 else: castling |= B_OO self.ini_rooks[1][1] = reprFile.index(char) + 56 elif char in [c.upper() for c in reprFile]: if char < reprCord[self.kings[WHITE]][0].upper(): castling |= W_OOO self.ini_rooks[0][0] = reprFile.index(char.lower()) else: castling |= W_OO self.ini_rooks[0][1] = reprFile.index(char.lower()) elif char == "K": castling |= W_OO self.ini_rooks[0][1] = rank1.rfind('R') elif char == "Q": castling |= W_OOO self.ini_rooks[0][0] = rank1.find('R') elif char == "k": castling |= B_OO self.ini_rooks[1][1] = rank8.rfind('r') + 56 elif char == "q": castling |= B_OOO self.ini_rooks[1][0] = rank8.find('r') + 56 else: if char == "K": castling |= W_OO elif char == "Q": castling |= W_OOO elif char == "k": castling |= B_OO elif char == "q": castling |= B_OOO if self.variant in (WILDCASTLECHESS, WILDCASTLESHUFFLECHESS, FISCHERRANDOMCHESS): self.ini_kings[WHITE] = self.kings[WHITE] self.ini_kings[BLACK] = self.kings[BLACK] if self.variant in (WILDCASTLECHESS, WILDCASTLESHUFFLECHESS): if self.ini_kings[WHITE] == D1 and self.ini_kings[BLACK] == D8: self.fin_kings = ([B1, F1], [B8, F8]) self.fin_rooks = ([C1, E1], [C8, E8]) elif self.ini_kings[WHITE] == D1: self.fin_kings = ([B1, F1], [C8, G8]) self.fin_rooks = ([C1, E1], [D8, F8]) elif self.ini_kings[BLACK] == D8: self.fin_kings = ([C1, G1], [B8, F8]) self.fin_rooks = ([D1, F1], [C8, E8]) else: self.fin_kings = ([C1, G1], [C8, G8]) self.fin_rooks = ([D1, F1], [D8, F8]) self.setCastling(castling) # Parse en passant target sqaure if epChr == "-": self.setEnpassant(None) else: self.setEnpassant(cordDic[epChr]) # Parse halfmove clock field if fiftyChr.isdigit(): self.fifty = int(fiftyChr) else: self.fifty = 0 # Parse fullmove number if moveNoChr.isdigit(): movenumber = max(int(moveNoChr), 1) * 2 - 2 if self.color == BLACK: movenumber += 1 self.plyCount = movenumber else: self.plyCount = 1 self.fen_was_applied = True