一字棋的实现
利用tk库实现一字棋GUI界面的制作,导入tk库。
from tkinter import * # 从tk库导入所有方法,制作GUI界面
import tkinter.messagebox as msq # 消息框 tkinter.messagebox
创建游戏类GamePlay,包含游戏界面,玩家与电脑的操作等。
class GamePlay(object):
def __init__(self, master=None):
def create_page(self):
def update_btn_text(self, digit):
def getvalue(self):
def computer_move(self):
初始化GamePlay类,需要初始化界面大小,创建两个列表分别用来纪录已进行的操作和按钮的序号,实时刷新按钮的text属性,变量记录已操作的按钮数,并执行界面绘制函数。
def __init__(self, master=None):
self.root = master # 定义内部变量root
self.page = Frame(self.root) # 创建Frame
self.root.geometry('%dx%d' % (600, 400)) # 设置窗口大小
self.matrix = [[" "] * 3, [" "] * 3, [" "] * 3] # matrix列表记录已进行的OX操作
self.digits = [1, 2, 3, 4, 5, 6, 7, 8, 9] # digits列表包含按钮的序号,完善按钮操作逻辑
self.btn_text1 = StringVar() # 实时刷新9个按钮的text属性
self.btn_text2 = StringVar()
self.btn_text3 = StringVar()
self.btn_text4 = StringVar()
self.btn_text5 = StringVar()
self.btn_text6 = StringVar()
self.btn_text7 = StringVar()
self.btn_text8 = StringVar()
self.btn_text9 = StringVar()
self.cnt = 0 # 记录已填充格数
self.create_page() # 执行界面绘制函数 create_page()
生成界面绘制函数,需要生成Buttun控件并分配位置,用command链接后续事件——text属性的刷新、胜负的判断以及电脑操作。
def create_page(self): # 界面绘制函数
self.page.pack() # 使本页面显示
Label(self.page, text="").grid(row=0, stick=W, pady=10) # 使界面顶部空出一行
button1 = Button(self.page, textvariable=self.btn_text1, width=10, font='Times 16 bold', height=3,
command=lambda: self.update_btn_text(1))
# 创建按钮对象,属性textvariale绑定btn_text变量实例,使text以变量方法呈现
# 利用command属性绑定lambda匿名函数,实现动态控制Button控件,同时传出对应的digit值
button1.grid(row=1, column=1) # 使用grid方法控制控件位置
button2 = Button(self.page, textvariable=self.btn_text2, width=10, height=3, font='Times 16 bold',
command=lambda: self.update_btn_text(2))
button2.grid(row=1, column=2)
button3 = Button(self.page, textvariable=self.btn_text3, width=10, height=3, font='Times 16 bold',
command=lambda: self.update_btn_text(3))
button3.grid(row=1, column=3)
button4 = Button(self.page, textvariable=self.btn_text4, width=10, height=3, font='Times 16 bold',
command=lambda: self.update_btn_text(4))
button4.grid(row=2, column=1)
button5 = Button(self.page, textvariable=self.btn_text5, width=10, height=3, font='Times 16 bold',
command=lambda: self.update_btn_text(5))
button5.grid(row=2, column=2)
button6 = Button(self.page, textvariable=self.btn_text6, width=10, height=3, font='Times 16 bold',
command=lambda: self.update_btn_text(6))
button6.grid(row=2, column=3)
button7 = Button(self.page, textvariable=self.btn_text7, width=10, height=3, font='Times 16 bold',
command=lambda: self.update_btn_text(7))
button7.grid(row=3, column=1)
button8 = Button(self.page, textvariable=self.btn_text8, width=10, height=3, font='Times 16 bold',
command=lambda: self.update_btn_text(8))
button8.grid(row=3, column=2)
button9 = Button(self.page, textvariable=self.btn_text9, width=10, height=3, font='Times 16 bold',
command=lambda: self.update_btn_text(9))
button9.grid(row=3, column=3)
在command属性链接的update_btn_text()函数中完成玩家按钮判断、text属性更新、玩家胜负判断,引出电脑操作。
def update_btn_text(self, digit): # 更新Button的text属性
player_letter = "O" # 玩家写'O'
self.cnt += 1 # 占用格数加一
if digit == 1 and digit in self.digits: # 判断按下按钮位置,且确保该按钮未被按下过,以下通过9个if判断
self.digits.remove(digit) # remove方法将digits列表中的本次按下按钮的值删除,防止多次按下触发
self.matrix[0][0] = player_letter # 记录玩家选择的位置,将'O'写入matrix列表
self.btn_text1.set(player_letter) # 将player_letter的值传入btn_text变量中,完成操作更新
if digit == 2 and digit in self.digits:
self.digits.remove(digit)
self.matrix[0][1] = player_letter
self.btn_text2.set(player_letter)
if digit == 3 and digit in self.digits:
self.digits.remove(digit)
self.matrix[0][2] = player_letter
self.btn_text3.set(player_letter)
if digit == 4 and digit in self.digits:
self.digits.remove(digit)
self.matrix[1][0] = player_letter
self.btn_text4.set(player_letter)
if digit == 5 and digit in self.digits:
self.digits.remove(digit)
self.matrix[1][1] = player_letter
self.btn_text5.set(player_letter)
if digit == 6 and digit in self.digits:
self.digits.remove(digit)
self.matrix[1][2] = player_letter
self.btn_text6.set(player_letter)
if digit == 7 and digit in self.digits:
self.digits.remove(digit)
self.matrix[2][0] = player_letter
self.btn_text7.set(player_letter)
if digit == 8 and digit in self.digits:
self.digits.remove(digit)
self.matrix[2][1] = player_letter
self.btn_text8.set(player_letter)
if digit == 9 and digit in self.digits:
self.digits.remove(digit)
self.matrix[2][2] = player_letter
self.btn_text9.set(player_letter)
if self.getvalue() == -100: # 进行玩家获胜判断
msq.showinfo("Result", "你获胜了!") # 消息弹窗(标题,内容)
exit() # 关闭消息提示框后,程序退出
else:
self.computer_move() # 若玩家还未获胜,则电脑操作
if (len(self.digits) == 1) and (self.getvalue != 100 and self.getvalue != -100):
msq.showinfo("Result", "平局了!") # 如果仅剩一格且两方都未获胜,即为平局
exit()
对当前局面进行博弈判断,博弈得分值越高越利好电脑,当得分达到100时电脑获胜,得分达到-100时玩家获胜,游戏结束时若于两者之间,则平局。
def getvalue(self): # 博弈得分值函数,value值越大越利好电脑
for i in range(0, 3): # 判断游戏是否结束的value值,100、-100
if self.matrix[0][i] == 'X' and self.matrix[1][i] == 'X' and self.matrix[2][i] == 'X':
return 100
if self.matrix[0][i] == 'O' and self.matrix[1][i] == 'O' and self.matrix[2][i] == 'O':
return -100
if self.matrix[i] == ['X', 'X', 'X']:
return 100
if self.matrix[i] == ['O', 'O', 'O']:
return -100
if self.matrix[0][0] == 'X' and self.matrix[1][1] == 'X' and self.matrix[2][2] == 'X':
return 100
if self.matrix[0][0] == 'O' and self.matrix[1][1] == 'O' and self.matrix[2][2] == 'O':
return -100
if self.matrix[0][2] == 'X' and self.matrix[1][1] == 'X' and self.matrix[2][0] == 'X':
return 100
if self.matrix[0][2] == 'O' and self.matrix[1][1] == 'O' and self.matrix[2][0] == 'O':
return -100
value = 0 # value置0,进行一般情况的value值计算
for i in range(0, 3):
if self.matrix[0][i] != 'O' and self.matrix[1][i] != 'O' and self.matrix[2][i] != 'O':
value += 1
if self.matrix[0][i] != 'X' and self.matrix[1][i] != 'X' and self.matrix[2][i] != 'X':
value -= 1
if self.matrix[0][i] != 'O' and self.matrix[1][i] != 'O' and self.matrix[2][i] != 'O':
value += 1
if self.matrix[0][i] != 'X' and self.matrix[1][i] != 'X' and self.matrix[2][i] != 'X':
value -= 1
if self.matrix[0][0] != 'O' and self.matrix[1][1] != 'O' and self.matrix[2][2] != 'O':
value += 1
if self.matrix[0][0] != 'X' and self.matrix[1][1] != 'X' and self.matrix[2][2] != 'X':
value -= 1
if self.matrix[0][2] != 'O' and self.matrix[1][1] != 'O' and self.matrix[2][0] != 'O':
value += 1
if self.matrix[0][2] != 'X' and self.matrix[1][1] != 'X' and self.matrix[2][0] != 'X':
value -= 1
return value
利用极小化极大算法建立value预估函数,借助函数递归进行博弈树运算
def dfs(now_maps, pre, step): # 极小化极大算法
if now_maps.cnt == 9 or now_maps.getvalue() == 100 or now_maps.getvalue() == -100:
# 删除后电脑不会对第一次按按钮作出反应(>.<)
return now_maps.getvalue()
if step % 2 == 0:
value = 200
for i in range(0, 3):
for j in range(0, 3):
if now_maps.matrix[i][j] == ' ':
now_maps.matrix[i][j] = 'O'
now_maps.cnt += 1
tmp = dfs(now_maps, value, step + 1) # 函数递归调用,利用Minimax算法原理
now_maps.cnt -= 1
now_maps.matrix[i][j] = ' '
if tmp < value:
value = tmp
if value <= pre:
return value
else:
value = -200
for i in range(0, 3):
for j in range(0, 3):
if now_maps.matrix[i][j] == ' ':
now_maps.matrix[i][j] = 'X'
now_maps.cnt += 1
tmp = dfs(now_maps, value, step + 1)
now_maps.cnt -= 1
now_maps.matrix[i][j] = ' '
if tmp > value:
value = tmp
if value >= pre:
return value
return value
电脑执行操作函数,对未选择的点进行遍历,选择出value预估值最大的点,写入电脑的选择
def computer_move(self): # 电脑操作函数
computer_letter = 'X' # 电脑写'X'
maxvalue = -200 # 设定一个较小值,后续执行冒泡操作
for i in range(0, 3): # 遍历、寻找一个合适的点,最大value预估值的点
for j in range(0, 3):
if self.matrix[i][j] == ' ':
self.cnt += 1
self.matrix[i][j] = 'X'
tmp = dfs(self, maxvalue, 0)
self.matrix[i][j] = ' '
self.cnt -= 1
if tmp > maxvalue:
maxvalue = tmp
x, y = i, j
self.matrix[x][y] = 'X' # 将'X'写入matrix列表,记录电脑选择的坐标
self.cnt += 1 # 占用格数加一
dit = x * 3 + y + 1 # 将矩阵坐标转换为一维坐标
self.digits.remove(dit) # 移除digits列表中电脑选择的坐标
if dit == 1: self.btn_text1.set(computer_letter) # 将'X'传入dit值对应的btn_text变量,同digit
if dit == 2: self.btn_text2.set(computer_letter)
if dit == 3: self.btn_text3.set(computer_letter)
if dit == 4: self.btn_text4.set(computer_letter)
if dit == 5: self.btn_text5.set(computer_letter)
if dit == 6: self.btn_text6.set(computer_letter)
if dit == 7: self.btn_text7.set(computer_letter)
if dit == 8: self.btn_text8.set(computer_letter)
if dit == 9: self.btn_text9.set(computer_letter)
if self.getvalue() == 100:
msq.showinfo("Result", "你失败了!")
exit()
程序运行,进入主界面
if __name__ == "__main__":
root = Tk()
root.title('一字棋')
GamePlay(root)
root.mainloop() # 进入到事件(消息)循环