
基于 Tkinter 制作出的井字棋游戏。
第一个版本是没有任何算法的纯随机电脑下棋方式(简称——智障):
import tkinter as tk
from tkinter import messagebox #! 我单独导入了messagebox(信息盒子)
import random as r
window = tk.Tk() #!创建了一个叫 window 的窗口
window.title("井字棋") #!窗口名字叫 井字棋
#! Create board 创建棋盘
def create_board(): #! 创建棋盘的函数
for i in range(3): #!同过双重给for循环实现创建棋盘上的9个盘格
for j in range(3):
#!通过给按钮添加点击事件实现点击后下棋的功能:command,又因为我们要知道点的是哪个按钮
#!所以需要用到lambda将row行,col列传递进去
button = tk.Button(window, text="", font=("Arial", 50),
height=2, width=6, bg="lightblue",
command=lambda row=i, col=j: handle_click(row, col))
button.grid(row=i, column=j, sticky="nsew") #!布局到窗口中
create_board() #!调用 创建棋盘的函数
#! Initialize variables 创二维列表,表示整个棋盘的棋子
board = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
#! Handle button clicks 按钮点击事件
def handle_click(row, col): #!函数传递row(行)col(列)用来表示具体哪个按钮
if board[row][col] == 0: #!判断点击的按钮是否为0,也就是这里是不是没有落过棋子
board[row][col] = "X" #!符合条件就将棋盘放上X棋子
button = window.grid_slaves(row=row, column=col)[0] #!找到对应的棋盘格子
button.config(text=board[row][col])#!将棋画在棋盘上
xx = check_for_winner()
if xx != 0:
PC_choice() #!你下完棋了,该电脑下棋了,调用电脑的下棋函数
def PC_choice():
cnt = 0 #! 计数器 表示棋盘列表0的个数
for row in board:
cnt+=row.count(0) #!找到棋盘列表0的个数就让cnt加一 如果是0就相当于没选过的格子
if cnt > 0: #! 如果cnt大于0说明棋盘上还有空格子
while 1:#!随机选格子可能选到选过的,所以我们要一直选直到选到没选过的为止
a = r.randint(0, 2) #!随机哪一行
b = r.randint(0, 2) #!随机哪一列
if board[a][b] == 0: #!如果选的那个等于0
break #!退出循环,
board[a][b] = "O" #!将棋子画在棋盘上
button = window.grid_slaves(row=a, column=b)[0] #!找到对应的棋盘格子
button.config(text=board[a][b]) #!将棋盘更新为O
bb = check_for_winner() #!将选择胜利者的函数返回值赋值给bb
if bb == 0: #!判断返回值是不是0 ,是的话相当于决定出输赢了
return 0 #!决定出输赢了就返回0
#! Check for a winner or a tie 判断输赢平局的>>>
def check_for_winner():
winner = None #!先将胜者赋值为None,表示没有呢
#! Check rows 检查每一行
for row in board:
# print(row[0])
#!如果一行中所有元素都相等,并且不是0,那么就将这个值赋值给胜利者
if row.count(row[0]) == len(row) and row[0] != 0:
winner = row[0]
break #!都有胜利者了,其他程序也没啥必要执行了,直接打破for循环
#! Check columns 这个是检查列的,和行的类似
for col in range(len(board)):
if board[0][col] == board[1][col] == board[2][col] and board[0][col] != 0:
winner = board[0][col]
break
#! Check diagonals #!这个是检查对角线的除了横竖连成三个能赢,还有斜着的
if board[0][0] == board[1][1] == board[2][2] and board[0][0] != 0:
winner = board[0][0]
elif board[0][2] == board[1][1] == board[2][0] and board[0][2] != 0:
winner = board[0][2]
#!如果都判断完了,
#!还没有找到胜利者,那么就判断是否平局
if all([all(row) for row in board]) and winner is None:
winner = "tie" #!tie : 平局
if winner: #!之前我们学过,if 条件为真就会执行里面的程序,
#!其中0,空都表示假,所以我们要判断一下胜利者是不是真
#!在最开始我们不是设置了胜利者为None吗,None就是假.如果Winner为假
#!我们就不执行这里的程序
cc = declare_winner(winner) #!如果胜利者是真,那么调用胜利者函数
if cc == 0: #!如果调用了胜利者函数说明我已经决定出输赢了,直接返回0
return 0
#! Declare the winner and ask to restart the game 选择胜利者,或平局
def declare_winner(winner):
if winner == "tie":
message = "平局啦!"
else:
message = f"玩家 {winner} 胜利!"
#!选择胜利者,或平局后,都会弹出消息框,询问是否重新开始游戏
answer = messagebox.askyesno("游戏结束!", message + " 你想要重新开始吗?")
if answer: #!如果胜利者点的是重新开始
global board #!将全局变量board赋值给局部变量board
#!将棋盘重新赋值,相当于重新开始游戏
board = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
#!重新开始游戏后,我们还要重新创建棋盘,调用create_board()函数
create_board()
return 0 #!都重新开始了就返回0
else:
window.quit() #!没选重新开始那不就是退出游戏吗?
window.mainloop()#!窗口一直显示的循环
第二个版本是电脑会通过算法来阻止你获胜,甚至能赢过你的(简称:智能)
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() #! 开启主循环,让窗口处于显示状态
import tkinter as tk
from tkinter import *
import random as r
win=tk.Tk()
win.geometry(‘490×545’)
def start():
d.grid_forget()
c.grid_forget()
for i in range(3):
for j in range(3):
button= tk.Button(win, text=””, height=10, width=22, command=lambda row=i, col=j:(row, col))
button.grid(row=i, column=j)
d=tk.Button(win,text=”开始”,height=13,width=26,command=start)
c=tk.Button(win,text=”退出”,height=13,width=26,command=win.quit)
d.grid(row=0, column=1)
c.grid(row=0,column=2)
e=[[0,0,0],[0,0,0],[0,0,0]]
def zjxq(row,col):
if e[row][col]==0:
e[row][col]=”x”
button = win.grid_slaves(row=row, column=col)[0]
button.config(text=e[row][col])
f=slz()
if f!=0:
dnxq()
def dnxq():
x=0
for row in e:
x+=row.count(0)
if x > 0:
while True:
a = r.randint(0, 2)
b = r.randint(0, 2)
if e[a][b] == 0:
break
e[a][b] = “O”
button = win.grid_slaves(row=a, column=b)[0]
button.config(text=e[a][b])
s =slz()
if s==0:
return 0
def slz():
w=None
for row in e:
if row.
win.mainloop()