Example #1
0
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
Example #2
0
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
Example #3
0
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)
Example #4
0
    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)
Example #6
0
    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)
Example #7
0
    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)
Example #8
0
    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()
Example #9
0
    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
Example #10
0
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)
Example #11
0
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
Example #12
0
    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
Example #14
0
    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)
Example #15
0
    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
Example #16
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)