def test_gating_castle_at_rook_wOO(self): FEN = "r2qk2r/pppbbppp/4pn2/1N1p4/1n1P4/4PN2/PPPBBPPP/R2QK2R[heHE] w KQkq - 10 8" board = LBoard(SCHESS) board.applyFen(FEN) moves = set() for move in genAllMoves(board): moves.add(toAN(board, move)) print("--------") print(board) self.assertIn("h1e1h", moves) self.assertIn("h1e1e", moves) self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O/Hh1'))), 'h1e1h') self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O/Eh1'))), 'h1e1e') self.assertEqual(toSAN(board, newMove(H1, E1, HAWK_GATE_AT_ROOK)), "O-O/Hh1") self.assertEqual(toSAN(board, newMove(H1, E1, ELEPHANT_GATE_AT_ROOK)), "O-O/Eh1") fen = board.asFen() move = parseAN(board, "h1e1e") board.applyMove(move) print("h1e1e") print(board) self.assertEqual(placement(board.asFen()), "r2qk2r/pppbbppp/4pn2/1N1p4/1n1P4/4PN2/PPPBBPPP/R2Q1RKE[heH]") board.popMove() self.assertEqual(placement(board.asFen()), placement(fen))
def test_toSAN(self): """ Testing toSAN() with giveaway king move """ board = LBoard(GIVEAWAYCHESS) board.applyFen("4R3/8/7B/2P5/8/8/PP5k/6k1 b - - 0 28") self.assertEqual(toSAN(board, newMove(G1, G2)), "Kgg2") self.assertEqual(toSAN(board, newMove(H2, G2)), "Khg2")
def on_players_changed(game): # fill fools mate moves to players move queue p0 = game.players[0] p0.move_queue.put_nowait(Move(newMove(F2, F3))) p0.move_queue.put_nowait(Move(newMove(G2, G4))) p1 = gamemodel.players[1] p1.move_queue.put_nowait(Move(newMove(E7, E5))) p1.move_queue.put_nowait(Move(newMove(D8, H4)))
def on_players_changed(game): # fill fools mate moves to players move queue p0 = game.players[0] p0.move_queue.put_nowait(Move(newMove(F2, F3))) p0.move_queue.put_nowait(Move(newMove(G2, G4))) p1 = gamemodel.players[1] p1.move_queue.put_nowait(Move(newMove(E7, E5))) p1.move_queue.put_nowait(Move(newMove(D8, H4)))
def test_gating_san_move(self): board = LBoard(SCHESS) board.applyFen(SCHESSSTART) self.assertEqual(repr(Move.Move(parseSAN(board, 'Nf3/H'))), 'g1f3h') self.assertEqual(repr(Move.Move(parseSAN(board, 'Nf3/E'))), 'g1f3e') self.assertEqual(toSAN(board, newMove(G1, F3, HAWK_GATE)), "Nf3/H") self.assertEqual(toSAN(board, newMove(G1, F3, ELEPHANT_GATE)), "Nf3/E")
def test_gating_castle_at_rook_wOOO_SAN(self): FEN = "r2ek2r/5pp1/1pp1pnp1/2pp1h2/3P4/2P1PP2/PP1N2PP/R3K1HR/E w KQHEAkq - 2 16" board = LBoard(SCHESS) board.applyFen(FEN) self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O-O'))), 'e1c1') self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O-O/Ea1'))), 'a1e1e') self.assertEqual(repr(Move.Move(parseSAN(board, 'O-O-O/Ee1'))), 'e1c1e') self.assertEqual(toSAN(board, newMove(E1, C1, QUEEN_CASTLE)), "O-O-O") self.assertEqual(toSAN(board, newMove(A1, E1, ELEPHANT_GATE_AT_ROOK)), "O-O-O/Ea1") self.assertEqual(toSAN(board, newMove(E1, C1, ELEPHANT_GATE)), "O-O-O/Ee1")
def validate(self, cord0, cord1): if cord0 is None or cord1 is None: return False # prevent accidental NULL_MOVE creation if cord0 == cord1 and self.parent.variant.variant != SITTUYINCHESS: return False if self.getBoard()[cord0] is None: return False if self.parent.setup_position: # prevent moving pieces inside holding if (cord0.x < 0 or cord0.x > self.FILES - 1) and \ (cord1.x < 0 or cord1.x > self.FILES - 1): return False else: return True if cord1.x < 0 or cord1.x > self.FILES - 1: return False if cord0.x < 0 or cord0.x > self.FILES - 1: # drop return validate( self.getBoard(), Move( lmovegen.newMove(self.getBoard()[cord0].piece, cord1.cord, DROP))) else: return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def emit_move_signal (self, cord0, cord1, promotion=None): # Game end can change cord0 to None while dragging a piece if cord0 is None: return color = self.view.model.boards[-1].color board = self.view.model.getBoardAtPly(self.view.shown, self.view.shownVariationIdx) # Ask player for which piece to promote into. If this move does not # include a promotion, QUEEN will be sent as a dummy value, but not used if promotion is None and board[cord0].sign == PAWN and cord1.y in (0, self.RANKS-1): promotion = self.getPromotion() if promotion is None: # Put back pawn moved be d'n'd self.view.runAnimation(redrawMisc = False) return if cord0.x < 0 or cord0.x > self.FILES-1: move = Move(lmovegen.newMove(board[cord0].piece, cord1.cord, DROP)) else: move = Move(cord0, cord1, board, promotion) if self.view.model.curplayer.__type__ == LOCAL and self.view.shownIsMainLine() and \ self.view.model.boards[-1] == board and self.view.model.status == RUNNING: self.emit("piece_moved", move, color) else: if board.board.next is None and not self.view.shownIsMainLine(): self.view.model.add_move2variation(board, move, self.view.shownVariationIdx) self.view.shown += 1 else: new_vari = self.view.model.add_variation(board, (move,)) self.view.setShownBoard(new_vari[-1])
def parsePolyglot(board, pg): """ Parse a 16-bit Polyglot-format move """ tcord = TCORD(pg) fcord = FCORD(pg) flag = NORMAL_MOVE if pg >> 12: flag = FLAG_PIECE((pg >> 12) + 1) elif board.arBoard[fcord] == KING: if board.arBoard[tcord] == ROOK: color = board.color friends = board.friends[color] if bitPosArray[tcord] & friends: if board.ini_rooks[color][0] == tcord: flag = QUEEN_CASTLE if board.variant == NORMALCHESS: # Want e1c1/e8c8 tcord += 2 else: flag = KING_CASTLE if board.variant == NORMALCHESS: # Want e1g1/e8g8 tcord -= 1 elif board.arBoard[fcord] == PAWN and board.arBoard[tcord] == EMPTY and \ FILE(fcord) != FILE(tcord) and RANK(fcord) != RANK(tcord): flag = ENPASSANT return newMove(fcord, tcord, flag)
def row_activated(self, iter, model): if self.mode == HINT and self.store.get_path(iter) != Gtk.TreePath( self.path): moves = self.store[iter][0][2] if moves is not None: score = self.store[iter][1][0] model.add_variation(self.engine.board, moves, comment="", score=score) if self.mode == SPY and self.store.get_path(iter) != Gtk.TreePath( self.path): moves = self.store[iter][0][2] if moves is not None: score = self.store[iter][1][0] board = self.engine.board.board # SPY analyzer has inverted color boards # we need to chage it to get the board in gamemodel variations board list later board.setColor(1 - board.color) king = board.kings[board.color] null_move = Move(newMove(king, king, NULL_MOVE)) model.add_variation(self.engine.board, [null_move] + moves, comment="", score=score)
def sittuyin_promotion_fcord(board, tcord): valid_promotions = list(gen_sittuyin_promotions(board)) queenMoves = moveArray[ASEAN_QUEEN] for fcord in iterBits(queenMoves[tcord]): if board.arBoard[fcord] and board.arBoard[fcord] == PAWN: if newMove(fcord, tcord, QUEEN_PROMOTION) in valid_promotions: return fcord
def emit_move_signal(self, cord0, cord1, promotion=None): # Game end can change cord0 to None while dragging a piece if cord0 is None: return color = self.view.model.boards[-1].color board = self.view.model.getBoardAtPly(self.view.shown, self.view.shownVariationIdx) # Ask player for which piece to promote into. If this move does not # include a promotion, QUEEN will be sent as a dummy value, but not used if promotion is None and board[cord0].sign == PAWN and cord1.y in ( 0, self.RANKS - 1): promotion = self.getPromotion() if promotion is None: # Put back pawn moved be d'n'd self.view.runAnimation(redrawMisc=False) return if cord0.x < 0 or cord0.x > self.FILES - 1: move = Move(lmovegen.newMove(board[cord0].piece, cord1.cord, DROP)) else: move = Move(cord0, cord1, board, promotion) if self.view.model.curplayer.__type__ == LOCAL and self.view.shownIsMainLine() and \ self.view.model.boards[-1] == board and self.view.model.status == RUNNING: self.emit("piece_moved", move, color) else: if board.board.next is None and not self.view.shownIsMainLine(): self.view.model.add_move2variation(board, move, self.view.shownVariationIdx) self.view.shown += 1 else: new_vari = self.view.model.add_variation(board, (move, )) self.view.setShownBoard(new_vari[-1])
def validate(self, cord0, cord1): if cord0 is None or cord1 is None: return False # prevent accidental NULL_MOVE creation if cord0 == cord1: return False if self.getBoard()[cord0] is None: return False if self.parent.setup_position: # prevent moving pieces inside holding if (cord0.x < 0 or cord0.x > self.FILES - 1) and \ (cord1.x < 0 or cord1.x > self.FILES - 1): return False else: return True if cord1.x < 0 or cord1.x > self.FILES - 1: return False if cord0.x < 0 or cord0.x > self.FILES - 1: # drop return validate(self.getBoard(), Move(lmovegen.newMove( self.getBoard()[cord0].piece, cord1.cord, DROP))) else: return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def parsePolyglot (board, pg): """ Parse a 16-bit Polyglot-format move """ tcord = TCORD(pg) fcord = FCORD(pg) flag = NORMAL_MOVE if pg >> 12: flag = FLAG_PIECE( (pg >> 12) + 1 ) elif board.arBoard[fcord] == KING: if board.arBoard[tcord] == ROOK: color = board.color friends = board.friends[color] if bitPosArray[tcord] & friends: if board.ini_rooks[color][0] == tcord: flag = QUEEN_CASTLE if board.variant == NORMALCHESS: # Want e1c1/e8c8 tcord += 2 else: flag = KING_CASTLE if board.variant == NORMALCHESS: # Want e1g1/e8g8 tcord -= 1 elif board.arBoard[fcord] == PAWN and board.arBoard[tcord] == EMPTY and \ FILE(fcord) != FILE(tcord) and RANK(fcord) != RANK(tcord): flag = ENPASSANT return newMove (fcord, tcord, flag)
def validate (self, cord0, cord1): if cord0 is None or cord1 is None: return False # prevent accidental NULL_MOVE creation if cord0 == cord1: return False if self.getBoard()[cord0] == None: return False if self.parent.setup_position: to_piece = self.getBoard()[cord1] # prevent moving pieces inside holding if (cord0.x < 0 or cord0.x > self.FILES-1) and \ (cord1.x < 0 or cord1.x > self.FILES-1): return False # prevent moving kings off board elif self.getBoard()[cord0].piece == KING and \ (cord1.x < 0 or cord1.x > self.FILES-1): return False # prevent taking enemy king elif to_piece is not None and to_piece.piece == KING: return False else: return True if cord1.x < 0 or cord1.x > self.FILES-1: return False if cord0.x < 0 or cord0.x > self.FILES-1: # drop return validate(self.getBoard(), Move(lmovegen.newMove(self.getBoard()[cord0].piece, cord1.cord, DROP))) else: return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def row_activated(self, iter, model): if self.mode == HINT and self.store.get_path(iter) != Gtk.TreePath( self.path): moves = self.store[iter][0][2] if moves is not None: score = self.store[iter][1][0] model.add_variation(self.engine.board, moves, comment="", score=score) if self.mode == SPY and self.store.get_path(iter) != Gtk.TreePath( self.path): moves = self.store[iter][0][2] if moves is not None: score = self.store[iter][1][0] board = self.engine.board.board # SPY analyzer has inverted color boards # we need to chage it to get the board in gamemodel variations board list later board.setColor(1 - board.color) king = board.kings[board.color] null_move = Move(newMove(king, king, NULL_MOVE)) model.add_variation(self.engine.board, [null_move] + moves, comment="", score=score)
def __init__ (self, cord0, cord1=None, board=None, promotion=None): """ Inits a new highlevel Move object. The object can be initialized in the follow ways: Move(cord0, cord1, board, [promotionPiece]) Move(lovLevelMoveInt) """ if not cord1: self.move = cord0 self.flag = self.move >> 12 self.cord0 = None if self.flag == DROP else Cord(lmove.FCORD(self.move)) self.cord1 = Cord(lmove.TCORD(self.move)) else: assert cord0 != None and cord1 != None, "cord0=%s, cord1=%s, board=%s" % (cord0, cord1, board) assert board[cord0] != None, "cord0=%s, cord1=%s, board=%s" % (cord0, cord1, board) self.cord0 = cord0 self.cord1 = cord1 if not board: raise ValueError("Move needs a Board object in order to investigate flags") self.flag = NORMAL_MOVE if board[self.cord0].piece == PAWN and self.cord1.cord in board.PROMOTION_ZONE[board.board.color]: if promotion == None: if board.variant == SITTUYINCHESS: if cord0 == cord1: self.flag = lmove.FLAG_PIECE(QUEEN) else: self.flag = lmove.FLAG_PIECE(QUEEN) else: self.flag = lmove.FLAG_PIECE(promotion) elif board[self.cord0].piece == KING: if self.cord0 == self.cord1: self.flag = NULL_MOVE elif board.variant == FISCHERRANDOMCHESS: if (abs(self.cord0.x - self.cord1.x) > 1 and self.cord1.x==C1) or \ \ (board.board.ini_rooks[board.color][0] == self.cord1.cord and \ ((board.board.color == WHITE and board.board.castling & W_OOO) or \ (board.board.color == BLACK and board.board.castling & B_OOO))): self.flag = QUEEN_CASTLE elif (abs(self.cord0.x - self.cord1.x) > 1 and self.cord1.x==G1) or \ \ (board.board.ini_rooks[board.color][1] == self.cord1.cord and \ ((board.board.color == WHITE and board.board.castling & W_OO) or \ (board.board.color == BLACK and board.board.castling & B_OO))): self.flag = KING_CASTLE elif board.variant != CAMBODIANCHESS: if self.cord0.x - self.cord1.x == 2: self.flag = QUEEN_CASTLE if self.cord0.x == 4 else KING_CASTLE elif self.cord0.x - self.cord1.x == -2: self.flag = KING_CASTLE if self.cord0.x == 4 else QUEEN_CASTLE elif board[self.cord0].piece == PAWN and \ board[self.cord1] == None and \ self.cord0.x != self.cord1.x and \ self.cord0.y != self.cord1.y: self.flag = ENPASSANT self.move = newMove(self.cord0.cord, self.cord1.cord, self.flag)
def __init__(self, cord0, cord1=None, board=None, promotion=None): """ Inits a new highlevel Move object. The object can be initialized in the follow ways: Move(cord0, cord1, board, [promotionPiece]) Move(lovLevelMoveInt) """ if not cord1: self.move = cord0 self.flag = self.move >> 12 self.cord0 = None if self.flag == DROP else Cord( lmove.FCORD(self.move)) self.cord1 = Cord(lmove.TCORD(self.move)) else: assert cord0 != None and cord1 != None, "cord0=%s, cord1=%s, board=%s" % ( cord0, cord1, board) assert board[cord0] != None, "cord0=%s, cord1=%s, board=%s" % ( cord0, cord1, board) self.cord0 = cord0 self.cord1 = cord1 if not board: raise ValueError( "Move needs a Board object in order to investigate flags") self.flag = NORMAL_MOVE if board[self.cord0].piece == PAWN and self.cord1.y in (0, 7): if promotion == None: promotion = QUEEN self.flag = lmove.FLAG_PIECE(promotion) elif board[self.cord0].piece == KING: if self.cord0 == self.cord1: self.flag = NULL_MOVE elif board.variant == FISCHERRANDOMCHESS: if (abs(self.cord0.x - self.cord1.x) > 1 and self.cord1.x==C1) or \ \ (board.board.ini_rooks[board.color][0] == self.cord1.cord and \ ((board.board.color == WHITE and board.board.castling & W_OOO) or \ (board.board.color == BLACK and board.board.castling & B_OOO))): self.flag = QUEEN_CASTLE elif (abs(self.cord0.x - self.cord1.x) > 1 and self.cord1.x==G1) or \ \ (board.board.ini_rooks[board.color][1] == self.cord1.cord and \ ((board.board.color == WHITE and board.board.castling & W_OO) or \ (board.board.color == BLACK and board.board.castling & B_OO))): self.flag = KING_CASTLE else: if self.cord0.x - self.cord1.x == 2: self.flag = QUEEN_CASTLE if self.cord0.x == 4 else KING_CASTLE elif self.cord0.x - self.cord1.x == -2: self.flag = KING_CASTLE if self.cord0.x == 4 else QUEEN_CASTLE elif board[self.cord0].piece == PAWN and \ board[self.cord1] == None and \ self.cord0.x != self.cord1.x and \ self.cord0.y != self.cord1.y: self.flag = ENPASSANT self.move = newMove(self.cord0.cord, self.cord1.cord, self.flag)
def parseAN(board, an): """ Parse an Algebraic Notation string """ if not 4 <= len(an) <= 6: raise ParsingError(an, "the move must be 4 or 6 chars long", board.asFen()) try: fcord = cordDic[an[:2]] tcord = cordDic[an[2:4]] except KeyError as e: raise ParsingError(an, "the cord (%s) is incorrect" % e.args[0], board.asFen()) flag = NORMAL_MOVE if len(an) > 4 and not an[-1] in "QRBNMSFqrbnmsf": if (board.variant != SUICIDECHESS and board.variant != GIVEAWAYCHESS) or \ (board.variant == SUICIDECHESS or board.variant == GIVEAWAYCHESS) and not an[ -1] in "Kk": raise ParsingError(an, "invalid promoted piece", board.asFen()) if len(an) == 5: # The a7a8q variant flag = chr2Sign[an[4].lower()] + 2 elif len(an) == 6: # The a7a8=q variant flag = chr2Sign[an[5].lower()] + 2 elif board.arBoard[fcord] == KING: if fcord - tcord == 2: flag = QUEEN_CASTLE if board.variant == FISCHERRANDOMCHESS: tcord = board.ini_rooks[board.color][0] elif fcord - tcord == -2: flag = KING_CASTLE if board.variant == FISCHERRANDOMCHESS: tcord = board.ini_rooks[board.color][1] elif board.arBoard[ tcord] == ROOK: color = board.color friends = board.friends[color] if bitPosArray[tcord] & friends: if board.ini_rooks[color][0] == tcord: flag = QUEEN_CASTLE else: flag = KING_CASTLE else: flag = NORMAL_MOVE elif board.arBoard[fcord] == PAWN and board.arBoard[tcord] == EMPTY and \ FILE(fcord) != FILE(tcord) and RANK(fcord) != RANK(tcord): flag = ENPASSANT elif board.arBoard[fcord] == PAWN: if an[3] in "18" and board.variant != SITTUYINCHESS: raise ParsingError( an, _("promotion move without promoted piece is incorrect"), board.asFen()) return newMove(fcord, tcord, flag)
def on_book_created(persp, event): self.assertTrue(os.path.isfile(BIN)) testcase = testcases[0] board = LBoard(Board) board.applyFen(testcase[0]) openings = book.getOpenings(board) self.assertEqual( sorted(openings), sorted([(newMove(E2, E4), 2, 0), (newMove(A2, A4), 0, 0)])) testcase = testcases[-1] board = LBoard(Board) board.applyFen(testcase[0]) openings = book.getOpenings(board) self.assertEqual(openings, []) event.set()
def emit_move_signal(self, cord0, cord1, promotion=None): # Game end can change cord0 to None while dragging a piece if cord0 is None: return color = self.view.model.boards[-1].color board = self.view.model.getBoardAtPly(self.view.shown, self.view.shown_variation_idx) # Ask player for which piece to promote into. If this move does not # include a promotion, QUEEN will be sent as a dummy value, but not used if promotion is None and board[ cord0].sign == PAWN and cord1.cord in board.PROMOTION_ZONE[ color]: if self.variant.variant == SITTUYINCHESS: # no promotion allowed if we have queen if board.board.boards[color][QUEEN]: promotion = None else: # promotion is always optional promotion = self.getPromotion() if promotion is None and cord0 == cord1: # if don't want in place promotion return elif len(self.variant.PROMOTIONS) == 1: promotion = lmove.PROMOTE_PIECE(self.variant.PROMOTIONS[0]) else: if conf.get("autoPromote", False): promotion = lmove.PROMOTE_PIECE(QUEEN_PROMOTION) else: promotion = self.getPromotion() if promotion is None: # Put back pawn moved be d'n'd self.view.runAnimation(redraw_misc=False) return if cord0.x < 0 or cord0.x > self.FILES - 1: move = Move(lmovegen.newMove(board[cord0].piece, cord1.cord, DROP)) else: move = Move(cord0, cord1, board, promotion) if (self.view.model.curplayer.__type__ == LOCAL or self.view.model.examined) and \ self.view.shownIsMainLine() and \ self.view.model.boards[-1] == board and \ self.view.model.status == RUNNING: if self.setup_position: self.emit("piece_moved", (cord0, cord1), board[cord0].color) else: self.emit("piece_moved", move, color) if self.view.model.examined: self.view.model.connection.bm.sendMove(toAN(board, move)) else: if board.board.next is None and not self.view.shownIsMainLine(): self.view.model.add_move2variation( board, move, self.view.shown_variation_idx) self.view.shown += 1 else: new_vari = self.view.model.add_variation(board, (move, )) self.view.setShownBoard(new_vari[-1])
def emit_move_signal(self, cord0, cord1, promotion=None): # Game end can change cord0 to None while dragging a piece if cord0 is None: return color = self.view.model.boards[-1].color board = self.view.model.getBoardAtPly(self.view.shown, self.view.shown_variation_idx) # Ask player for which piece to promote into. If this move does not # include a promotion, QUEEN will be sent as a dummy value, but not used if promotion is None and board[ cord0].sign == PAWN and cord1.cord in board.PROMOTION_ZONE[ color]: if self.variant.variant == SITTUYINCHESS: # no promotion allowed if we have queen if board.board.boards[color][QUEEN]: promotion = None else: # promotion is always optional promotion = self.getPromotion() if promotion is None and cord0 == cord1: # if don't want in place promotion return elif len(self.variant.PROMOTIONS) == 1: promotion = lmove.PROMOTE_PIECE(self.variant.PROMOTIONS[0]) else: if conf.get("autoPromote", False): promotion = lmove.PROMOTE_PIECE(QUEEN_PROMOTION) else: promotion = self.getPromotion() if promotion is None: # Put back pawn moved be d'n'd self.view.runAnimation(redraw_misc=False) return if cord0.x < 0 or cord0.x > self.FILES - 1: move = Move(lmovegen.newMove(board[cord0].piece, cord1.cord, DROP)) else: move = Move(cord0, cord1, board, promotion) if (self.view.model.curplayer.__type__ == LOCAL or self.view.model.examined) and \ self.view.shownIsMainLine() and \ self.view.model.boards[-1] == board and \ self.view.model.status == RUNNING: if self.setup_position: self.emit("piece_moved", (cord0, cord1), board[cord0].color) else: self.emit("piece_moved", move, color) if self.view.model.examined: self.view.model.connection.bm.sendMove(toAN(board, move)) else: if board.board.next is None and not self.view.shownIsMainLine(): self.view.model.add_move2variation( board, move, self.view.shown_variation_idx) self.view.shown += 1 else: new_vari = self.view.model.add_variation(board, (move, )) self.view.setShownBoard(new_vari[-1])
def on_book_created(persp, event): self.assertTrue(os.path.isfile(BIN)) book.path = BIN book.bookfile = True testcase = testcases[0] board = LBoard(Board) board.applyFen(testcase[0]) openings = book.getOpenings(board) self.assertEqual(sorted(openings), sorted([(newMove(E2, E4), 2, 0), (newMove(A2, A4), 0, 0)])) testcase = testcases[-1] board = LBoard(Board) board.applyFen(testcase[0]) openings = book.getOpenings(board) self.assertEqual(openings, []) event.set()
def validate (self, cord0, cord1): assert cord0 != None and cord1 != None, "cord0: " + str(cord0) + ", cord1: " + str(cord1) if self.getBoard()[cord0] == None: return False if cord1.x < 0 or cord1.x > self.FILES-1: return False if cord0.x < 0 or cord0.x > self.FILES-1: # drop return validate(self.getBoard(), Move(lmovegen.newMove(self.getBoard()[cord0].piece, cord1.cord, DROP))) else: return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def emit_move_signal(self, cord0, cord1, promotion=None): # Game end can change cord0 to None while dragging a piece if cord0 is None: return board = self.getBoard() color = board.color # Ask player for which piece to promote into. If this move does not # include a promotion, QUEEN will be sent as a dummy value, but not used if promotion is None and board[cord0].sign == PAWN and \ cord1.cord in board.PROMOTION_ZONE[color] and \ self.variant.variant != SITTUYINCHESS: if len(self.variant.PROMOTIONS) == 1: promotion = lmove.PROMOTE_PIECE(self.variant.PROMOTIONS[0]) else: if conf.get("autoPromote"): promotion = lmove.PROMOTE_PIECE(QUEEN_PROMOTION) else: promotion = self.getPromotion() if promotion is None: # Put back pawn moved be d'n'd self.view.runAnimation(redraw_misc=False) return if promotion is None and board[cord0].sign == PAWN and \ cord0.cord in board.PROMOTION_ZONE[color] and \ self.variant.variant == SITTUYINCHESS: # no promotion allowed if we have queen if board.board.boards[color][QUEEN]: promotion = None # in place promotion elif cord1.cord in board.PROMOTION_ZONE[color]: promotion = lmove.PROMOTE_PIECE(QUEEN_PROMOTION) # queen move promotion (but not a pawn capture!) elif board[cord1] is None and (cord0.cord + cord1.cord) % 2 == 1: promotion = lmove.PROMOTE_PIECE(QUEEN_PROMOTION) if cord0.x < 0 or cord0.x > self.FILES - 1: move = Move(lmovegen.newMove(board[cord0].piece, cord1.cord, DROP)) else: move = Move(cord0, cord1, board, promotion) if (self.view.model.curplayer.__type__ == LOCAL or self.view.model.examined) and \ self.view.shownIsMainLine() and \ self.view.model.boards[-1] == board and \ self.view.model.status == RUNNING: # emit move if self.setup_position: self.emit("piece_moved", (cord0, cord1), board[cord0].color) else: self.emit("piece_moved", move, color) if self.view.model.examined: self.view.model.connection.bm.sendMove(toAN(board, move)) else: self.play_or_add_move(board, move)
def validate (self, cord0, cord1): if cord0 is None or cord1 is None: return False if self.getBoard()[cord0] == None: return False if cord1.x < 0 or cord1.x > self.FILES-1: return False if cord0.x < 0 or cord0.x > self.FILES-1: # drop return validate(self.getBoard(), Move(lmovegen.newMove(self.getBoard()[cord0].piece, cord1.cord, DROP))) else: return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def testFRCCastling(self): """Testing FRC castling movegen""" print() for fen, castles in data: print(fen) board = LBoard(FISCHERRANDOMCHESS) board.applyFen(fen) # print board moves = [move for move in genCastles(board)] self.assertEqual(len(moves), len(castles)) for i, castle in enumerate(castles): kfrom, kto, flag = castle self.assertEqual(moves[i], newMove(kfrom, kto, flag))
def testFRCCastling(self): """Testing FRC castling movegen""" print() for fen, castles in data: print(fen) board = LBoard(FISCHERRANDOMCHESS) board.applyFen(fen) # print board moves = [move for move in genCastles(board)] self.assertEqual(len(moves), len(castles)) for i, castle in enumerate(castles): kfrom, kto, flag = castle self.assertEqual(moves[i], newMove(kfrom, kto, flag))
def validate (self, cord0, cord1): if cord0 is None or cord1 is None: return False # prevent accidental NULL_MOVE creation if cord0 == cord1: return False if self.getBoard()[cord0] == None: return False if cord1.x < 0 or cord1.x > self.FILES-1: return False if cord0.x < 0 or cord0.x > self.FILES-1: # drop return validate(self.getBoard(), Move(lmovegen.newMove(self.getBoard()[cord0].piece, cord1.cord, DROP))) else: return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def validate(self, cord0, cord1): if cord0 is None or cord1 is None: return False if self.getBoard()[cord0] == None: return False if cord1.x < 0 or cord1.x > self.FILES - 1: return False if cord0.x < 0 or cord0.x > self.FILES - 1: # drop return validate( self.getBoard(), Move( lmovegen.newMove(self.getBoard()[cord0].piece, cord1.cord, DROP))) else: return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def validate(self, cord0, cord1): if cord0 is None or cord1 is None: return False # prevent accidental NULL_MOVE creation if cord0 == cord1: return False if self.getBoard()[cord0] == None: return False if cord1.x < 0 or cord1.x > self.FILES - 1: return False if cord0.x < 0 or cord0.x > self.FILES - 1: # drop return validate( self.getBoard(), Move( lmovegen.newMove(self.getBoard()[cord0].piece, cord1.cord, DROP))) else: return validate(self.getBoard(), Move(cord0, cord1, self.getBoard()))
def test_pdb_database_get_bitboards(self): """Testing .pdb database get_bitboards""" db = self.load_test_pgn() board = LBoard() board.applyFen(FEN_START) def get_bb_candidates(board): bb_candidates = {} for lmove in genAllMoves(board): board.applyMove(lmove) if board.opIsChecked(): board.popMove() continue bb_candidates[board.friends[0] | board.friends[1]] = lmove board.popMove() return bb_candidates # ply 1 bitboards = db.get_bitboards(1, get_bb_candidates(board)) print("==========") for row in bitboards: print(row) print("----------") self.assertEqual(len(bitboards), BITBOARD_COUNT[1]) self.assertEqual(sum([row[1] for row in bitboards]), GAME_COUNT) self.assertEqual(bitboards[0][5], 2712) # ply 2 board.applyMove(newMove(E2, E4)) bitboards = db.get_bitboards(2, get_bb_candidates(board)) print("==========") for row in bitboards: print(row) print("----------") self.assertEqual(len(bitboards), BITBOARD_COUNT[2]) self.assertEqual(sum([row[1] for row in bitboards]), GAME_COUNT)
def defencive_moves_tactic(model, ply, phase): # ------------------------------------------------------------------------ # # Test if we threat something, or at least put more pressure on it # # ------------------------------------------------------------------------ # # We set bishop value down to knight value, as it is what most people expect bishopBackup = PIECE_VALUES[BISHOP] PIECE_VALUES[BISHOP] = PIECE_VALUES[KNIGHT] board = model.getBoardAtPly(ply).board oldboard = model.getBoardAtPly(ply - 1).board move = model.getMoveAtPly(ply - 1).move fcord = FCORD(move) tcord = TCORD(move) found_threatens = [] found_increases = [] # What do we attack now? board.setColor(1 - board.color) for ncap in genCaptures(board): # getCaptures also generate promotions if FLAG(ncap) in PROMOTIONS: continue # We are only interested in the attacks of the piece we just moved if FCORD(ncap) != TCORD(move): continue # We don't want to move back if TCORD(ncap) == FCORD(move): continue # We don't thread the king. We check him! (in another function) if board.arBoard[TCORD(ncap)] == KING: continue # If we also was able to attack that cord last time, we don't care if validateMove(oldboard, newMove(FCORD(move), TCORD(ncap))): continue # Test if we threats our enemy, at least more than before see0 = staticExchangeEvaluate(oldboard, TCORD(ncap), 1 - oldboard.color) see1 = staticExchangeEvaluate(board, TCORD(ncap), 1 - oldboard.color) if see1 > see0: # If a new winning capture has been created if see1 > 0: # Find the easiest attack attacks = getAttacks(board, TCORD(ncap), board.color) v, cord = min((PIECE_VALUES[board.arBoard[fc]], fc) for fc in iterBits(attacks)) easiestAttack = newMove(cord, TCORD(ncap)) found_threatens.append(toSAN(board, easiestAttack, True)) # Even though we might not yet be strong enough, we might still # have strengthened another friendly attack else: found_increases.append(reprCord[TCORD(ncap)]) board.setColor(1 - board.color) # -------------------------------------------------------------------- # # Test if we defend a one of our pieces # # -------------------------------------------------------------------- # found_defends = [] # Test which pieces were under attack used = [] for ncap in genCaptures(board): # getCaptures also generate promotions if FLAG(ncap) in PROMOTIONS: continue # We don't want to know about the same cord more than once if TCORD(ncap) in used: continue used.append(TCORD(ncap)) # If the attack was poining on the piece we just moved, we ignore it if TCORD(ncap) == FCORD(move) or TCORD(ncap) == TCORD(move): continue # If we were already defending the piece, we don't send a new # message if defends(oldboard, FCORD(move), TCORD(ncap)): continue # If the attack was not strong, we ignore it see = staticExchangeEvaluate(oldboard, ncap) if see < 0: continue v = defends(board, TCORD(move), TCORD(ncap)) # If the defend didn't help, it doesn't matter. Like defending a # bishop, threatened by a pawn, with a queen. # But on the other hand - it might still be a defend... # newsee = staticExchangeEvaluate(board, ncap) # if newsee <= see: continue if v: found_defends.append(reprCord[TCORD(ncap)]) # ------------------------------------------------------------------------ # # Test if we are rescuing an otherwise exposed piece # # ------------------------------------------------------------------------ # # Rescuing is only an option, if our own move wasn't an attack if oldboard.arBoard[tcord] == EMPTY: see0 = staticExchangeEvaluate(oldboard, fcord, oldboard.color) see1 = staticExchangeEvaluate(board, tcord, oldboard.color) if see1 > see0 and see1 > 0: yield _("rescues a %s") % reprPiece[board.arBoard[tcord]].lower() if found_threatens: yield _("threatens to win material by %s") % join(found_threatens) if found_increases: yield _("increases the pressure on %s") % join(found_increases) if found_defends: yield _("defends %s") % join(found_defends) PIECE_VALUES[BISHOP] = bishopBackup
def on_game_started(game): p1 = gamemodel.players[1] p1.move_queue.put_nowait(Move(newMove(G8, F6)))
from pychess.compat import basestring from pychess.Utils.const import DROP from pychess.Utils.lutils.lmovegen import newMove MAXMOVE = newMove(63, 63, DROP) COMMENT, VARI_START, VARI_END, NAG = (MAXMOVE + i + 1 for i in range(4)) def walk(node, arr, txt): """Prepares a game data for databse. Recursively walks the node tree to collect moves and comments. Arguments: node - list (a tree of lboards created by the pgn parser) arr - array("H") (2 byte unsigned ints representing lmove objects or COMMENT, VARI_START, VARI_END, NAG+nag) txt - list (comment strings)""" arr_append = arr.append while True: if node is None: break # Initial game or variation comment if node.prev is None: for child in node.children: if isinstance(child, basestring): arr_append(COMMENT) txt.append(child) node = node.next continue
def emit_move_signal(self, cord0, cord1, promotion=None): # Game end can change cord0 to None while dragging a piece if cord0 is None: return gating = None board = self.getBoard() color = board.color # Ask player for which piece to promote into. If this move does not # include a promotion, QUEEN will be sent as a dummy value, but not used if promotion is None and board[cord0].sign == PAWN and \ cord1.cord in board.PROMOTION_ZONE[color] and \ self.variant.variant != SITTUYINCHESS: if len(self.variant.PROMOTIONS) == 1: promotion = lmove.PROMOTE_PIECE(self.variant.PROMOTIONS[0]) elif self.variant.variant == LIGHTBRIGADECHESS: promotion = lmove.PROMOTE_PIECE(QUEEN_PROMOTION if color == WHITE else KNIGHT_PROMOTION) else: if conf.get("autoPromote"): promotion = lmove.PROMOTE_PIECE(QUEEN_PROMOTION) else: promotion = self.getPromotion() if promotion is None: # Put back pawn moved be d'n'd self.view.runAnimation(redraw_misc=False) return if promotion is None and board[cord0].sign == PAWN and \ cord0.cord in board.PROMOTION_ZONE[color] and \ self.variant.variant == SITTUYINCHESS: # no promotion allowed if we have queen if board.board.boards[color][QUEEN]: promotion = None # in place promotion elif cord1.cord in board.PROMOTION_ZONE[color]: promotion = lmove.PROMOTE_PIECE(QUEEN_PROMOTION) # queen move promotion (but not a pawn capture!) elif board[cord1] is None and (cord0.cord + cord1.cord) % 2 == 1: promotion = lmove.PROMOTE_PIECE(QUEEN_PROMOTION) holding = board.board.holding[color] if self.variant.variant == SCHESS: moved = board[cord0].sign hawk = holding[HAWK] > 0 elephant = holding[ELEPHANT] > 0 if (hawk or elephant) and cord0.cord in iterBits( board.board.virgin[color]): castling = moved == KING and abs(cord0.x - cord1.x) == 2 gating = self.getGating(castling, hawk, elephant) if gating is not None: if gating in (HAWK_GATE_AT_ROOK, ELEPHANT_GATE_AT_ROOK): side = 0 if cord0.x - cord1.x == 2 else 1 rcord = board.board.ini_rooks[color][side] move = Move(lmovegen.newMove(rcord, cord0.cord, gating)) else: move = Move(lmovegen.newMove(cord0.cord, cord1.cord, gating)) elif cord0.x < 0 or cord0.x > self.FILES - 1: move = Move(lmovegen.newMove(board[cord0].piece, cord1.cord, DROP)) else: move = Move(cord0, cord1, board, promotion) if (self.view.model.curplayer.__type__ == LOCAL or self.view.model.examined) and \ self.view.shownIsMainLine() and \ self.view.model.boards[-1] == board and \ self.view.model.status == RUNNING: # emit move if self.setup_position: self.emit("piece_moved", (cord0, cord1), board[cord0].color) else: self.emit("piece_moved", move, color) if self.view.model.examined: self.view.model.connection.bm.sendMove(toAN(board, move)) else: self.play_or_add_move(board, move)
from pychess.Utils.const import DROP from pychess.Utils.lutils.lmovegen import newMove MAXMOVE = newMove(63, 63, DROP) COMMENT, VARI_START, VARI_END, NAG = (MAXMOVE + i + 1 for i in range(4)) def walk(node, arr, txt): """Prepares a game data for databse. Recursively walks the node tree to collect moves and comments. Arguments: node - list (a tree of lboards created by the pgn parser) arr - array("H") (2 byte unsigned ints representing lmove objects or COMMENT, VARI_START, VARI_END, NAG+nag) txt - list (comment strings)""" arr_append = arr.append while True: if node is None: break # Initial game or variation comment if node.prev is None: for child in node.children: if isinstance(child, str): arr_append(COMMENT) txt.append(child) node = node.next continue arr_append(node.lastMove)
def scoreAllMoves (self, board, probeSoft=False): global URL, expression, PROMOTION_FLAGS fen = board.asFen().split()[0] + " w - - 0 1" if (fen,board.color) in self.table: return self.table[(fen,board.color)] if probeSoft or not conf.get("online_egtb_check", True): return [] # Request the page url = (URL + fen).replace(" ", "%20") try: f = urlopen(url) except IOError as e: log.warning("Unable to read endgame tablebase from the Internet: %s" % repr(e)) return [] data = f.read() # Parse for color, move_data in enumerate(data.split("\nNEXTCOLOR\n")): try: moves = [] for fcord, tcord, promotion, result in expression.findall(move_data): fcord = int(fcord) tcord = int(tcord) if promotion: flag = PROMOTION_FLAGS[int(promotion)] elif RANK(fcord) != RANK(tcord) and FILE(fcord) != FILE(tcord) and \ board.arBoard[fcord] == PAWN and board.arBoard[tcord] == EMPTY: flag = ENPASSANT else: flag = NORMAL_MOVE move = newMove(fcord, tcord, flag) if result == "Draw": state = DRAW steps = 0 else: s, steps = result.split(" in ") steps = int(steps) if result.startswith("Win"): if color == WHITE: state = WHITEWON else: state = BLACKWON elif result.startswith("Lose"): if color == WHITE: state = BLACKWON else: state = WHITEWON moves.append( (move,state,steps) ) if moves: self.table[(fen,color)] = moves elif color == board.color and board.opIsChecked(): log.warning("Asked endgametable for a won position: %s" % fen) elif color == board.color: log.warning("Couldn't get %s data for position %s.\nData was: %s" % (reprColor[color], fen, repr(data))) except (KeyError, ValueError): log.warning("Couldn't parse %s data for position %s.\nData was: %s" % (reprColor[color], fen, repr(data))) self.table[(fen, color)] = [] # Don't try again. if (fen,board.color) in self.table: return self.table[(fen,board.color)] return []
def _get_move(from_coord, to_coord, board: Board) -> Move: move = Move(newMove(from_coord, to_coord)) move = Move(move.cord0, move.cord1, board) return move
def on_game_started(game): p1 = gamemodel.players[1] p1.move_queue.put_nowait(Move(newMove(G8, F6)))
def parseSAN (board, san): """ Parse a Short/Abbreviated Algebraic Notation string """ notat = san color = board.color if notat == "--": return newMove(board.kings[color], board.kings[color], NULL_MOVE) if notat[-1] in ("+", "#"): notat = notat[:-1] # If '++' was used in place of # if notat[-1] == "+": notat = notat[:-1] flag = NORMAL_MOVE # If last char is a piece char, we assue it the promote char c = notat[-1] if c in ("K", "Q", "R", "B", "N", "k", "q", "r", "b", "n"): c = c.lower() if c == "k" and board.variant != SUICIDECHESS: raise ParsingError, (san, "invalid promoted piece", board.asFen()) flag = chr2Sign[c] + 2 if notat[-2] == "=": notat = notat[:-2] else: notat = notat[:-1] if len(notat) < 2: raise ParsingError, (san, _("the move needs a piece and a cord"), board.asFen()) if notat[0] in "O0o": fcord = board.ini_kings[color] flag = KING_CASTLE if notat == "O-O" or notat == "0-0" or notat == "o-o" else QUEEN_CASTLE side = flag -QUEEN_CASTLE if FILE(fcord) == 3 and board.variant in (WILDCASTLECHESS, WILDCASTLESHUFFLECHESS): side = 0 if side == 1 else 1 if board.variant == FISCHERRANDOMCHESS: tcord = board.ini_rooks[color][side] else: tcord = board.fin_kings[color][side] return newMove (fcord, tcord, flag) # LAN is not allowed in pgn spec, but sometimes it occures if "-" in notat: notat = notat.replace("-", "") if "@" in notat: tcord = cordDic[notat[-2:]] if notat[0].islower(): # Sjeng-ism piece = chr2Sign[notat[0]] else: piece = chrU2Sign[notat[0]] return newMove(piece, tcord, DROP) if notat[0] in ("Q", "R", "B", "K", "N"): piece = chrU2Sign[notat[0]] notat = notat[1:] else: piece = PAWN if notat[-1] in ("1", "8") and flag == NORMAL_MOVE: raise ParsingError, ( san, _("promotion move without promoted piece is incorrect"), board.asFen()) if "x" in notat: notat, tcord = notat.split("x") if not tcord in cordDic: raise ParsingError, ( san, _("the captured cord (%s) is incorrect") % tcord, board.asFen()) tcord = cordDic[tcord] if piece == PAWN: # If a pawn is attacking an empty cord, we assue it an enpassant if board.arBoard[tcord] == EMPTY: flag = ENPASSANT else: if not notat[-2:] in cordDic: raise ParsingError, ( san, "the end cord (%s) is incorrect" % notat[-2:], board.asFen()) tcord = cordDic[notat[-2:]] notat = notat[:-2] # In suicide promoting to king is valid, so # more than 1 king per side can exist ! if board.variant != SUICIDECHESS and piece == KING: return newMove(board.kings[color], tcord, flag) # If there is any extra location info, like in the move Bexd1 or Nh3f4 we # want to know frank = None ffile = None if notat and notat[0] in reprRank: frank = int(notat[0])-1 notat = notat[1:] if notat and notat[0] in reprFile: ffile = ord(notat[0]) - ord("a") notat = notat[1:] if notat and notat[0] in reprRank: frank = int(notat[0])-1 notat = notat[1:] # we know all we want return newMove(frank*8+ffile, tcord, flag) if piece == PAWN: if (ffile is not None) and ffile != FILE(tcord): # capture if color == WHITE: fcord = tcord-7 if ffile > FILE(tcord) else tcord-9 else: fcord = tcord+7 if ffile < FILE(tcord) else tcord+9 else: if color == WHITE: pawns = board.boards[WHITE][PAWN] fcord = tcord-16 if RANK(tcord)==3 and not (pawns & fileBits[FILE(tcord)] & rankBits[2]) else tcord-8 else: pawns = board.boards[BLACK][PAWN] fcord = tcord+16 if RANK(tcord)==4 and not (pawns & fileBits[FILE(tcord)] & rankBits[5]) else tcord+8 return newMove(fcord, tcord, flag) else: if board.pieceCount[color][piece] == 1: # we have only one from this kind if piece, so: fcord = firstBit(board.boards[color][piece]) return newMove(fcord, tcord, flag) else: # We find all pieces who could have done it. (If san was legal, there should # never be more than one) moves = genPieceMoves(board, piece, tcord) if len(moves) == 1: return moves.pop() else: for move in moves: f = FCORD(move) if frank != None and frank != RANK(f): continue if ffile != None and ffile != FILE(f): continue board_clone = board.clone() board_clone.applyMove(move) if board_clone.opIsChecked(): continue return move errstring = "no %s is able to move to %s" % (reprPiece[piece], reprCord[tcord]) raise ParsingError, (san, errstring, board.asFen())
flag = KING_CASTLE elif fcord - tcord == 2: flag = QUEEN_CASTLE elif fcord - tcord == -2: flag = KING_CASTLE else: flag = NORMAL_MOVE elif board.arBoard[fcord] == PAWN and board.arBoard[tcord] == EMPTY and \ FILE(fcord) != FILE(tcord) and RANK(fcord) != RANK(tcord): flag = ENPASSANT elif board.arBoard[fcord] == PAWN and an[3] in ("1", "8"): raise ParsingError, ( an, _("promotion move without promoted piece is incorrect"), board.asFen()) return newMove (fcord, tcord, flag) ################################################################################ # toFAN # ################################################################################ san2WhiteFanDic = { ord(u"K"): FAN_PIECES[WHITE][KING], ord(u"Q"): FAN_PIECES[WHITE][QUEEN], ord(u"R"): FAN_PIECES[WHITE][ROOK], ord(u"B"): FAN_PIECES[WHITE][BISHOP], ord(u"N"): FAN_PIECES[WHITE][KNIGHT], ord(u"+"): u"†", ord(u"#"): u"‡" }
from pychess.compat import basestring from pychess.Utils.const import NULL_MOVE from pychess.Utils.lutils.lmovegen import newMove MAXMOVE = newMove(63, 63, NULL_MOVE) COMMENT, VARI_START, VARI_END, NAG = (MAXMOVE + i + 1 for i in range(4)) def walk(node, arr, txt): """Prepares a game data for databse. Recursively walks the node tree to collect moves and comments. Arguments: node - list (a tree of lboards created by the pgn parser) arr - array("H") (2 byte unsigned ints representing lmove objects or COMMENT, VARI_START, VARI_END, NAG+nag) txt - list (comment strings)""" arr_append = arr.append while True: if node is None: break # Initial game or variation comment if node.prev is None: for child in node.children: if isinstance(child, basestring): arr_append(COMMENT) txt.append(child) node = node.next continue
def parseSAN(board, san): """ Parse a Short/Abbreviated Algebraic Notation string """ notat = san color = board.color if notat == "--": return newMove(board.kings[color], board.kings[color], NULL_MOVE) if notat[-1] in "+#": notat = notat[:-1] # If '++' was used in place of # if notat[-1] == "+": notat = notat[:-1] flag = NORMAL_MOVE # If last char is a piece char, we assue it the promote char c = notat[-1] if c in "KQRBNSMFkqrbnsmf.": c = c.lower() if c == "k" and board.variant != SUICIDECHESS and board.variant != GIVEAWAYCHESS: raise ParsingError(san, _("invalid promoted piece"), board.asFen()) elif c == "." and board.variant in (CAMBODIANCHESS, MAKRUKCHESS, SITTUYINCHESS): # temporary hack for xboard bug flag = QUEEN_PROMOTION else: flag = chr2Sign[c] + 2 if notat[-2] == "=": notat = notat[:-2] else: notat = notat[:-1] if len(notat) < 2: raise ParsingError(san, _("the move needs a piece and a cord"), board.asFen()) if notat[0] in "O0o": fcord = board.ini_kings[color] flag = KING_CASTLE if notat in ("O-O", "0-0", "o-o", "OO", "00", "oo") else QUEEN_CASTLE side = flag - QUEEN_CASTLE if FILE(fcord) == 3 and board.variant in (WILDCASTLECHESS, WILDCASTLESHUFFLECHESS): side = 0 if side == 1 else 1 if board.variant == FISCHERRANDOMCHESS: tcord = board.ini_rooks[color][side] else: tcord = board.fin_kings[color][side] return newMove(fcord, tcord, flag) # LAN is not allowed in pgn spec, but sometimes it occures if "-" in notat: notat = notat.replace("-", "") if "@" in notat: tcord = cordDic[notat[-2:]] if notat[0].islower(): # Sjeng-ism piece = chr2Sign[notat[0]] else: piece = chrU2Sign[notat[0]] return newMove(piece, tcord, DROP) # standard piece letters if notat[0] in "QRBKNSMF": piece = chrU2Sign[notat[0]] notat = notat[1:] # unambigious lowercase piece letters elif notat[0] in "qrknsm": piece = chr2Sign[notat[0]] notat = notat[1:] # a lowercase bishop letter or a pawn capture elif notat[0] == "b" and len(notat) > 2 and board.variant == NORMALCHESS: tcord = cordDic[notat[-2:]] trank = int(notat[-1]) # if from and to lines are not neighbours -> Bishop if abs(ord(notat[0]) - ord(notat[-2])) > 1: piece = chr2Sign[notat[0]] notat = notat[1:] # if from and to lines are neighbours (or the same) but to is an empty square # which can't be en-passant square target -> Bishop elif board.arBoard[tcord] == EMPTY and ((color == BLACK and trank != 3) or (color == WHITE and trank != 6)): piece = chr2Sign[notat[0]] notat = notat[1:] # elif "ba3", "bc3" ,"ba6", "bc6" # these can be Bishop or Pawn moves, but we don't try to introspect them (sorry) else: piece = PAWN else: piece = PAWN if notat[-1] in "18" and flag == NORMAL_MOVE and board.variant != SITTUYINCHESS: raise ParsingError( san, _("promotion move without promoted piece is incorrect"), board.asFen()) if "x" in notat: notat, tcord = notat.split("x") if tcord not in cordDic: raise ParsingError(san, _("the captured cord (%s) is incorrect") % tcord, board.asFen()) tcord = cordDic[tcord] if piece == PAWN: # If a pawn is attacking an empty cord, we assue it an enpassant if board.arBoard[tcord] == EMPTY: if (color == BLACK and 2 * 8 <= tcord < 3 * 8) or (color == WHITE and 5 * 8 <= tcord < 6 * 8): flag = ENPASSANT else: raise ParsingError( san, _("pawn capture without target piece is invalid"), board.asFen()) else: if not notat[-2:] in cordDic: raise ParsingError(san, _("the end cord (%s) is incorrect") % notat[-2:], board.asFen()) tcord = cordDic[notat[-2:]] notat = notat[:-2] # In suicide promoting to king is valid, so # more than 1 king per side can exist ! if board.variant != SUICIDECHESS and board.variant != GIVEAWAYCHESS and piece == KING: return newMove(board.kings[color], tcord, flag) # If there is any extra location info, like in the move Bexd1 or Nh3f4 we # want to know frank = None ffile = None if notat and notat[0] in reprRank: frank = int(notat[0]) - 1 notat = notat[1:] if notat and notat[0] in reprFile: ffile = ord(notat[0]) - ord("a") notat = notat[1:] if notat and notat[0] in reprRank: frank = int(notat[0]) - 1 notat = notat[1:] # we know all we want return newMove(frank * 8 + ffile, tcord, flag) if piece == PAWN: if (ffile is not None) and ffile != FILE(tcord): # capture if color == WHITE: fcord = tcord - 7 if ffile > FILE(tcord) else tcord - 9 else: fcord = tcord + 7 if ffile < FILE(tcord) else tcord + 9 else: if color == WHITE: pawns = board.boards[WHITE][PAWN] # In horde white pawns on first rank may move two squares also if board.variant == HORDECHESS and RANK(tcord) == 2 and not ( pawns & fileBits[FILE(tcord)] & rankBits[1]): fcord = tcord - 16 else: fcord = tcord - 16 if RANK(tcord) == 3 and not ( pawns & fileBits[FILE(tcord)] & rankBits[2]) else tcord - 8 else: pawns = board.boards[BLACK][PAWN] fcord = tcord + 16 if RANK(tcord) == 4 and not ( pawns & fileBits[FILE(tcord)] & rankBits[5]) else tcord + 8 if board.variant == SITTUYINCHESS and flag == QUEEN_PROMOTION and \ (pawns & fileBits[FILE(tcord)] & rankBits[RANK(tcord)]): return newMove(tcord, tcord, flag) return newMove(fcord, tcord, flag) else: if board.pieceCount[color][piece] == 1: # we have only one from this kind if piece, so: fcord = firstBit(board.boards[color][piece]) return newMove(fcord, tcord, flag) else: # We find all pieces who could have done it. (If san was legal, there should # never be more than one) moves = genPieceMoves(board, piece, tcord) if len(moves) == 1: return moves.pop() else: for move in moves: f = FCORD(move) if frank is not None and frank != RANK(f): continue if ffile is not None and ffile != FILE(f): continue board_clone = board.clone() board_clone.applyMove(move) if board_clone.opIsChecked(): continue return move errstring = "no %s is able to move to %s" % (reprPiece[piece], reprCord[tcord]) raise ParsingError(san, errstring, board.asFen())
def parseSAN(board, san): """ Parse a Short/Abbreviated Algebraic Notation string """ notat = san color = board.color if notat == "--": return newMove(board.kings[color], board.kings[color], NULL_MOVE) if notat[-1] in "+#": notat = notat[:-1] # If '++' was used in place of # if notat[-1] == "+": notat = notat[:-1] flag = NORMAL_MOVE # If last char is a piece char, we assue it the promote char c = notat[-1] if c in "KQRBNSMFkqrbnsmf.": c = c.lower() if c == "k" and board.variant != SUICIDECHESS and board.variant != GIVEAWAYCHESS: raise ParsingError(san, _("invalid promoted piece"), board.asFen()) elif c == ".": if board.variant in (CAMBODIANCHESS, MAKRUKCHESS, SITTUYINCHESS): # temporary hack for xboard bug flag = QUEEN_PROMOTION else: raise ParsingError(san, "invalid san", board.asFen()) else: flag = chr2Sign[c] + 2 if notat[-2] == "=": notat = notat[:-2] else: notat = notat[:-1] if len(notat) < 2: raise ParsingError(san, _("the move needs a piece and a cord"), board.asFen()) if notat[0] in "O0o": fcord = board.ini_kings[color] flag = KING_CASTLE if notat in ("O-O", "0-0", "o-o", "OO", "00", "oo") else QUEEN_CASTLE side = flag - QUEEN_CASTLE if FILE(fcord) == 3 and board.variant in (WILDCASTLECHESS, WILDCASTLESHUFFLECHESS): side = 0 if side == 1 else 1 if board.variant == FISCHERRANDOMCHESS: tcord = board.ini_rooks[color][side] else: tcord = board.fin_kings[color][side] return newMove(fcord, tcord, flag) # LAN is not allowed in pgn spec, but sometimes it occures if "-" in notat: notat = notat.replace("-", "") if "@" in notat: tcord = cordDic[notat[-2:]] if notat[0].islower(): # Sjeng-ism piece = chr2Sign[notat[0]] else: piece = chrU2Sign[notat[0]] return newMove(piece, tcord, DROP) # standard piece letters if notat[0] in "QRBKNSMF": piece = chrU2Sign[notat[0]] notat = notat[1:] # unambigious lowercase piece letters elif notat[0] in "qrknsm": piece = chr2Sign[notat[0]] notat = notat[1:] # a lowercase bishop letter or a pawn capture elif notat[0] == "b" and len(notat) > 2 and board.variant == NORMALCHESS: tcord = cordDic[notat[-2:]] trank = int(notat[-1]) # if from and to lines are not neighbours -> Bishop if abs(ord(notat[0]) - ord(notat[-2])) > 1: piece = chr2Sign[notat[0]] notat = notat[1:] # if from and to lines are neighbours (or the same) but to is an empty square # which can't be en-passant square target -> Bishop elif board.arBoard[tcord] == EMPTY and ( (color == BLACK and trank != 3) or (color == WHITE and trank != 6)): piece = chr2Sign[notat[0]] notat = notat[1:] # elif "ba3", "bc3" ,"ba6", "bc6" # these can be Bishop or Pawn moves, but we don't try to introspect them (sorry) else: piece = PAWN else: piece = PAWN if notat[ -1] in "18" and flag == NORMAL_MOVE and board.variant != SITTUYINCHESS: flag = QUEEN_PROMOTION if "x" in notat: notat, tcord = notat.split("x") if tcord not in cordDic: raise ParsingError( san, _("the captured cord (%s) is incorrect") % tcord, board.asFen()) tcord = cordDic[tcord] if piece == PAWN: # If a pawn is attacking an empty cord, we assue it an enpassant if board.arBoard[tcord] == EMPTY: if (color == BLACK and 2 * 8 <= tcord < 3 * 8) or ( color == WHITE and 5 * 8 <= tcord < 6 * 8): flag = ENPASSANT else: raise ParsingError( san, _("pawn capture without target piece is invalid"), board.asFen()) else: if not notat[-2:] in cordDic: raise ParsingError( san, _("the end cord (%s) is incorrect") % notat[-2:], board.asFen()) tcord = cordDic[notat[-2:]] notat = notat[:-2] # In suicide promoting to king is valid, so # more than 1 king per side can exist ! if board.variant != SUICIDECHESS and board.variant != GIVEAWAYCHESS and piece == KING: return newMove(board.kings[color], tcord, flag) # If there is any extra location info, like in the move Bexd1 or Nh3f4 we # want to know frank = None ffile = None if notat and notat[0] in reprRank: frank = int(notat[0]) - 1 notat = notat[1:] if notat and notat[0] in reprFile: ffile = ord(notat[0]) - ord("a") notat = notat[1:] if notat and notat[0] in reprRank: frank = int(notat[0]) - 1 notat = notat[1:] # we know all we want return newMove(frank * 8 + ffile, tcord, flag) if piece == PAWN: if (ffile is not None) and ffile != FILE(tcord): # capture if color == WHITE: fcord = tcord - 7 if ffile > FILE(tcord) else tcord - 9 else: fcord = tcord + 7 if ffile < FILE(tcord) else tcord + 9 else: if color == WHITE: pawns = board.boards[WHITE][PAWN] # In horde white pawns on first rank may move two squares also if board.variant == HORDECHESS and RANK(tcord) == 2 and not ( pawns & fileBits[FILE(tcord)] & rankBits[1]): fcord = tcord - 16 else: fcord = tcord - 16 if RANK(tcord) == 3 and not ( pawns & fileBits[FILE(tcord)] & rankBits[2]) else tcord - 8 else: pawns = board.boards[BLACK][PAWN] fcord = tcord + 16 if RANK(tcord) == 4 and not ( pawns & fileBits[FILE(tcord)] & rankBits[5]) else tcord + 8 if board.variant == SITTUYINCHESS and flag == QUEEN_PROMOTION: if pawns & fileBits[FILE(tcord)] & rankBits[RANK(tcord)]: # in place promotion return newMove(tcord, tcord, flag) else: # queen move promotion (fcord have to be the closest cord of promotion zone) fcord = sittuyin_promotion_fcord(board, tcord) return newMove(fcord, tcord, flag) return newMove(fcord, tcord, flag) else: if board.pieceCount[color][piece] == 1: # we have only one from this kind if piece, so: fcord = firstBit(board.boards[color][piece]) return newMove(fcord, tcord, flag) else: # We find all pieces who could have done it. (If san was legal, there should # never be more than one) moves = genPieceMoves(board, piece, tcord) if len(moves) == 1: return moves.pop() else: for move in moves: f = FCORD(move) if frank is not None and frank != RANK(f): continue if ffile is not None and ffile != FILE(f): continue board_clone = board.clone() board_clone.applyMove(move) if board_clone.opIsChecked(): continue return move errstring = _("no %(piece)s is able to move to %(cord)s") % { "piece": reprPiece[piece], "cord": reprCord[tcord] } raise ParsingError(san, errstring, board.asFen())
def __init__(self, cord0, cord1=None, board=None, promotion=None): """ Inits a new highlevel Move object. The object can be initialized in the follow ways: Move(cord0, cord1, board, [promotionPiece]) Move(lovLevelMoveInt) """ if not cord1: self.move = cord0 self.flag = self.move >> 12 self.cord0 = None if self.flag == DROP else Cord(lmove.FCORD( self.move)) self.cord1 = Cord(lmove.TCORD(self.move)) else: assert cord0 is not None and cord1 is not None, "cord0=%s, cord1=%s, board=%s" % ( cord0, cord1, board) assert board[cord0] is not None, "cord0=%s, cord1=%s, board=%s" % ( cord0, cord1, board) self.cord0 = cord0 self.cord1 = cord1 if not board: raise ValueError( "Move needs a Board object in order to investigate flags") self.flag = NORMAL_MOVE if board[self.cord0].piece == PAWN and \ self.cord1.cord in board.PROMOTION_ZONE[board.board.color] and \ board.variant != SITTUYINCHESS: if promotion is None: self.flag = lmove.FLAG_PIECE(QUEEN) else: self.flag = lmove.FLAG_PIECE(promotion) elif board[self.cord0].piece == PAWN and board.variant == SITTUYINCHESS: if cord0 == cord1: # in place promotion self.flag = lmove.FLAG_PIECE(QUEEN) elif board[self.cord1] is None and \ (self.cord0.cord + self.cord1.cord) % 2 == 1 and \ (self.cord0.cord in board.PROMOTION_ZONE[board.board.color] or board.board.pieceCount[board.color][PAWN] == 1): # queen move promotion self.flag = lmove.FLAG_PIECE(QUEEN) elif board[self.cord0].piece == KING: if self.cord0 == self.cord1: self.flag = NULL_MOVE if self.cord0.x - self.cord1.x == 2 and board.variant not in (CAMBODIANCHESS, FISCHERRANDOMCHESS): self.flag = QUEEN_CASTLE if self.cord0.x == 4 else KING_CASTLE elif self.cord0.x - self.cord1.x == -2 and board.variant not in (CAMBODIANCHESS, FISCHERRANDOMCHESS): self.flag = KING_CASTLE if self.cord0.x == 4 else QUEEN_CASTLE else: if (abs(self.cord0.x - self.cord1.x) > 1 and self.cord1.x == C1) or ( board.board.ini_rooks[board.color][0] == self.cord1.cord and ( (board.board.color == WHITE and board.board.castling & W_OOO) or ( board.board.color == BLACK and board.board.castling & B_OOO))): self.flag = QUEEN_CASTLE elif (abs(self.cord0.x - self.cord1.x) > 1 and self.cord1.x == G1) or ( board.board.ini_rooks[board.color][1] == self.cord1.cord and ( (board.board.color == WHITE and board.board.castling & W_OO) or ( board.board.color == BLACK and board.board.castling & B_OO))): self.flag = KING_CASTLE elif board[self.cord0].piece == PAWN and \ board[self.cord1] is None and \ self.cord0.x != self.cord1.x and \ self.cord0.y != self.cord1.y: self.flag = ENPASSANT self.move = newMove(self.cord0.cord, self.cord1.cord, self.flag)
def scoreAllMoves(self, board, probeSoft=False): global URL, expression, PROMOTION_FLAGS fen = board.asFen().split()[0] + " w - - 0 1" if (fen, board.color) in self.table: return self.table[(fen, board.color)] if probeSoft or not conf.get("online_egtb_check"): return [] def get_data(URL, fen): # Request the page url = (URL + fen).replace(" ", "%20") try: f = urlopen(url) except IOError as e: log.warning( "Unable to read endgame tablebase from the Internet: %s" % repr(e)) data = b"" data = f.read() return data loop = asyncio.get_event_loop() future = loop.run_in_executor(None, get_data, URL, fen) data = yield from future # Parse for color, move_data in enumerate(data.split(b"\nNEXTCOLOR\n")): try: moves = [] for fcord, tcord, promotion, result in expression.findall( move_data.decode()): fcord = int(fcord) tcord = int(tcord) if promotion: flag = PROMOTION_FLAGS[int(promotion)] elif RANK(fcord) != RANK(tcord) and FILE(fcord) != FILE(tcord) and \ board.arBoard[fcord] == PAWN and board.arBoard[tcord] == EMPTY: flag = ENPASSANT else: flag = NORMAL_MOVE move = newMove(fcord, tcord, flag) if result == "Draw": state = DRAW steps = 0 else: s, steps = result.split(" in ") steps = int(steps) if result.startswith("Win"): if color == WHITE: state = WHITEWON else: state = BLACKWON elif result.startswith("Lose"): if color == WHITE: state = BLACKWON else: state = WHITEWON moves.append((move, state, steps)) if moves: self.table[(fen, color)] = moves elif color == board.color and board.opIsChecked(): log.warning("Asked endgametable for a won position: %s" % fen) elif color == board.color: log.warning( "Couldn't get %s data for position %s.\nData was: %s" % (reprColor[color], fen, repr(data))) except (KeyError, ValueError): log.warning( "Couldn't parse %s data for position %s.\nData was: %s" % (reprColor[color], fen, repr(data))) self.table[(fen, color)] = [] # Don't try again. if (fen, board.color) in self.table: return self.table[(fen, board.color)] return []
def defencive_moves_tactic(model, ply, phase): # ------------------------------------------------------------------------ # # Test if we threat something, or at least put more pressure on it # # ------------------------------------------------------------------------ # # We set bishop value down to knight value, as it is what most people expect bishopBackup = PIECE_VALUES[BISHOP] PIECE_VALUES[BISHOP] = PIECE_VALUES[KNIGHT] board = model.getBoardAtPly(ply).board oldboard = model.getBoardAtPly(ply - 1).board move = model.getMoveAtPly(ply - 1).move fcord = FCORD(move) tcord = TCORD(move) found_threatens = [] found_increases = [] # What do we attack now? board.setColor(1 - board.color) for ncap in genCaptures(board): # getCaptures also generate promotions if FLAG(ncap) in PROMOTIONS: continue # We are only interested in the attacks of the piece we just moved if FCORD(ncap) != TCORD(move): continue # We don't want to move back if TCORD(ncap) == FCORD(move): continue # We don't thread the king. We check him! (in another function) if board.arBoard[TCORD(ncap)] == KING: continue # If we also was able to attack that cord last time, we don't care if validateMove(oldboard, newMove(FCORD(move), TCORD(ncap))): continue # Test if we threats our enemy, at least more than before see0 = staticExchangeEvaluate(oldboard, TCORD(ncap), 1 - oldboard.color) see1 = staticExchangeEvaluate(board, TCORD(ncap), 1 - oldboard.color) if see1 > see0: # If a new winning capture has been created if see1 > 0: # Find the easiest attack attacks = getAttacks(board, TCORD(ncap), board.color) v, cord = min((PIECE_VALUES[board.arBoard[fc]], fc) for fc in iterBits(attacks)) easiestAttack = newMove(cord, TCORD(ncap)) found_threatens.append(toSAN(board, easiestAttack, True)) # Even though we might not yet be strong enough, we might still # have strengthened another friendly attack else: found_increases.append(reprCord[TCORD(ncap)]) board.setColor(1 - board.color) # -------------------------------------------------------------------- # # Test if we defend a one of our pieces # # -------------------------------------------------------------------- # found_defends = [] # Test which pieces were under attack used = [] for ncap in genCaptures(board): # getCaptures also generate promotions if FLAG(ncap) in PROMOTIONS: continue # We don't want to know about the same cord more than once if TCORD(ncap) in used: continue used.append(TCORD(ncap)) # If the attack was poining on the piece we just moved, we ignore it if TCORD(ncap) == FCORD(move) or TCORD(ncap) == TCORD(move): continue # If we were already defending the piece, we don't send a new # message if defends(oldboard, FCORD(move), TCORD(ncap)): continue # If the attack was not strong, we ignore it see = staticExchangeEvaluate(oldboard, ncap) if see < 0: continue v = defends(board, TCORD(move), TCORD(ncap)) # If the defend didn't help, it doesn't matter. Like defending a # bishop, threatened by a pawn, with a queen. # But on the other hand - it might still be a defend... # newsee = staticExchangeEvaluate(board, ncap) # if newsee <= see: continue if v: found_defends.append(reprCord[TCORD(ncap)]) # ------------------------------------------------------------------------ # # Test if we are rescuing an otherwise exposed piece # # ------------------------------------------------------------------------ # # Rescuing is only an option, if our own move wasn't an attack if oldboard.arBoard[tcord] == EMPTY: see0 = staticExchangeEvaluate(oldboard, fcord, oldboard.color) see1 = staticExchangeEvaluate(board, tcord, oldboard.color) if see1 > see0 and see1 > 0: yield _("rescues a %s") % reprPiece[board.arBoard[tcord]].lower() if found_threatens: yield _("threatens to win material by %s") % join(found_threatens) if found_increases: yield _("increases the pressure on %s") % join(found_increases) if found_defends: yield _("defends %s") % join(found_defends) PIECE_VALUES[BISHOP] = bishopBackup
def parseSAN(board, san, full=True): """ Parse a Short/Abbreviated Algebraic Notation string """ notat = san color = board.color if notat == "--": return newMove(board.kings[color], board.kings[color], NULL_MOVE) if notat[-1] in "+#": notat = notat[:-1] # If '++' was used in place of # if notat[-1] == "+": notat = notat[:-1] flag = NORMAL_MOVE # If last char is a piece char, we assue it the promote char c = notat[-1] if c in "KQRBNSMFkqrbnsmf.": c = c.lower() if c == "k" and board.variant != SUICIDECHESS: raise ParsingError(san, _("invalid promoted piece"), board.asFen()) elif c == "." and board.variant in (CAMBODIANCHESS, MAKRUKCHESS, SITTUYINCHESS): # temporary hack for xboard bug flag = QUEEN_PROMOTION else: flag = chr2Sign[c] + 2 if notat[-2] == "=": notat = notat[:-2] else: notat = notat[:-1] if len(notat) < 2: raise ParsingError(san, _("the move needs a piece and a cord"), board.asFen()) if notat[0] in "O0o": fcord = board.ini_kings[color] flag = KING_CASTLE if notat == "O-O" or notat == "0-0" or notat == "o-o" else QUEEN_CASTLE side = flag - QUEEN_CASTLE if FILE(fcord) == 3 and board.variant in (WILDCASTLECHESS, WILDCASTLESHUFFLECHESS): side = 0 if side == 1 else 1 if board.variant == FISCHERRANDOMCHESS: tcord = board.ini_rooks[color][side] else: tcord = board.fin_kings[color][side] return newMove(fcord, tcord, flag) # LAN is not allowed in pgn spec, but sometimes it occures if "-" in notat: notat = notat.replace("-", "") if "@" in notat: tcord = cordDic[notat[-2:]] if notat[0].islower(): # Sjeng-ism piece = chr2Sign[notat[0]] else: piece = chrU2Sign[notat[0]] return newMove(piece, tcord, DROP) if notat[0] in "QRBKNSMF": piece = chrU2Sign[notat[0]] notat = notat[1:] else: piece = PAWN if notat[ -1] in "18" and flag == NORMAL_MOVE and board.variant != SITTUYINCHESS: raise ParsingError( san, _("promotion move without promoted piece is incorrect"), board.asFen()) if "x" in notat: notat, tcord = notat.split("x") if tcord not in cordDic: raise ParsingError( san, _("the captured cord (%s) is incorrect") % tcord, board.asFen()) tcord = cordDic[tcord] if piece == PAWN: # If a pawn is attacking an empty cord, we assue it an enpassant if board.arBoard[tcord] == EMPTY: if (color == BLACK and 2 * 8 <= tcord < 3 * 8) or ( color == WHITE and 5 * 8 <= tcord < 6 * 8): flag = ENPASSANT else: raise ParsingError( san, _("pawn capture without target piece is invalid"), board.asFen()) else: if not notat[-2:] in cordDic: raise ParsingError( san, _("the end cord (%s) is incorrect") % notat[-2:], board.asFen()) tcord = cordDic[notat[-2:]] notat = notat[:-2] # In suicide promoting to king is valid, so # more than 1 king per side can exist ! if board.variant != SUICIDECHESS and piece == KING: return newMove(board.kings[color], tcord, flag) # If there is any extra location info, like in the move Bexd1 or Nh3f4 we # want to know frank = None ffile = None if notat and notat[0] in reprRank: frank = int(notat[0]) - 1 notat = notat[1:] if notat and notat[0] in reprFile: ffile = ord(notat[0]) - ord("a") notat = notat[1:] if notat and notat[0] in reprRank: frank = int(notat[0]) - 1 notat = notat[1:] # we know all we want return newMove(frank * 8 + ffile, tcord, flag) if piece == PAWN: if (ffile is not None) and ffile != FILE(tcord): # capture if color == WHITE: fcord = tcord - 7 if ffile > FILE(tcord) else tcord - 9 else: fcord = tcord + 7 if ffile < FILE(tcord) else tcord + 9 else: if color == WHITE: pawns = board.boards[WHITE][PAWN] fcord = tcord - 16 if RANK(tcord) == 3 and not ( pawns & fileBits[FILE(tcord)] & rankBits[2]) else tcord - 8 else: pawns = board.boards[BLACK][PAWN] fcord = tcord + 16 if RANK(tcord) == 4 and not ( pawns & fileBits[FILE(tcord)] & rankBits[5]) else tcord + 8 if board.variant == SITTUYINCHESS and flag == QUEEN_PROMOTION and \ (pawns & fileBits[FILE(tcord)] & rankBits[RANK(tcord)]): return newMove(tcord, tcord, flag) return newMove(fcord, tcord, flag) else: if board.pieceCount[color][piece] == 1: # we have only one from this kind if piece, so: fcord = firstBit(board.boards[color][piece]) return newMove(fcord, tcord, flag) else: # We find all pieces who could have done it. (If san was legal, there should # never be more than one) moves = genPieceMoves(board, piece, tcord) if len(moves) == 1: return moves.pop() else: for move in moves: f = FCORD(move) if frank is not None and frank != RANK(f): continue if ffile is not None and ffile != FILE(f): continue board_clone = board.clone(full) board_clone.applyMove(move, full) if board_clone.opIsChecked(): continue return move errstring = "no %s is able to move to %s" % (reprPiece[piece], reprCord[tcord]) raise ParsingError(san, errstring, board.asFen())