
import tkinter as tk
from tkinter import messagebox
class Gobang:
def __init__(self):
self.window = tk.Tk()
self.window.title("五子棋(AI对战版)")
# 初始化棋盘参数
self.board_size = 15
self.cell_size = 40
self.board_margin = 80
# 创建画布
canvas_size = self.board_size * self.cell_size + self.board_margin + 30
self.canvas = tk.Canvas(self.window, width=canvas_size, height=canvas_size)
self.canvas.pack(padx=10, pady=10)
try:
# 加载背景图片
self.bg_image = tk.PhotoImage(file="./background.png")
# 显示背景图片
self.canvas.create_image(canvas_size//2, canvas_size//2,
image=self.bg_image)
except Exception as e:
print("背景图片加载失败:", e)
# 如果加载图片失败,使用纯色背景
self.canvas.create_rectangle(0, 0, canvas_size, canvas_size,
fill='#CDBA96', outline='#CDBA96')
# 添加悔棋次数限制
self.undo_remaining = 10
# 添加悔棋按钮
button_x = self.board_margin + (self.board_size-1) * self.cell_size / 2
button_y = self.board_margin - 40
# 添加重新开始按钮
self.restart_button = tk.Button(self.window, text="重新开始", command=self.reset_game,
font=('Arial', 14, 'bold'),
fg='black',
bg='#CDBA96',
activebackground='#BDB088',
relief='flat')
self.restart_button.place(x=button_x - 120, y=button_y - 10)
# 绑定重新开始按钮的鼠标事件
self.restart_button.bind('<Enter>', lambda e: e.widget.configure(bg='#BDB088'))
self.restart_button.bind('<Leave>', lambda e: e.widget.configure(bg='#CDBA96'))
# 悔棋按钮
self.undo_button = tk.Button(self.window, text=f"悔棋(剩余{self.undo_remaining}次)", command=self.undo_move,
font=('Arial', 14, 'bold'),
fg='black',
bg='#CDBA96',
activebackground='#BDB088',
relief='flat')
self.undo_button.place(x=button_x - 20, y=button_y - 10)
# 绑定鼠标事件来实现透明效果
self.undo_button.bind('<Enter>', lambda e: e.widget.configure(bg='#BDB088'))
self.undo_button.bind('<Leave>', lambda e: e.widget.configure(bg='#CDBA96'))
# 添加历史记录
self.move_history = []
# 初始化棋盘数据
self.board = [[0] * self.board_size for _ in range(self.board_size)]
self.current_player = 1 # 1为玩家(黑子),2为电脑(白子)
# 绘制棋盘
self.draw_board()
# 绑定鼠标点击事件
self.canvas.bind('<Button-1>', self.handle_click)
def draw_board(self):
# 删除当前执子显示,直接开始绘制横线和坐标
for i in range(self.board_size):
y = self.board_margin + i * self.cell_size
# 绘制横线
self.canvas.create_line(self.board_margin, y,
self.board_margin + (self.board_size-1) * self.cell_size, y,
width=2.0, fill='#000000') # 加粗线条
# 添加纵坐标标记(A-O)
self.canvas.create_text(self.board_margin - 20, y,
text=chr(65 + i), font=('Arial', 11))
# 绘制竖线和坐标
for i in range(self.board_size):
x = self.board_margin + i * self.cell_size
# 绘制竖线
self.canvas.create_line(x, self.board_margin,
x, self.board_margin + (self.board_size-1) * self.cell_size,
width=2.0, fill='#000000') # 加粗线条
# 添加横坐标标记(1-15)
self.canvas.create_text(x, self.board_margin - 20,
text=str(i + 1), font=('Arial', 11))
def place_piece(self, x, y):
# 记录这步棋
self.move_history.append((x, y, self.current_player))
# 在棋盘数据中记录
self.board[y][x] = self.current_player
piece_x = self.board_margin + x * self.cell_size
piece_y = self.board_margin + y * self.cell_size
# 绘制阴影
shadow_offset = 2
self.canvas.create_oval(piece_x-15+shadow_offset, piece_y-15+shadow_offset,
piece_x+15+shadow_offset, piece_y+15+shadow_offset,
fill='#666666', outline='#666666')
# 设置棋子基本属性
piece_color = 'black' if self.current_player == 1 else '#FFFFFF'
gradient_color = '#444444' if self.current_player == 1 else '#e0e0e0'
outline_color = 'black'
# 主棋子
self.canvas.create_oval(piece_x-15, piece_y-15,
piece_x+15, piece_y+15,
fill=piece_color, outline=outline_color)
# 渐变效果
self.canvas.create_oval(piece_x-12, piece_y-12,
piece_x+8, piece_y+8,
fill=gradient_color, stipple='gray50')
# 高光效果
self.canvas.create_oval(piece_x-10, piece_y-10,
piece_x-4, piece_y-4,
fill='white', stipple='gray75')
# 添加渐变效果
if self.current_player == 1: # 黑棋
gradient_color = '#444444'
gradient_pos = 8 # 渐变位置
else: # 白棋
gradient_color = '#e0e0e0'
gradient_pos = 8 # 调整白棋渐变位置
self.canvas.create_oval(piece_x-12, piece_y-12,
piece_x+gradient_pos, piece_y+gradient_pos,
fill=gradient_color, stipple='gray50')
# 添加高光效果
if self.current_player == 1: # 黑棋
shine_size = 6
shine_pos = -10
else: # 白棋
shine_size = 6
shine_pos = -10
self.canvas.create_oval(piece_x+shine_pos, piece_y+shine_pos,
piece_x+shine_pos+shine_size, piece_y+shine_pos+shine_size,
fill='white', stipple='gray75')
def redraw_all_pieces(self):
# 重新绘制所有已放置的棋子
for y in range(self.board_size):
for x in range(self.board_size):
if self.board[y][x] != 0:
# 临时保存当前玩家
original_player = self.current_player
# 设置为对应的玩家
self.current_player = self.board[y][x]
# 绘制棋子
piece_x = self.board_margin + x * self.cell_size
piece_y = self.board_margin + y * self.cell_size
# 绘制阴影
shadow_offset = 2
self.canvas.create_oval(piece_x-15+shadow_offset, piece_y-15+shadow_offset,
piece_x+15+shadow_offset, piece_y+15+shadow_offset,
fill='#666666', outline='#666666')
# 设置棋子颜色
piece_color = 'black' if self.board[y][x] == 1 else '#FFFFFF'
gradient_color = '#444444' if self.board[y][x] == 1 else '#e0e0e0'
# 主棋子
self.canvas.create_oval(piece_x-15, piece_y-15,
piece_x+15, piece_y+15,
fill=piece_color, outline='black')
# 渐变效果
self.canvas.create_oval(piece_x-12, piece_y-12,
piece_x+8, piece_y+8,
fill=gradient_color, stipple='gray50')
# 高光效果
self.canvas.create_oval(piece_x-10, piece_y-10,
piece_x-4, piece_y-4,
fill='white', stipple='gray75')
# 恢复当前玩家
self.current_player = original_player
def get_ai_move(self):
# 简单AI:评估每个位置的分数,选择最优位置
best_score = float('-inf')
best_move = None
for y in range(self.board_size):
for x in range(self.board_size):
if self.board[y][x] == 0: # 空位置
score = self.evaluate_position(x, y)
if score > best_score:
best_score = score
best_move = (x, y)
return best_move
def evaluate_position(self, x, y):
# 评估某个位置的分数
score = 0
directions = [(1,0), (0,1), (1,1), (1,-1)]
for dx, dy in directions:
# 计算AI落子后的连子数
count_ai = 1
# 正向检查
temp_x, temp_y = x + dx, y + dy
while 0 <= temp_x < self.board_size and 0 <= temp_y < self.board_size:
if self.board[temp_y][temp_x] == 2: # AI的子
count_ai += 1
temp_x += dx
temp_y += dy
else:
break
# 反向检查
temp_x, temp_y = x - dx, y - dy
while 0 <= temp_x < self.board_size and 0 <= temp_y < self.board_size:
if self.board[temp_y][temp_x] == 2:
count_ai += 1
temp_x -= dx
temp_y -= dy
else:
break
# 计算玩家在该位置的连子数(防守分数)
count_player = 1
# 正向检查
temp_x, temp_y = x + dx, y + dy
while 0 <= temp_x < self.board_size and 0 <= temp_y < self.board_size:
if self.board[temp_y][temp_x] == 1: # 玩家的子
count_player += 1
temp_x += dx
temp_y += dy
else:
break
# 反向检查
temp_x, temp_y = x - dx, y - dy
while 0 <= temp_x < self.board_size and 0 <= temp_y < self.board_size:
if self.board[temp_y][temp_x] == 1:
count_player += 1
temp_x -= dx
temp_y -= dy
else:
break
# 评分规则
if count_ai >= 5:
score += 100000 # 必胜
elif count_ai == 4:
score += 10000
elif count_ai == 3:
score += 1000
elif count_ai == 2:
score += 100
if count_player >= 5:
score += 50000 # 必须防守
elif count_player == 4:
score += 5000
elif count_player == 3:
score += 500
elif count_player == 2:
score += 50
return score
def handle_click(self, event):
if self.current_player != 1: # 如果不是玩家回合,直接返回
return
# 将点击位置转换为棋盘坐标
x = round((event.x - self.board_margin) / self.cell_size)
y = round((event.y - self.board_margin) / self.cell_size)
# 检查是否在有效范围内
if 0 <= x < self.board_size and 0 <= y < self.board_size:
if self.board[y][x] == 0: # 如果该位置没有棋子
# 玩家落子
self.place_piece(x, y)
if self.check_win(x, y):
self.window.update()
messagebox.showinfo("游戏结束", "玩家获胜!")
self.reset_game()
return
# 切换到AI回合
self.current_player = 2
# AI落子
ai_x, ai_y = self.get_ai_move()
if ai_x is not None and ai_y is not None:
self.place_piece(ai_x, ai_y)
if self.check_win(ai_x, ai_y):
self.window.update()
messagebox.showinfo("游戏结束", "电脑AI获胜!")
self.reset_game()
return
# 切换回玩家回合
self.current_player = 1
self.redraw_board_and_pieces()
def redraw_board_and_pieces(self):
# 重绘棋盘和所有棋子
self.canvas.delete("all")
canvas_size = self.board_size * self.cell_size + self.board_margin + 30
try:
self.canvas.create_image(canvas_size//2, canvas_size//2,
image=self.bg_image)
except:
self.canvas.create_rectangle(0, 0, canvas_size, canvas_size,
fill='#CDBA96', outline='#CDBA96')
# 更新悔棋按钮位置和文字
button_x = self.board_margin + (self.board_size-1) * self.cell_size / 2
button_y = self.board_margin - 40
self.undo_button.place(x=button_x - 20, y=button_y - 10)
self.undo_button.config(text=f"悔棋(剩余{self.undo_remaining}次)")
self.draw_board()
self.redraw_all_pieces()
def check_win(self, x, y):
directions = [(1,0), (0,1), (1,1), (1,-1)] # 横、竖、右斜、左斜
for dx, dy in directions:
count = 1
winning_pieces = [(x, y)] # 记录获胜棋子的位置
# 正向检查
temp_x, temp_y = x + dx, y + dy
while 0 <= temp_x < self.board_size and 0 <= temp_y < self.board_size:
if self.board[temp_y][temp_x] == self.current_player:
count += 1
winning_pieces.append((temp_x, temp_y))
temp_x += dx
temp_y += dy
else:
break
# 反向检查
temp_x, temp_y = x - dx, y - dy
while 0 <= temp_x < self.board_size and 0 <= temp_y < self.board_size:
if self.board[temp_y][temp_x] == self.current_player:
count += 1
winning_pieces.append((temp_x, temp_y))
temp_x -= dx
temp_y -= dy
else:
break
if count >= 5:
self.highlight_winning_pieces(winning_pieces)
return True
return False
def highlight_winning_pieces(self, positions):
for x, y in positions:
piece_x = self.board_margin + x * self.cell_size
piece_y = self.board_margin + y * self.cell_size
# 绘制高亮边框
self.canvas.create_oval(piece_x-16, piece_y-16,
piece_x+16, piece_y+16,
outline='red',
width=2)
def undo_move(self):
if self.undo_remaining <= 0:
messagebox.showinfo("提示", "悔棋次数已用完!")
return
if len(self.move_history) >= 2:
self.undo_remaining -= 1
# 更新按钮文字
self.undo_button.config(text=f"悔棋(剩余{self.undo_remaining}次)")
# 移除最近的两步棋
self.move_history.pop() # 移除AI的落子
self.move_history.pop() # 移除玩家的落子
# 重置棋盘
self.board = [[0] * self.board_size for _ in range(self.board_size)]
# 重新绘制棋盘
self.redraw_board_and_pieces()
# 重新执行所有历史移动
current_player_backup = self.current_player
for x, y, player in self.move_history:
self.current_player = player
self.board[y][x] = player
self.current_player = current_player_backup
# 重新绘制
self.redraw_board_and_pieces()
def reset_game(self):
self.move_history = [] # 清空历史记录
# 清空棋盘数据
self.board = [[0] * self.board_size for _ in range(self.board_size)]
# 清空画布
self.canvas.delete("all")
# 重新绘制背景
canvas_size = self.board_size * self.cell_size + self.board_margin + 30
try:
# 重新显示背景图片
self.canvas.create_image(canvas_size//2, canvas_size//2,
image=self.bg_image)
except:
# 如果加载图片失败,使用纯色背景
self.canvas.create_rectangle(0, 0, canvas_size, canvas_size,
fill='#CDBA96', outline='#CDBA96')
# 重新绘制棋盘
self.draw_board()
# 重置悔棋次数
self.undo_remaining = 10
self.undo_button.config(text=f"悔棋(剩余{self.undo_remaining}次)")
# 重新绘制棋盘
self.draw_board()
# 重置当前玩家
self.current_player = 1
def run(self):
self.window.mainloop()
if __name__ == "__main__":
game = Gobang()
game.run()
玩家为黑子,电脑AI为白子,支持玩家悔棋,现在可以做为单机娱乐的小游戏了玩了。