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

在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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271