import itertools
from tkinter.messagebox import *
from tkinter import *
import random
自定义函数 | 功能 |
---|---|
def start(self, event) | 初始化游戏页面 |
def callback1(self, event) | 双人模式控制黑棋 |
def callback2(self, event) | 双人模式控制白棋 |
def rand(self, event) | 单人模式控制黑棋 |
def check_win(self, x, y) | 判断胜负 |
def is_repeat(self, x, y) | 判断棋子是否重复 |
def destroy_buttons(self) | 销毁按钮,防止误触从而重新进入游戏界面 |
def is_continue_game(self, event) | 游戏结束后,判断用户是否继续游戏还是结束游戏 |
def regret_chess(self, event) | 悔棋 |
在程序中,可以把一局游戏看作一个
class
,每个环节是它的一个函数,而__init__()
的函数总是在class被初始化时执行
它的主要功能是:传递参数,比如赋值给对象属性
功能:
- 为游戏创建了一个基于Tkinter的GUI窗口
- 添加了三个按钮用于选择游戏模式和退出游戏
- 由于进入游戏后防止误触以及不美观等因素,需要创建一个列表来保存按钮的引用,以便销毁双人模式和单人模式
实现代码及注释如下:
def __init__(self):
self.root = Tk() # 创建窗口
self.root.geometry("1000x1000") # 窗口大小
self.r = Canvas(self.root, width=1000, height=1000) # 创建画布
self.r.pack(pady=20) # 画布位置
self.pic = PhotoImage(file='D:\\code\\2024\\Python\\python\\task\\Gomoku_game\\data\\Gomoku_main_page.png')
self.r.create_image(500, 450, image=self.pic) # 图片位置
# 创建一个退出游戏的按钮,以便退出游戏
self.b1 = Button(self.root, text="退出游戏", width=8, font=("楷体", 13), command=self.root.destroy)
self.b1.pack() # 显示
self.b1.place(x=870, y=950) # 位置
self.buttons = [] # 创建一个列表来保存按钮的引用,以便销毁
s = ["双人", "单人"]
for i in range(len(s)):
self.b = Button(self.root, text=s[i], width=10, font=("楷体", 17)) # 添加按钮
self.b.bind('' , self.start) # 绑定self.start方法
self.b.place(x=(i + 1) * 300, y=950) # 按钮位置
self.buttons.append(self.b) # 将按钮引用添加到列表中
self.root.mainloop() # 运行窗口
功能:
- 销毁以前的画布,并新建一个画布绘制棋盘和交叉点,初始化棋盘
- 创建悔棋按钮,并绑定到regret_chess方法
- 根据被点击的模式选择按钮,将画布上的鼠标点击事件绑定到不同的方法。
- 随后销毁选择模式的按钮
def start(self, event):
self.r.destroy() # 销毁上面的画布,为了进入游戏
self.c = Canvas(self.root, width=1000, height=930) # 创建新的画布
self.c.pack()
self.c.place(x=0, y=0) # 防止用户重复点击造成画布位置移动
self.pic = PhotoImage(file='D:\\code\\2024\\Python\\python\\task\\Gomoku_game\\data\\Gomoku_background.png')
self.c.create_image(400, 340, image=self.pic) # 图片位置
self.r_chess = Button(self.root, text="悔棋", width=8, font=("楷体", 17)) # 放置悔棋按钮
self.r_chess.bind('' , self.regret_chess) #绑定一个方法
self.r_chess.pack()
self.r_chess.place(x=447, y=940)
# 绘制棋盘网格
for i in range(1, 16): # 绘制棋盘的水平和垂直线
self.c.create_line(60, 60 * i, 900, 60 * i)
self.c.create_line(60 * i, 60, 60 * i, 900)
# 绘制棋盘的交叉点
for i in range(60, 901, 60):
for j in range(60, 901, 60):
self.c.create_oval(i - 2, j - 2, i + 2, j + 2, fill="black") # 在棋盘的每个交叉点上绘制一个小的蓝色圆形
self.matrix = [[0 for y in range(16)] for x in range(16)] # 初始化矩阵的每个位置为0,用于存储棋盘的状态
# 实际上棋子是落在前14x14,为防止白子的自动生成造成越界访问将其初始化为16x16
# isinstance()方法:检查了widget是否真的是一个Button实例
if isinstance(event.widget, Button) and (event.widget["text"]) == "双人":
self.c.bind("" , self.callback1) # 如果点击双人使用左键,调用callback1
self.c.bind("" , self.callback2) # 如果点击双人使用右键,调用callback2
elif isinstance(event.widget, Button) and (event.widget["text"]) == "单人":
self.c.bind("" , self.rand) # 如果左键点击单人,调用rand
self.destroy_buttons() # 点击之后进入游戏,因此需要销毁按钮
功能:
- 使用
try...except
块来捕捉可能出现的异常,确保程序不会因为意外情况而崩溃- 全局变量
temp
:使用 global temp 声明全局变量,用于跟踪当前是黑方还是白方的回合(callback1为黑方,callback2为白方)- 判断当前位置是否有棋子,若无,则在棋盘上放置一个黑子,更新矩阵并在画布上绘制一个黑子
- 判断是否有玩家获胜
- 如果用户点击了棋盘以外的地方,提示用户
def callback1(self, event):
try: # 捕捉异常防止崩溃
global temp #声明全局变量
if temp % 2 == 0: #为偶数
u, v = event.x, event.y # 获取鼠标点击的位置:从event中提取鼠标的x和y坐标,并分别赋值给u和v
temp += 1
else:
showinfo("ERROR", "现在是白方回合!")
# 确定鼠标点击的棋盘格子
for i in range(1, 16): # 用于确定x坐标(即u)对应的棋盘列索引zx
if 30 * i < u < 30 * (2 * i + 1):
zx = i - 1
break
for i in ra