对抗搜索游戏-实验报告

实验要求

实验目的:
1.熟悉博弈树及博弈搜索;
2.了解minmax搜索算法和alpha-beta剪枝算法;
3.了解评估函数设计方法,运用博弈搜索解决益智游戏问题;

实验内容(三选一):
1.设计Minimax智能体;
2.利用Minimax算法的思想实现井字棋游戏;
3.自选对抗搜索游戏并进行实现。

原理

参考资料
https://blog.csdn.net/weixin_42165981/article/details/103263211
https://blog.csdn.net/xrying621/article/details/127254462

核心代码

程序使用time.monotonic()函数统计,输出电脑每一次决策需要的时间。最关键的部分是α-β剪枝优化,自己做的界面有点简单,可以调用pyQt或者pygame绘制更棒的图形界面。

import copy
import tkinter as tk
from tkinter import messagebox
import tkinter
import time

HEIGHT_OF_WINDOW = 500
WIDTH_OF_WINDOW = 500
HEIGHT_OF_GRID = 100
WIDTH_OF_GRID = 100
ENUM = ["O", "X"]
COLUMN_ROW = 3
MAX_VALUE = 2
ANCHOR = tk.CENTER
X_GRID = 100
Y_GRID = 50
X_CENTER = 0.3
Y_CENTER = 0.2
DEVIDE = 5
DELAY = 1000
COUNT = 0


class Node:
    def __init__(
        self,
        board,
        mark,
        index=None,
    ) -> None:
        self.board = board
        self.mark = mark
        self.value = MAX_VALUE if mark else -MAX_VALUE
        # self.value=None
        self.alpha = -MAX_VALUE
        self.beta = MAX_VALUE
        self.index = index

        self.leaf = self.is_leaf()
        self.next: list[Node] = []

    def is_leaf(self):
        if self.board[0][0] == self.board[1][1] == self.board[2][2] != "":
            return self.board[0][0]
        if self.board[0][2] == self.board[1][1] == self.board[2][0] != "":
            return self.board[0][2]
        for i in range(3):
            if self.board[i][0] == self.board[i][1] == self.board[i][2] != "":
                return self.board[i][0]
            if self.board[0][i] == self.board[1][i] == self.board[2][i] != "":
                return self.board[0][i]
        if all([self.board[i][j] != "" for i in range(3) for j in range(3)]):
            return "draw"
        return False

    def search_node(self):
        if self.leaf:
            self.evaluate_leaf()
        else:
            for i in range(3):
                for j in range(3):
                    if self.board[i][j] in ENUM:
                        continue
                    board = copy.deepcopy(self.board)
                    board[i][j] = ENUM[self.mark]
                    temp = Node(board, not self.mark, (i, j))
                    temp.alpha = self.alpha
                    temp.beta = self.beta
                    temp.search_node()
                    if self.mark:
                        self.alpha = max(self.alpha, temp.value)
                        self.value = self.alpha
                    else:
                        self.beta = min(self.beta, temp.value)
                        self.value = self.beta
                    if self.beta <= self.alpha:
                        return
                    self.next.append(temp)

    def evaluate_leaf(self):
        if self.leaf == "X":
            self.value = 1
        elif self.leaf == "O":
            self.value = -1
        else:
            self.value = 0

    def search_next(self):
        # global COUNT
        # COUNT=0
        clock = time.monotonic()
        self.search_node()
        if self.mark:
            self.value = -MAX_VALUE
            for n in self.next:
                if n.value > self.value:
                    self.value = n.value
                    index = n.index
        else:
            self.value = MAX_VALUE
            for n in self.next:
                if n.value < self.value:
                    self.value = n.value
                    index = n.index
        print(time.monotonic() - clock)
        # print(COUNT)
        return index


class Player:
    def __init__(self, mark="X"):
        self.mark = mark
        self.mode = False

    def switch(self):
        if self.mark == "X":
            self.mark = "O"
        else:
            self.mark = "X"

    def winner(self, board):

        for i in range(3):
            if all([cell == self.mark for cell in board[i]]):
                return self.mark
            if all([cell == self.mark for cell in [row[i] for row in board]]):
                return self.mark
        if all([board[i][i] == self.mark for i in range(3)]):
            return self.mark
        if all([board[i][2 - i] == self.mark for i in range(3)]):
            return self.mark
        if all([cell != "" for row in board for cell in row]):
            return "draw"
        return False


