美文网首页
pycharm+pygame基于剪枝技术的一字棋博弈系统

pycharm+pygame基于剪枝技术的一字棋博弈系统

作者: sugar_coated | 来源:发表于2020-09-28 15:59 被阅读0次

    在pycharm中建立pure python项目即可。
    python最好不要下载based版本,使用扩展包不太方便。我下载的版本链接:Windows x86-64 executable installer

    关于包的安装(在terminal端口执行如下命令):
    pip install pygame。若出现异常参考-->Pygame安装教程
    pip install numpy
    pip install pyinstaller

    该项目主要包括两个py文件(ChessGame.py, MyLibrary.py),也可以只写一个py文件。

    α-β剪枝思路流程图。(来源于下方参考的pdf)



    具体代码如下,估价函数等在注释中已经写出(MyLibrary.py)。

    # 颜色
    black_color = (0, 0, 0)  # 定义黑色
    white_color = (255, 255, 255)  # 定义白色
    blue_color = (0, 0, 255)  # 定义蓝色
    background_color = (46, 139, 87)  # 定义背景色
    line_color = (200, 200, 200)  # 定义棋盘线条色
    
    # 界面大小参数
    cell_size = 50  # 每个格子大小
    cell_num = 5  # 棋盘规格
    grid_size = cell_size * (cell_num - 1) + cell_size * 2  # 窗口的大小,且将棋盘四边留白cell_size
    chess_radius = 20
    font_size = 25
    
    # 区分棋子参数
    none_chess = 0
    black_chess = 1  # 黑子先行
    white_chess = 2
    
    # 游戏状态与结果参数
    not_start = -2  # 还未开始
    ongoing = -1  # 正在进行
    win_win = 0  # 双赢
    computer_win = 1
    player_win = 2
    
    # 估价函数与剪枝
    # f(p) = (所有空格放上电脑方棋子后,n子连线的总个数)-(所有空格放上玩家方棋子后,n子连线的总个数)。
    # 设电脑方为MAX,玩家方为MIN
    # MAX结点(或结点)的alpha值为当前子结点的最大倒推值
    # MIN结点(与结点)的beta值为当前子结点的最小倒推值
    # beta剪枝:若任何MAX结点n的alpha值大于或等于它先辈结点的beta值,则n以下的分枝可停止搜索,并令结点n的倒推值为alpha。
    # alpha剪枝:若任何MIN结点n的beta值小于或等于它先辈结点的alpha值,则n以下的分枝可停止搜索,并令结点n的倒推值为beta。
    INF = 100
    DEPTH = 3
    
    
    # 估计函数f(p)
    def get_price(chess_matrix, computer_chess, player_chess):
        return count_win(chess_matrix, computer_chess) - count_win(chess_matrix, player_chess)
    
    
    # 所有空格都放上某色棋子后五子成线的总数
    def count_win(chess_matrix, count_chess):
        cnt = 0
        cnt1 = cnt2 = 0
        for i in range(cell_num):
            cnt3 = cnt4 = 0
            for j in range(cell_num):
                if chess_matrix[i][j] == none_chess or chess_matrix[i][j] == count_chess:
                    cnt3 += 1
                    if i == j:
                        cnt1 += 1
                    if i + j == cell_num - 1:
                        cnt2 += 1
                if chess_matrix[j][i] == none_chess or chess_matrix[j][i] == count_chess:
                    cnt4 += 1
    
            cnt += (cnt3 == cell_num) + (cnt4 == cell_num)
    
        cnt += (cnt1 == cell_num) + (cnt2 == cell_num)
        return cnt
    
    
    # alpha beta 算法
    # current_chess表示当前该current_chess值的棋子走步
    def alpha_beta_score(chess_matrix, current_chess, depth, alpha, beta, computer_chess, player_chess):
        if depth >= DEPTH:
            return get_price(chess_matrix, computer_chess, player_chess)
        result = probe_result(chess_matrix, computer_chess)
        if result == computer_win:
            return INF
        if result == player_win:
            return -INF
        # print_matrix(chess_matrix)
        for i in range(cell_num):
            for j in range(cell_num):
                if chess_matrix[i][j] == none_chess:
                    chess_matrix[i][j] = current_chess
                    price = alpha_beta_score(chess_matrix,
                                             computer_chess if current_chess == player_chess else player_chess,
                                             depth + 1, alpha, beta, computer_chess, player_chess)
                    chess_matrix[i][j] = none_chess
                    if current_chess == computer_chess:
                        alpha = max(alpha, price)
                        if alpha >= beta:  # beta剪枝:当前MAX(computer)结点的alpha>=它先辈结点的beta值,其以下的分枝可停止搜索
                            return alpha  # 令结点n的倒推值为alpha
                    else:
                        beta = min(beta, price)
                        if beta <= alpha:  # alpha剪枝:当前MIN(player)结点的beta<=它先辈结点的alpha值
                            return beta
    
        return alpha if current_chess == computer_chess else beta
    
    
    # 探测游戏结果
    def probe_result(chess_matrix, computer_chess):
        count_none = 0
        result = ongoing
        v1 = v2 = chess_matrix[round(cell_num / 2)][round(cell_num / 2)]
        f1 = f2 = True
        for i in range(cell_num):
            v3 = chess_matrix[i][0]
            v4 = chess_matrix[0][i]
            f3 = f4 = True
            for j in range(cell_num):
                if chess_matrix[i][j] == none_chess:
                    count_none += 1
                if i == j and v1 != chess_matrix[i][j]:
                    f1 = False
                if i + j == cell_num - 1 and v2 != chess_matrix[i][j]:
                    f2 = False
                if v3 != chess_matrix[i][j]:
                    f3 = False
                if v4 != chess_matrix[j][i]:
                    f4 = False
            if f3 and v3 != none_chess:
                result = computer_win if v3 == computer_chess else player_win
            if f4 and v4 != none_chess:
                result = computer_win if v4 == computer_chess else player_win
        if f1 and v1 != none_chess:
            result = computer_win if v1 == computer_chess else player_win
        if f2 and v2 != none_chess:
            result = computer_win if v2 == computer_chess else player_win
        if result == ongoing and count_none == 0:
            result = win_win
        return result
    
    
    # 打印矩阵
    def print_matrix(matrix):
        for i in range(len(matrix)):
            for j in range(len(matrix[i])):
                print(matrix[j][i], "")
            print()
    
    

    ChessGame.py如下。

    import pygame  # 导入pygame库
    from pygame.locals import *  # 导入pygame中所有常量
    from sys import exit  # sys中exit()函数用于关闭窗口
    import numpy as np
    from MyLibrary import *
    
    
    # 引导开始界面
    def draw_begin():
        global screen
        screen = pygame.display.set_mode((grid_size, grid_size))  # 创建一个分辨率为grid_size*grid_size的窗口
        screen.fill(white_color)
        image_text = my_font.render("我要黑色棋子", True, black_color)
        screen.blit(image_text, (75, 100))  # 文本为左上角坐标(75,100)的150*25的矩形
        image_text = my_font.render("我要白色棋子", True, black_color)
        screen.blit(image_text, (75, 175))
    
    
    # 开始游戏前,玩家选择执黑子或者白子
    def select_chess():
        global player_fist, player_chess, computer_chess, player_color, computer_color, game_state
        x, y = pygame.mouse.get_pos()  # 获取鼠标位置
        if 75 <= x <= 225 and (100 <= y <= 125 or 175 <= y <= 200):
            game_state = ongoing
            draw_checkerboard()
            if y <= 125:
                player_fist = True
                player_chess = black_chess
                player_color = black_color
                computer_chess = white_chess
                computer_color = white_color
            else:
                player_fist = False
                player_chess = white_chess
                player_color = white_color
                computer_chess = black_chess
                computer_color = black_color
                computer_fall()
    
    
    # 鼠标在可点击部分移动时改变鼠标的样式
    def mouse_motion():
        x, y = pygame.mouse.get_pos()  # 获取鼠标位置
        flag = False
        if game_state == not_start and 75 <= x <= 225 and (100 <= y <= 125 or 175 <= y <= 200):
            flag = True  # 开始界面的选择黑白棋子
        if game_over() and 100 <= x <= 200 and 310 <= y <= 335:
            flag = True  # 结束界面的选择再来一局
        if game_state == ongoing and 25 <= x <= 275 and 25 <= y <= 275:
            flag = True  # 博弈时再棋盘上
        if flag:
            pygame.mouse.set_cursor(*pygame.cursors.tri_left)  # 鼠标在文本上改变样式
        else:
            pygame.mouse.set_cursor(*pygame.cursors.arrow)  # 离开文本恢复到正常模式
    
    
    # 画棋盘
    def draw_checkerboard():
        global screen
        screen = pygame.display.set_mode((grid_size, grid_size + cell_size))  # 创建一个分辨率为grid_size*grid_size的窗口
        screen.fill(background_color)  # 将界面设置为绿色
        # 画网格线
        # 左上角为原点,向右为X轴,向下为Y轴
        for i in range(0, cell_size * cell_num, cell_size):  # start, stop[, step]
            pygame.draw.line(screen, line_color, (i + cell_size, 0 + cell_size),
                             (i + cell_size, grid_size - cell_size))  # screen, color, start, stop
        for j in range(0, cell_size * cell_num, cell_size):
            pygame.draw.line(screen, line_color, (0 + cell_size, j + cell_size), (grid_size - cell_size, j + cell_size))
    
    
    # 画棋子
    def draw_chess(pos, chess_color):
        # surface, color, pos, radius(半径), width(绘制圆的线的宽度,为0时,圆内全部被填充)
        pygame.draw.circle(screen, chess_color, pos, chess_radius)
    
    
    # 玩家落子
    def player_fall():
        if game_over():
            return
        x, y = pygame.mouse.get_pos()
        x = round(x / cell_size) * cell_size  # 调整落子的位置,使其圆心在交叉线上
        y = round(y / cell_size) * cell_size  # 鼠标落在以交叉点为中心的cell_size的正方形中时,圆心都在交叉点上
        i = int(x / cell_size - 1)
        j = int(y / cell_size - 1)
        if (i in range(cell_num)) and (j in range(cell_num)):
            if chessboard[i][j] == none_chess:
                draw_chess((x, y), player_color)
                chessboard[i][j] = player_chess
                return True
        return False
    
    
    # 电脑落子
    def computer_fall():
        global game_state
        if game_over():
            return
        max_score = -INF
        i = j = 0
        for a in range(cell_num):
            for b in range(cell_num):
                if chessboard[a][b] == none_chess:
                    chessboard[a][b] = computer_chess
                    score = alpha_beta_score(chessboard, player_chess, 1, -INF, INF, computer_chess,
                                             player_chess)  # 将alpha初始化极小,去获取尽可能大的alpha
                    chessboard[a][b] = none_chess
                    if score > max_score:
                        i, j = a, b
                        max_score = score
    
        x = (i + 1) * cell_size
        y = (j + 1) * cell_size
        draw_chess((x, y), computer_color)
        chessboard[i][j] = computer_chess
    
    
    # 判断游戏是否结束
    def game_over():
        global game_state
        if game_state == not_start:
            return False
        game_state = probe_result(chessboard, computer_chess)
        if game_state != ongoing:
            return True
        return False
    
    
    # 画游戏结束界面
    def draw_game_over():
        msg = "输了,再接再厉"
        if game_state == win_win:
            msg = "平局,不分胜负"
        if game_state == player_win:
            msg = "赢啦,你真厉害"
        image_text = my_font.render(msg, True, black_color)
        screen.blit(image_text, (62, 274))
        image_text = my_font.render("再来一局", True, blue_color)
        screen.blit(image_text, (100, 310))
    
    
    # 初始化游戏
    def init_game():
        global chessboard, player_fist, game_state
        draw_begin()
        chessboard = np.zeros((cell_num, cell_num), dtype=int)
        game_state = not_start
    
    
    # main program begins
    pygame.init()  # 初始化pygame模块
    screen = pygame.display.get_surface()
    my_font = pygame.font.Font("SimHei.ttf", font_size)
    pygame.display.set_caption("一字棋博弈")  # 设置窗口名称
    # chessboard = [[none_chess for i in range(cell_num)] for j in range(cell_num)]  # 列表生成式
    chessboard = None  # 表示棋盘的二维数组
    player_fist = None  # 玩家是否先下,规则为黑子先行
    game_state = None  # 游戏状态
    player_chess = computer_chess = None
    player_color = computer_color = None
    init_game()
    
    # main loop
    while True:  # 一个游戏窗口应该是不断循环,直到玩家关闭窗口
    
        # 事件部分
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()  # 如果一个Pygame程序在调用pygame.quit()之前就sys.exit()了,将会导致IDLE挂起
                exit()
            # 玩家在开始界面,未进入游戏
            if event.type == MOUSEMOTION:
                mouse_motion()
            if game_state == not_start:
    
                if event.type == MOUSEBUTTONDOWN:  # 鼠标落下
                    select_chess()
    
            elif game_state == ongoing:
                # 玩家落子后电脑落子
                if event.type == MOUSEBUTTONDOWN:
                    if player_fall():
                        computer_fall()
    
            elif game_over():
                # 再来一局事件
                if event.type == MOUSEBUTTONDOWN:
                    xi, yi = pygame.mouse.get_pos()
                    if 100 <= xi <= 200 and 310 <= yi <= 335:
                        init_game()
    
        if game_over():
            draw_game_over()
    
        pygame.display.update()  # 必须调用update才能看到绘图显示
    
    

    最后用pyinstaller进行打包。
    在terminal处执行pyinstaller ChessGame.py,会在项目目录中生成dist、build、ChessGame.spec,其中dist里的文件夹就为免安装的可执行程序。build为临时文件,spec为配置文件,都可以删除。

    若出现:TypeError: an integer is required (got type bytes),表示python版本与pyinstaller版本不兼容。



    最终效果:



    有误望指出,非常感谢。
    棋盘设计参考:http://www.cppcns.com/jiaoben/python/283355.html
    α-β剪枝参考:α-β剪枝实现的一字棋.pdf

    相关文章

      网友评论

          本文标题:pycharm+pygame基于剪枝技术的一字棋博弈系统

          本文链接:https://www.haomeiwen.com/subject/quhhbctx.html