Exemple #1
0
def search_point(
        board: ChessBoard, ai_num: int, player: int, depth: int, alpha: int, beta: int
):
    """
    This function use alpha-beta pruning to calculate best-fit point
    :param board: chessboard
    :param ai_num: The player number which ai is
    :param player: current player number
    :param depth: maximum depth of calculation
    :param alpha: max value when calculate
    :param beta: min value when calculate
    :return: the maximum score of the position
    """
    # Computer - Human
    split_board = list(board.split_board())
    v = board.evaluate(ai_num, split_board) - board.evaluate(
        1 if ai_num == 2 else 2, split_board
    )
    if depth <= 0 or board.win_determine() in [WHITE_WIN, BLACK_WIN, TIE]:
        return v
    points = points_gen(board, player)

    if player == ai_num:
        # Computer turn, max level
        for point in points:
            board.board[point[0]][point[1]] = player
            cur_v = search_point(
                board, ai_num, 1 if player == 2 else 2, depth - 1, alpha, beta
            )
            board.board[point[0]][point[1]] = 0
            alpha = max(alpha, cur_v)
            # Prune
            if beta < alpha:
                # print("Pruned {} nodes".format(len(points) - points.index(point) - 1))
                break
        return alpha
    else:
        # Human turn, min level
        for point in points:
            board.board[point[0]][point[1]] = player
            cur_v = search_point(
                board, ai_num, 1 if player == 2 else 2, depth - 1, alpha, beta
            )
            board.board[point[0]][point[1]] = 0
            beta = min(beta, cur_v)
            # Prune
            if beta < alpha:
                print("Pruned {} nodes".format(len(points) - points.index(point) - 1))
                break
        return beta
