def decode_tcn(tcn_string: str) -> list[Move]: moves: list[Move] = [] for i in range(0, len(tcn_string), 2): from_value_index: int = TCN_INDEX_STRING.index(tcn_string[i]) to_value_index: int = TCN_INDEX_STRING.index(tcn_string[i + 1]) promotion: Optional[PieceType] = None drop: Optional[PieceType] = None if to_value_index > 63: promotion = PIECE_SYMBOLS.index( TCN_PIECE_INDEX_STRING[(to_value_index - 64) // 3]) to_value_index = from_value_index + ( -8 if from_value_index < 16 else 8) + (to_value_index - 1) % 3 - 1 if from_value_index > 75: drop = PIECE_SYMBOLS.index( TCN_PIECE_INDEX_STRING[from_value_index - 79]) moves.append( Move(from_square=from_value_index, to_square=to_value_index, promotion=promotion, drop=drop)) return moves
def parse(): char_dict = { 'a':0, 'b':1, 'c':2, 'd':3, 'e':4, 'f':5, 'g':6, 'h':7, } int_dict = { '8':0, '7':1, '6':2, '5':3, '4':4, '3':5, '2':6, '1':7 } with open("Adams.txt") as f: content = f.readlines() long_string = "".join(content).replace("\n"," ") long_string = re.split("\d\/?\d?-\d\/?\d?",long_string) re_prog = re.compile("[a-z]\d\-[a-z]\d") output = [] for ls in long_string: z = re_prog.findall(ls) output.append(z) print(len(output)) new_games = [[Move(int_dict[move[1]],char_dict[move[0]],int_dict[move[4]],char_dict[move[3]]) for move in game] for game in output] return new_games
def play(): # TODO: get player move from GET request object # TODO: if there is no player move, render the page template if request.method == 'POST': move_str = request.form["move"] try: start, end = game.split_input(move_str) move = Move(start, end) except InputError: ui.errmsg = 'Invalid move format, please try again' return render_template('chess.html', ui=ui) movetype = game.movetype(start, end) if movetype is None: ui.errmsg = 'Invalid move for the piece, please try again' return render_template('chess.html', ui=ui) move.storepiece(game) movehis.push(move) game.update(move.tuple()) coord = game.check_for_promotion() if coord is not None: return redirect('/promote') game.next_turn() ui.board = game.display() ui.inputlabel = f'{game.turn} player: ' ui.errmsg = None if game.winner != None: ui.inputlabel = game.winner return render_template('win.html', ui=ui) return render_template('chess.html', ui=ui)
def test_take_move_diff(self): '''Test take move deduction''' board_1 = BlindBoard.from_dict({ chess.E2: WHITE, chess.F2: BLACK, }) board_2 = BlindBoard.from_dict({ chess.F2: WHITE, }) diff = BlindBoard.diff_board(board_2, board_1) move = core.diffreader.read(diff) expected_move = Move(chess.E2, chess.F2) nose.tools.eq_(move, expected_move)
def get_move_from_model(self, move): starting_square = move // (7 * 8 + 8 + 9) _type = move % (7 * 8 + 8 + 9) promotion = None # Sliding move if _type < 56: direction = _type // 7 distance = (_type % 7) + 1 directions = [-1, 1, -7, 7, -8, 8, -9, 9] direction = directions[direction] ending_square = starting_square + distance * direction # Direction should be 1 if and only if the squares are on the same rank if ((square_rank(starting_square) == square_rank(ending_square)) != (abs(direction) == 1)): return None # Direction should be 8 if and only if the squares are on the same file if ((square_file(starting_square) == square_file(ending_square)) != abs(direction) == 8): return None # If promoting, promote as queen if ((self.pawns & self.occupied_co[self.turn] & BB_SQUARES[starting_square]) and ((self.turn and starting_square // 8 == 6) or (not self.turn and starting_square // 8 == 1))): promotion = 5 # Knight move elif _type >= 56 and _type < 64: knight_moves = [17, 15, 10, 6, -17, -15, -10, -6] ending_square = starting_square + knight_moves[_type - 56] if (square_distance(starting_square, ending_square) != 2): return None # Underpromotion else: _type = _type - 64 direction = _type // 3 promotion = (_type % 3) + 2 directions = [7, 8, 9] direction = directions[direction] if not self.turn: direction *= -1 ending_square = starting_square + direction if ending_square < 0 or ending_square > 63: return None else: return Move(starting_square, ending_square, promotion=promotion)
def test_queen_castling_move_diff(self): '''Test queen castling move deduction''' board_1 = BlindBoard.from_dict({ chess.E8: BLACK, chess.A8: BLACK, }) board_2 = BlindBoard.from_dict({ chess.D8: BLACK, chess.C8: BLACK, }) diff = BlindBoard.diff_board(board_2, board_1) move = core.diffreader.read(diff) expected_move = Move(chess.E8, chess.C8) nose.tools.eq_(move, expected_move)
def test_king_castling_move_diff(self): '''Test king castling move deduction''' board_1 = BlindBoard.from_dict({ chess.H1: WHITE, chess.E1: WHITE, }) board_2 = BlindBoard.from_dict({ chess.F1: WHITE, chess.G1: WHITE, }) diff = BlindBoard.diff_board(board_2, board_1) move = core.diffreader.read(diff) expected_move = Move(chess.E1, chess.G1) nose.tools.eq_(move, expected_move)
def read_move(self): index = self.proc.expect( [self.Messages.RE_REGULAR_MOVE, pexpect.TIMEOUT, pexpect.EOF], timeout=self.DEFAULT_TIMEOUT) if index == 0: # it's a match match = self.proc.match from_square = match.group('from_square').decode() to_square = match.group('to_square').decode() color = BLACK if match.group('black_move') else WHITE move_count = int(match.group('move_count')) utils.log.debug("read {} move from gnuchess '{}: {}{}'".format( color, move_count, from_square, to_square)) return Move(from_square, to_square, move_count, color) elif index in [1, 2]: # TODO: throw exception instead self.raise_error()
def mouse_release(self, event): """ Called when mouse is released, Stop dragging piece and ask if the move is legal """ global drag_sq if drag_sq != -1: # dst_sq = (event.y // sq_size) * 8+ (event.x // sq_size) dst_sq = self.coord_to_sq((event.x, event.y)) m = Move(drag_sq, dst_sq) m.set_from_user() # this is input from user (not file) if not self.on_move_piece(m): # Withdraw the piece to original spot obj = self.piece_objs[drag_sq] self.canvas.coords(obj, self.sq_to_coord(drag_sq)) # ((drag_sq%8)*sq_size, (drag_sq//8)*sq_size)) drag_sq = -1 return
def play(): if request.method == 'POST': player_move = request.form['player_input'] # TODO: Validate move, display error msg if move is invalid try: start, end = split_and_convert(player_move) capturedcoords = (end[0], end[1]) encapped_move = Move(start, end, game.get_piece(capturedcoords)) game.update(encapped_move.start, encapped_move.end) # Removing the error statement after a valid input ui.errmsg = None except: ui.errmsg = 'Invalid move, try again.' return render_template('chess.html', ui=ui) else: game.next_turn() movehistory.push(encapped_move) ui.board = game.display() ui.inputlabel = f'{game.turn} player: ' return render_template('chess.html', ui=ui)
def read(blind_board_diff): # aliases utils.log.debug("reading diff: {}".format(blind_board_diff)) # sanity check: make sure this diff is not too odd... _diff_sanity_check(blind_board_diff) # check for castling move castling_move = _read_castling(blind_board_diff) if castling_move: utils.log.debug('read castling move {}'.format(castling_move)) return castling_move # TODO check for promotion move # TODO check for 'en passant' # OK, so this oughta be a simple move or a take: print out some debug # message if _diff_is_simple_move(blind_board_diff): utils.log.debug('diff is simple move') elif _diff_is_take(blind_board_diff): utils.log.debug('diff is take move') else: utils.log.error('unreadable diff move') # get the square the move came from, the square it goes to, create the # appropriate Move object and return it from_square = utils.singleton_get(blind_board_diff.get_emptied()) if _diff_is_take(blind_board_diff): to_square = utils.singleton_get(blind_board_diff.get_changed()) else: # diff is simple move to_square = utils.singleton_get(blind_board_diff.get_filled()) move = Move(from_square, to_square) utils.log.debug('read move {}'.format(move)) return move
def _onTokenRelease(self, event): #This function is called when the left mouse button is released c1 = round((event.y - round(self.size / 2)) / self.size) r1 = round((event.x - round(self.size / 2)) / self.size) #Records the destination square location, with respect to the mouse release location event self._moveData["c1"] = c1 self._moveData["r1"] = r1 #Initialises the tkinter move with the move tkinterMove = str(self._moveData["c0"]) + str( self._moveData["r0"]) + str(self._moveData["c1"]) + str( self._moveData["r1"]) #Moves the piece from the original square to the destination square Move(tkinterMove) #Refreshes the board so the user can see the move self._refreshBoard() #Resets the move data self._moveData["c0"] = 0 self._moveData["r0"] = 0 #Resets the cursor style root.config(cursor="arrow")
def __promotion(self, state: State) -> int: step = 8 stop = 64 if not state.board.turn: step = -8 stop = -1 h = 0 for pawn in state.board.pieces(PAWN, state.board.turn): from_square = pawn # Pawn path path = list(range(pawn + step, stop, step)) path_len = len(path) # Is path short enough if path_len > state.moves_left: continue # Is path clear & not move in check is_possible = True for to_square in path: if state.board.piece_at(to_square): is_possible = False break if state.board.is_into_check(Move(from_square, to_square)): is_possible = False break from_square = to_square if not is_possible: continue h -= state.moves_left - path_len return h
def generate_moves(self, from_mask=BB_ALL, to_mask=BB_ALL): our_pieces = self.occupied_co[self.turn] # Generate piece moves. non_pawns = our_pieces & ~self.pawns & from_mask for from_square in scan_reversed(non_pawns): moves = self.attacks_mask(from_square) & ~our_pieces & to_mask for to_square in scan_reversed(moves): yield Move(from_square, to_square) # Generate castling moves. if from_mask & self.kings: yield from self.generate_castling_moves(from_mask, to_mask) # The remaining moves are all pawn moves. pawns = self.pawns & self.occupied_co[self.turn] & from_mask if not pawns: return # Generate pawn captures. capturers = pawns for from_square in scan_reversed(capturers): # All pawn captures are now pseudo-legal targets = (BB_PAWN_ATTACKS[self.turn][from_square] & to_mask) for to_square in scan_reversed(targets): if square_rank(to_square) in [0, 7]: yield Move(from_square, to_square, QUEEN) yield Move(from_square, to_square, ROOK) yield Move(from_square, to_square, BISHOP) yield Move(from_square, to_square, KNIGHT) else: yield Move(from_square, to_square) # Prepare pawn advance generation. if self.turn == WHITE: single_moves = pawns << 8 & ~self.occupied double_moves = single_moves << 8 & ~self.occupied & (BB_RANK_3 | BB_RANK_4) else: single_moves = pawns >> 8 & ~self.occupied double_moves = single_moves >> 8 & ~self.occupied & (BB_RANK_6 | BB_RANK_5) single_moves &= to_mask double_moves &= to_mask # Generate single pawn moves. for to_square in scan_reversed(single_moves): from_square = to_square + (8 if self.turn == BLACK else -8) if square_rank(to_square) in [0, 7]: yield Move(from_square, to_square, QUEEN) yield Move(from_square, to_square, ROOK) yield Move(from_square, to_square, BISHOP) yield Move(from_square, to_square, KNIGHT) else: yield Move(from_square, to_square) # Generate double pawn moves. for to_square in scan_reversed(double_moves): from_square = to_square + (16 if self.turn == BLACK else -16) yield Move(from_square, to_square)
def push(self, move): backrank = BB_RANK_1 if self.turn == WHITE else BB_RANK_8 is_legal = True dest_sq = move.to_square clear_squares = 0 # Observations that are made as a result of this move are encoded by this var observation = np.zeros((8, 8, 13), dtype='float32') # TODO: Add observation from failed castling # TODO: Add observation from sliding pawn move # Castling is legal if the squares between the king and rook are empty if self.is_kingside_castling(move): cols = BB_FILE_F | BB_FILE_G squares = cols & backrank is_legal = (squares & self.occupied) == 0 elif self.is_queenside_castling(move): cols = BB_FILE_B | BB_FILE_C | BB_FILE_D squares = cols & backrank is_legal = (squares & self.occupied) == 0 elif BB_SQUARES[move.from_square] & self.pawns: # Pawn moves that are straight need to go to empty squares if move.from_square % 8 == move.to_square % 8: is_legal = (BB_SQUARES[move.to_square] & self.occupied) == 0 # Pawn moves that are diagonal need to be captures (accounts for ep) else: is_legal = self.is_capture(move) elif (BB_SQUARES[move.from_square] & (self.bishops | self.rooks | self.queens)): # Returns the new destination and a mask for all squares that were revealed to be empty dest_sq, clear_squares = self.adjust_sliding_move( move.from_square, move.to_square) true_move = Move(move.from_square, dest_sq, promotion=move.promotion) if not is_legal: true_move = Move.null() # The square the pawn is moving to is empty if BB_SQUARES[move.from_square] & self.pawns: observation[square_rank(move.to_square)][square_file( move.to_square)][12] = 1 capture = None if true_move != Move.null(): # Updates visible board. Moves pieces from from_square to to_square # Special case for promotion and castling needed visible = self.visible_state[self.turn] if true_move.promotion is None: visible.set_piece_at(true_move.to_square, visible.piece_at(true_move.from_square)) else: visible._set_piece_at(true_move.to_square, true_move.promotion, self.turn, True) visible.remove_piece_at(true_move.from_square) if self.is_castling(move): if self.is_kingside_castling(move): rook_from = H1 if self.turn else H8 rook_to = F1 if self.turn else F8 else: rook_from = A1 if self.turn else A8 rook_to = D1 if self.turn else D8 visible.set_piece_at(rook_to, visible.piece_at(rook_from)) visible.remove_piece_at(rook_from) capture = self.is_capture(true_move) # Update our visible board to be empty on any squares we moved through if clear_squares: observation = self.get_current_state(self.turn, clear_squares) from_rank = square_rank(true_move.from_square) from_file = square_file(true_move.from_square) to_rank = square_rank(true_move.to_square) to_file = square_file(true_move.to_square) if capture: # If you capture something, update your opponent's visibility self.visible_state[not self.turn].remove_piece_at( true_move.to_square) for i in range(6, 12): # We observe a -1 for all their pieces on that square observation[to_rank][to_file][i] = -1 # Our opponent observes a 1 for all our pieces on that square self.observation[not self.turn][to_rank][to_file][i] = 1 self.observation[self.turn] = observation super().push(true_move) # Return reward for move if true_move == Move.null(): return -5 elif capture: return 5 else: return 0
from chess import Move from chessboard import BlindBoard import utils class IllegalMove(Exception): pass # list of blindboard diffs for castling moves, and the corresponding moves (as # encoded in UCI) CASTLING_DIFFS = [ # king-side (BlindBoard.Diff(chess.BB_E1 | chess.BB_H1, chess.BB_F1 | chess.BB_G1, 0), Move(chess.E1, chess.G1)), (BlindBoard.Diff(chess.BB_E8 | chess.BB_H8, chess.BB_F8 | chess.BB_G8, 0), Move(chess.E8, chess.G8)), # queen-side (BlindBoard.Diff(chess.BB_E1 | chess.BB_A1, chess.BB_C1 | chess.BB_D1, 0), Move(chess.E1, chess.C1)), (BlindBoard.Diff(chess.BB_E8 | chess.BB_A8, chess.BB_C8 | chess.BB_D8, 0), Move(chess.E8, chess.C8)) ] class DiffLength: CASTLING = (2, 2, 0) TAKE = (1, 0, 1) SIMPLE = (1, 1, 0)