class Button:
    def __init__(
        self,
        master,
        text,
        command,
        row=0,
        column=0,
        relx=0,
        rely=0,
        font=None,
        width=3,
        height=1,
    ):
        if font == None:
            self.button = tk.Button(master, text=text, command=command)
        else:
            self.button = tk.Button(
                master,
                text=text,
                font=font,
                width=width,
                height=height,
                command=command,
            )
        self.button.grid(row=row, column=column)
        self.button.place(relx=relx, rely=rely, anchor=ANCHOR)


class Buttons:
    def __init__(self, master: tkinter.Tk) -> None:
        self.master = master
        self.grids: list[tk.Button] = []
        self.board = []
        self.player = Player()
        self.canvas = tk.Canvas(
            self.master, width=WIDTH_OF_WINDOW, height=HEIGHT_OF_WINDOW
        )
        self.canvas.grid()
        self.canvas.create_rectangle(
            X_GRID,
            Y_GRID,
            X_GRID + WIDTH_OF_GRID * COLUMN_ROW,
            Y_GRID + HEIGHT_OF_GRID * COLUMN_ROW,
            outline="black",
        )
        self.tips = Button(master, "Tips", self.tip, 4, 1, 0.7, 0.9)
        self.manMan = Button(master, "Man-\nMan", self.man_man, 4, 0, 0.3, 0.9)
        self.manMechine = Button(
            master, "Man-\nMechine", self.man_mechine, 4, 0, 0.5, 0.9
        )

    def grid(self, i, j):
        self.canvas.delete("all")
        if self.board[i][j] == "":
            self.board[i][j] = self.player.mark
            self.grids[i * 3 + j].config(text=self.player.mark)
            self.grids[i * 3 + j].config(state=tk.DISABLED)
            # print(self.board)
        winner = self.player.winner(self.board)
        if winner:
            self.over(winner)
        else:
            self.player.switch()
            if self.player.mode == "O" and self.player.mark == "X":
                node = Node(self.board, True)
                i, j = node.search_next()
                self.grid(i, j)
            elif self.player.mode == "X" and self.player.mark == "O":
                node = Node(self.board, False)
                i, j = node.search_next()
                self.grid(i, j)

    def start(self):
        self.canvas.delete("all")
        self.player.mark = "X"
        for grid in self.grids:
            grid.destroy()
        self.grids = []
        self.board = [["" for i in range(3)] for j in range(3)]
        for row in range(3):
            for column in range(3):

                grid = tk.Button(
                    self.master,
                    text=self.board[row][column],
                    font=("Arial", 30),
                    width=3,
                    height=1,
                    command=lambda i=row, j=column: self.grid(i, j),
                )
                grid.grid(row=row, column=column)
                grid.place(
                    relx=X_CENTER + row / DEVIDE,
                    rely=Y_CENTER + column / DEVIDE,
                    anchor=tk.CENTER,
                )
                self.grids.append(grid)

    def tip(self):
        # print(self.board,self.player.mark)
        node = Node(self.board, True if self.player.mark == "X" else False)
        row, column = node.search_next()
        x1 = row * WIDTH_OF_GRID
        y1 = column * HEIGHT_OF_GRID
        x2 = x1 + WIDTH_OF_GRID
        y2 = y1 + HEIGHT_OF_GRID

        self.canvas.create_rectangle(
            X_GRID + x1, Y_GRID + y1, X_GRID + x2, Y_GRID + y2, outline="red"
        )
        self.master.after(
            DELAY,
            lambda: self.canvas.create_rectangle(
                X_GRID + x1, Y_GRID + y1, X_GRID + x2, Y_GRID + y2, outline="black"
            ),
        )

    def man_man(self):
        self.start()

    def man_mechine(self):
        self.start()
        if messagebox.askyesno(None, "Are you the Player 1?"):
            self.player.mode = "X"
        else:
            self.player.mode = "O"
            node = Node(self.board, True)
            i, j = node.search_next()
            self.grid(i, j)

    def over(self, winner):

        if winner == "draw":
            messagebox.showinfo(None, "It's a draw!")
        else:
            messagebox.showinfo(None, winner + " wins!")
        if messagebox.askyesno(None, "Do you want to play again?"):
            self.reset()
        else:
            messagebox.showinfo("Game Over", "Thanks for playing!")
            self.master.destroy()

    def reset(self):
        self.master.destroy()
        tictactoe()


class GUI:
    def __init__(self, height=HEIGHT_OF_WINDOW, width=WIDTH_OF_WINDOW):

        self.root = tkinter.Tk()
        self.root.geometry(f"{width}x{height}")
        self.root.title("Tic Tac Toe")

    def start(self):
        self.root.mainloop()


def tictactoe():
    gui = GUI()
    Buttons(gui.root)
    gui.start()


if __name__ == "__main__":
    tictactoe()

效果演示

测得执行一步计算时间在0.2s以内
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值