Exemple #2
0
class ChessBoardInterface:
    def __init__(self, pack_label: bool = False):
        self.chessBoard = ChessBoard("p", "p")
        self.root = Tk()
        self.root.resizable(width=FALSE, height=FALSE)
        self.root.title("GoBang")

        self.button_freeze = False

        # Total chessboard
        self.boardFrame = Frame(self.root)

        # Menu
        self._init_menu()

        # Create canvas
        self.gap = 25
        self.mainBoard = Canvas(self.boardFrame,
                                width=self.gap * 16,
                                height=self.gap * 16)  # size: 400 * 400
        self._draw_lines()

        # Bind mouse action
        self.mainBoard.bind("<Button-1>", self.mouse_click)

        # Generate all positions
        self.all_positions = {(x, y)
                              for x in range(self.gap, self.gap * 16, self.gap)
                              for y in range(self.gap, self.gap * 16, self.gap)
                              }
        self.operate = []
        # print(self.all_positions)

        # Create control frame
        self.controlFrame = Labelframe(self.root, text="Control Panel")
        self.turn = Label(self.controlFrame, text="Next player: black")
        self.turn.pack(pady=10)
        # Evaluate part
        self.score1 = Label(self.controlFrame, text="Black: 0")
        self.score2 = Label(self.controlFrame, text="White: 0")
        self.evaluateButton = Button(self.controlFrame,
                                     text="Evaluate",
                                     command=self.button_evaluate)
        # AI part
        self.aiCalculateButton = Button(self.controlFrame,
                                        text="AI Calculate (beta)",
                                        command=self.ai_calculate)
        self.score1.pack()
        self.score2.pack()
        # self.evaluateButton.pack()
        self.aiCalculateButton.pack(pady=10)

        # Create Label frames
        self.horLabel = Frame(self.boardFrame)
        self.verLabel = Frame(self.boardFrame)
        self._draw_label()

        # Pack Widgets
        if pack_label:
            self.horLabel.grid(row=0, column=1, pady=0)
            self.verLabel.grid(row=1, column=0, padx=0)
        self.mainBoard.grid(row=1, column=1, padx=0, pady=0)
        self.boardFrame.grid()
        # Some bugs here, cannot click canvas after click button....
        self.controlFrame.grid(row=0, column=1, padx=5)

    def ai_calculate(self):
        """
        Call min_max search in ai.py to find the best place to set chess
        """
        if self.button_freeze:
            return
        print("Perform AI Evaluate!", end=" ")
        start_time = time.time()
        self.chessBoard.freeze = True
        self.button_freeze = True

        bar = Bar(len(points_gen(self.chessBoard, self.chessBoard.next_turn)))
        position = min_max_search(self.chessBoard,
                                  self.chessBoard.next_turn,
                                  bar,
                                  depth=2)

        print(position)
        position = self.convert_coordinate(position[0], position[1])
        temp_coo = namedtuple("Coordinate", ["x", "y"])
        print("Used time: {}".format(round(time.time() - start_time, 3)))

        self.chessBoard.freeze = False
        self.button_freeze = False
        self.mouse_click(temp_coo(position[0], position[1]))

    def button_evaluate(self):
        """
        Evaluate and score the situation on chessboard for both players
        """
        if self.button_freeze:
            return
        print("Perform evaluate_point!")
        split = list(self.chessBoard.split_board())
        self.score1["text"] = "Black: {}".format(
            self.chessBoard.evaluate(1, split))
        self.score2["text"] = "White: {}".format(
            self.chessBoard.evaluate(2, split))
        self.root.update()

    def mouse_click(self, click):
        x, y = self._nearest_position(click)
        converted_coo = self.convert_coordinate(x, y)

        # Calculate is the place already has a chess
        if self.chessBoard.board[converted_coo[0]][converted_coo[1]] != 0:
            return

        # If the board is frozen...
        if self.chessBoard.freeze:
            return

        # Draw chess
        chess_size = self.gap * 2 // 5
        if self.chessBoard.next_turn == 1:
            color = "Black"
        else:
            color = "White"
        new_position = self.convert_coordinate(x, y)
        print("{} chess at position({}, {})".format(color, new_position[0],
                                                    new_position[1]))
        self.operate.append(
            self.mainBoard.create_oval(
                x - chess_size,
                y - chess_size,
                x + chess_size,
                y + chess_size,
                fill=color,
                tag="{} {}".format(x, y),
            ))
        self.mainBoard.update()

        # Update Chessboard
        result = self.chessBoard.set_chess(converted_coo[0], converted_coo[1])
        # If the game is ended
        if result == TIE:
            print("Tie!")
            self.chessBoard.freeze = True
            ok_window = OkWindow(TIE)
            self.root.wait_window(ok_window.root)
            print("Restart: {}".format(ok_window.final_restart))
            # Restart game
            if ok_window.final_restart:
                self.restart_game()

        elif result != CONTINUE:
            if result == BLACK_WIN:
                winner = "Black"
            else:
                winner = "White"
            print("{} won!".format(winner))
            self.chessBoard.freeze = True
            ok_window = OkWindow(winner)
            self.root.wait_window(ok_window.root)
            print("Restart: {}".format(ok_window.final_restart))
            # Restart game
            if ok_window.final_restart:
                self.restart_game()

        # Update gui
        self.turn["text"] = "Next player: {}".format(
            "black" if self.chessBoard.next_turn == 1 else "white")
        self.button_evaluate()
        # print(self.chessBoard)

    def _nearest_position(self, click) -> Tuple[int, int]:
        x, y = click.x, click.y
        return (
            sorted(
                range(self.gap, self.gap * 16, self.gap),
                key=lambda temp_x: abs(temp_x - x),
            )[0],
            sorted(
                range(self.gap, self.gap * 16, self.gap),
                key=lambda temp_y: abs(temp_y - y),
            )[0],
        )

    @staticmethod
    def convert_coordinate(x: int, y: int) -> Tuple[int, int]:
        if x > 16 or y > 16:
            return x // 25 - 1, y // 25 - 1
        else:
            return (x + 1) * 25, (y + 1) * 25

    def _init_menu(self):
        menu = Menu(self.root, tearoff=0)
        self.root.config(menu=menu)
        game_menu = Menu(menu)
        game_menu.add_command(label="New game", command=self.restart_game)
        # game_menu.add_command(label="Evaluate", command=self.button_evaluate)
        game_menu.add_command(label="Withdraw", command=self.withdraw)
        game_menu.add_command(label="Exit", command=self.root.destroy)
        menu.add_cascade(label="Game", menu=game_menu)

    def _draw_label(self):
        for x in range(15):
            Label(self.horLabel, text=format_number(x)).pack(side=LEFT,
                                                             padx=3,
                                                             pady=0)
        for y in range(15):
            Label(self.verLabel, text=format_number(y)).pack(side=TOP,
                                                             pady=2,
                                                             padx=0)

    def _draw_lines(self):
        # horizontal
        for index in range(1, 16):
            self.mainBoard.create_line(self.gap * index, self.gap,
                                       self.gap * index, 25 * 15)
            self.mainBoard.create_line(self.gap, self.gap * index, 25 * 15,
                                       self.gap * index)

    def restart_game(self):
        global user_info, bd
        del bd
        print("\n----------Restart Game!----------")
        self.root.destroy()
        bd = ChessBoardInterface(pack_label=user_info["pack_label"])
        bd.root.mainloop()

    def withdraw(self):
        # Check if the chessboard is frozen
        if self.chessBoard.freeze or self.button_freeze:
            return

        withdraw_gen = self.chessBoard.withdraw()
        last_chess = next(withdraw_gen)
        player, position = last_chess
        print("Withdraw {} chess at ({}, {})".format(
            "Black" if player == 1 else "White", position[0], position[1]))

        # Change next player
        self.chessBoard.next_turn = 1 if self.chessBoard.next_turn == 2 else 2

        # Delete picture
        self.mainBoard.delete(self.operate.pop())

        self.button_evaluate()