本程序实现了井字棋人机对战小游戏,先选择玩家先手,电脑先手,再开始游戏,点击重玩会重置游戏界面,鼠标移动上去变成能点击样式的才能点
import tkinter as tk
import numpy as np
import copy
# 游戏结束
def game_over(B1, B2, B3, ):
global B
for i in range(3):
for j in range(3):
B[i][j]['state'] = 'disabled' # 设置为禁用
B[i][j]['cursor'] = 'arrow' # 鼠标样式更改
if B1['text'] == 'x':
B1['text'] = '玩'
B2['text'] = '家'
B3['text'] = '胜'
elif B1['text'] == 'o' or B2['text'] == 'o':
B1['text'] = '电'
B2['text'] = '脑'
B3['text'] = '胜'
else:
global b2
b2['text'] = '和棋'
# 绝杀点/关键点
def Key_points(M, t):
x = []
s_r = M.sum(axis=1) # 各行的和
s_c = M.sum(axis=0) # 各列的和
m_d = M[0, 0] + M[1, 1] + M[2, 2] # 主对角线
b_d = M[0, 2] + M[1, 1] + M[2, 0] # 反对角线
for i in range(3):
for j in range(3):
if M[i][j] == 0: # 这是个空位
c = 0 # 行,列,或对角和为1的个数
if i == j and m_d == t: c += 1 # 如果是对角线或反对角线
if i + j == 2 and b_d == t: c += 1
if s_r[i] == t: c += 1
if s_c[j] == t: c += 1
if c >= 2:
x.append([i, j]) # 能绝杀,放置
return x
# 电脑下棋+判断输赢
def aiplay():
global B, b1, b2, b3
mark = [] # 记号,储存棋盘
t = 0 # 记录空位个数
for i in range(3):
mark.append([])
for j in range(3):
if B[i][j]['text'] == 'o':
mark[i].append(1)
elif B[i][j]['text'] == 'x':
mark[i].append(-1)
else:
mark[i].append(0)
t += 1
if sum(mark[i]) == -3: # 如果某一排有三个x玩家赢
game_over(B[i][0], B[i][1], B[i][2])
return
if not t: # 没有空位,和棋
game_over(b1, b2, b3)
mark = np.array(mark)
sum_row = mark.sum(axis=1) # 各行的和
sum_column = mark.sum(axis=0) # 各列的和
main_diagonal = mark[0, 0] + mark[1, 1] + mark[2, 2] # 主对角线
back_diagonal = mark[0, 2] + mark[1, 1] + mark[2, 0] # 反对角线
for j in range(3):
if sum_column[j] == -3: # 如果某一列有三个x玩家赢
game_over(B[0][j], B[1][j], B[2][j])
return
if main_diagonal == -3: # 单独判断对角线
game_over(B[0][0], B[1][1], B[2][2])
return
if back_diagonal == -3: # 单独判断反对角线
game_over(B[0][2], B[1][1], B[2][0])
return
# 电脑胜判断======================================================
for i in range(3): # 有2个o电脑再下一个,电脑胜
if sum_row[i] == 2:
game_over(B[i][0], B[i][1], B[i][2])
return
if sum_column[i] == 2:
game_over(B[0][i], B[1][i], B[2][i])
return
if main_diagonal == 2: # 单独判断对角线
game_over(B[0][0], B[1][1], B[2][2])
return
if back_diagonal == 2: # 单独判断反对角线
game_over(B[0][2], B[1][1], B[2][0])
return
# 没法直接赢,看看有没有能堵住的==================================
for i in range(3): # 有2个x电脑堵一下
if sum_row[i] == -2:
aiplace(B[i][0]), aiplace(B[i][1]), aiplace(B[i][2])
return
if sum_column[i] == -2:
aiplace(B[0][i]), aiplace(B[1][i]), aiplace(B[2][i])
return
if main_diagonal == -2: # 单独判断对角线
aiplace(B[0][0]), aiplace(B[1][1]), aiplace(B[2][2])
return
if back_diagonal == -2: # 单独判断反对角线
aiplace(B[0][2]), aiplace(B[1][1]), aiplace(B[2][0])
return
# 多想一步,如果有地方下了能有两个值为2的地方,就能绝杀================================
K = Key_points(mark, 1)
for i in K:
aiplace(B[i[0]][i[1]]) # 能绝杀,放置
return
# 再多想一步,如果对方有能把自己绝杀的点,抢先堵住=======================================
K = Key_points(mark, -1)
for i in K:
aiplace(B[i[0]][i[1]]) # 能绝杀,放置
return
# 多想两步,看有没有位置放了之后可以出现两个绝杀点
for i in range(3):
for j in range(3):
if mark[i][j] == 0: # 这是个空位
mark_new = copy.deepcopy(mark) # 深拷贝
mark_new[i][j] = 1
K = Key_points(mark_new, 1)
if len(K) >= 2:
aiplace(B[i][j]) # 或许能绝杀,放置
return
# 再多想两步,看对面有没有位置放了之后可以出现两个绝杀点
for i in range(3):
for j in range(3):
if mark[i][j] == 0: # 这是个空位
mark_new = copy.deepcopy(mark) # 深拷贝
mark_new[i][j] = -1
K = Key_points(mark_new, -1)
if len(K) >= 2:
aiplace(B[i][j]) # 或许能绝杀,放置
return
# 这还没办法,看哪有空位下在哪
for i in range(3):
for j in range(3):
if mark[i][j] == 0: # 这是个空位
aiplace(B[i][j]) # 放置
return
# 没有空位了,平局
game_over(b1, b2, b3)
# 电脑放置
def aiplace(Bu):
if not Bu['text'] == 'x':
Bu['text'] = 'o' # 打上o
Bu['state'] = 'disabled' # 设置为禁用
Bu['cursor'] = 'arrow' # 鼠标样式更改
# 玩家放置
def place(Bu):
Bu['text'] = 'x' # 打上x
Bu['state'] = 'disabled' # 设置为禁用
Bu['cursor'] = 'arrow' # 鼠标样式更改
aiplay()
# 重玩
def restart():
global b1, b2, b3, B
for i in range(3):
for j in range(3):
B[i][j]['text'] = '' # 棋盘重置
B[i][j]['state'] = 'disabled' # 设置为禁用
B[i][j]['cursor'] = 'arrow' # 鼠标样式更改
b1['state'] = 'disabled' # 设置为禁用
b1['cursor'] = 'arrow' # 鼠标样式更改
b2['text'] = '开始' # 和棋时需要重置
b2['state'] = 'normal' # 设置为启用
b2['cursor'] = 'hand2' # 鼠标样式更改
b3['state'] = 'normal' # 设置为启用
b3['cursor'] = 'hand2' # 鼠标样式更改
# 开始
def play():
global b1, b2, b3, B, player_first
for i in range(3):
for j in range(3):
B[i][j]['text'] = '' # 棋盘重置
B[i][j]['state'] = 'normal' # 设置为启用
B[i][j]['cursor'] = 'hand2' # 鼠标样式更改
b1['state'] = 'normal' # 设置为启用
b1['cursor'] = 'hand2' # 鼠标样式更改
b2['state'] = 'disabled' # 设置为禁用
b2['cursor'] = 'arrow' # 鼠标样式更改
b3['state'] = 'disabled' # 设置为禁用
b3['cursor'] = 'arrow' # 鼠标样式更改
if not player_first:
aiplay() # AI先下
# 先手判断
def Go_first():
global player_first
if player_first:
b3['text'] = '电脑先手'
else:
b3['text'] = '玩家先手'
player_first = not player_first
global player_first, B, b1, b2, b3
player_first = True # 玩家先手
main_window = tk.Tk() # 调用Tk()创建主窗口
main_window.title("井字棋") # 给主窗口起一个名字
main_window.geometry("450x450") # 大小
# main_window.resizable(0,0) # 不允许拉伸改变大小
# 按钮 text:按钮文本,width、height:按钮大小,cursor:鼠标样式,command:调用函数
B = [] # 棋盘按钮组
for i in range(3):
B.append([])
main_window.grid_rowconfigure(i, weight=1) # row为i,缩放比为1
for j in range(3):
main_window.grid_columnconfigure(j, weight=1) # column为i,缩放比为1
B[i].append(tk.Button(main_window, text="", width=15, height=5, )) # 添加按钮
B[i][j]['command'] = lambda i=i, j=j: place(B[i][j]) # 增加点击函数
B[i][j]['state'] = 'disabled' # 初始禁用
B[i][j].grid(row=i, column=j, sticky=tk.N + tk.S + tk.W + tk.E) # 添加到主窗口显示
b1 = tk.Button(main_window, text="重玩", width=15, height=5, command=restart) # 添加按钮,点击调用重启函数
b2 = tk.Button(main_window, text="开始", width=15, height=5, cursor='hand2', command=play) # 添加按钮,点击调用开始函数
b3 = tk.Button(main_window, text='玩家先手', width=15, height=5, cursor='hand2', command=Go_first) # 添加按钮,点击调用先手函数
b1['state'] = 'disabled' # 初始为禁用
b1.grid(row=3, column=0, sticky=tk.N + tk.S + tk.W + tk.E)
b2.grid(row=3, column=1, sticky=tk.N + tk.S + tk.W + tk.E)
b3.grid(row=3, column=2, sticky=tk.N + tk.S + tk.W + tk.E)
main_window.mainloop() # 开启主循环,让窗口处于显示状态