在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