【创新】教你用python做五子棋AI(含代码)
老样子先讲思路:
观点由作者老程想出,只供参考
- FiveChessGame 类的初始化:
-
创建主窗口,设置标题和背景颜色。
-
创建画布并绑定鼠标点击事件。
- draw_board 方法:
- 在画布上绘制棋盘的横竖线条。
- place_piece 方法:
-
根据鼠标点击位置确定棋子放置的行列。
-
如果位置为空,放置棋子并更新棋盘状态。
-
检查是否获胜或平局,如果是则显示相应的消息框并更新游戏状态。
-
切换当前玩家,如果是 AI 回合则调用 ai_move 方法。
- draw_piece 方法:
- 在指定位置绘制棋子。
- check_win 方法:
- 遍历整个棋盘,对于每个非空棋子,调用 check_win_at_pos 方法检查是否获胜。
- check_win_at_pos 方法:
- 针对特定位置和棋子类型,在四个方向上检查是否形成五子连珠。
- is_draw 方法:
- 检查棋盘是否已满,以判断是否平局。
- ai_move 方法:
-
进行一定深度的搜索,计算每个可能落子位置的得分。
-
选择得分最高的位置放置 AI 的棋子。
-
检查放置后是否获胜或平局,并更新游戏状态和显示相应消息框。
- minimax 方法:
- 实现了极小极大算法,根据当前深度、搜索范围和玩家角色(最大化或最小化)进行递归搜索,计算局面得分。
- evaluate_position 方法:
- 评估当前棋盘局面的得分,考虑棋子的布局情况,如活三、活四等。
- check_open_three 和 check_open_four 方法:
- 分别检查是否存在活三或活四的情况。
- handle_click 方法:
- 处理鼠标点击事件,调用 place_piece 方法放置玩家的棋子。
- run 方法:
- 绘制棋盘并启动主事件循环。
总体思路是通过各种方法实现五子棋的游戏逻辑,包括玩家和 AI 的落子、胜负判断、局面评估等,以实现一个可交互的五子棋游戏,并且 AI 具有一定的智能水平。
理论已明确,以下是实践:
首先是导入模块
import tkinter as tk
from tkinter import messagebox
剩下是修改原五子棋的代码,添加了AI,增强了算法能力。
BOARD_SIZE = 20
GRID_SIZE = 30
WHITE = "white"
BLACK = "black"
YELLOW = "yellow"
PLAYER1_COLOR = BLACK
PLAYER2_COLOR = WHITE
GAME_CONTINUE = 0
PLAYER1_WIN = 1
PLAYER2_WIN = 2
DRAW = 3
class FiveChessGame:
def __init__(self):
self.root = tk.Tk()
self.root.title("五子棋游戏")
self.root.configure(bg=YELLOW)
self.canvas = tk.Canvas(self.root, width=BOARD_SIZE * GRID_SIZE, height=BOARD_SIZE * GRID_SIZE)
self.canvas.pack()
self.board = [[0 for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)]
self.current_player = PLAYER1_COLOR
self.game_state = GAME_CONTINUE
self.canvas.bind("<Button-1>", self.handle_click)
def draw_board(self):
for i in range(BOARD_SIZE):
self.canvas.create_line(0, i * GRID_SIZE, BOARD_SIZE * GRID_SIZE, i * GRID_SIZE)
self.canvas.create_line(i * GRID_SIZE, 0, i * GRID_SIZE, BOARD_SIZE * GRID_SIZE)
def place_piece(self, x, y):
row = y // GRID_SIZE
col = x // GRID_SIZE
if self.board[row][col] == 0:
self.board[row][col] = 1 if self.current_player == PLAYER1_COLOR else 2
self.draw_piece(col * GRID_SIZE, row * GRID_SIZE)
if self.check_win(): # 不再传递 row 和 col
if self.current_player == PLAYER1_COLOR:
self.game_state = PLAYER1_WIN
messagebox.showinfo("游戏结果", "玩家胜利!")
else:
self.game_state = PLAYER2_WIN
messagebox.showinfo("游戏结果", "AI 胜利!")
elif self.is_draw():
self.game_state = DRAW
messagebox.showinfo("游戏结果", "平局!")
else:
self.current_player = PLAYER2_COLOR if self.current_player == PLAYER1_COLOR else PLAYER1_COLOR
if self.current_player == PLAYER2_COLOR:
self.ai_move()
def draw_piece(self, x, y):
color = self.current_player
self.canvas.create_oval(x + 5, y + 5, x + GRID_SIZE - 5, y + GRID_SIZE - 5, fill=color)
def check_win(self): # 修改为不需要 row 和 col 参数
for row in range(BOARD_SIZE):
for col in range(BOARD_SIZE):
if self.board[row][col]!= 0 and self.check_win_at_pos(row, col): # 调用新的辅助方法
return True
return False
def check_win_at_pos(self, row, col): # 新的辅助方法来检查特定位置是否导致获胜
directions = [(1, 0), (0, 1), (1, 1), (1, -1)]
piece_type = self.board[row][col]
for dr, dc in directions:
count = 1
r, c = row + dr, col + dc
while 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE and self.board[r][c] == piece_type:
count += 1
r += dr
c += dc
r, c = row - dr, col - dc
while 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE and self.board[r][c] == piece_type:
count += 1
r -= dr
c -= dc
if count >= 5:
return True
return False
def is_draw(self):
for row in self.board:
for col in row:
if col == 0:
return False
return True
def ai_move(self):
max_depth = 2 # 增加搜索深度,可根据性能和需求调整
best_score = float('-inf')
best_move = None
for row in range(BOARD_SIZE):
for col in range(BOARD_SIZE):
if self.board[row][col] == 0:
self.board[row][col] = 2
score = self.minimax(max_depth, float('-inf'), float('inf'), False)
self.board[row][col] = 0
if score > best_score:
best_score = score
best_move = (col, row)
if best_move:
col, row = best_move
self.board[row][col] = 2
self.draw_piece(col * GRID_SIZE, row * GRID_SIZE)
if self.check_win(): # 不再传递 row 和 col
self.game_state = PLAYER2_WIN
messagebox.showinfo("游戏结果", "AI 胜利!")
elif self.is_draw():
self.game_state = DRAW
messagebox.showinfo("游戏结果", "平局!")
else:
self.current_player = PLAYER1_COLOR
def minimax(self, depth, alpha, beta, is_maximizing_player):
if depth == 0 or self.check_win(): # 不再传递 row 和 col
return self.evaluate_position()
if is_maximizing_player:
max_eval = float('-inf')
for row in range(BOARD_SIZE):
for col in range(BOARD_SIZE):
if self.board[row][col] == 0:
self.board[row][col] = 2
eval_score = self.minimax(depth - 1, alpha, beta, False)
self.board[row][col] = 0
max_eval = max(max_eval, eval_score)
alpha = max(alpha, eval_score)
if beta <= alpha:
break
return max_eval
else:
min_eval = float('inf')
for row in range(BOARD_SIZE):
for col in range(BOARD_SIZE):
if self.board[row][col] == 0:
self.board[row][col] = 1
eval_score = self.minimax(depth - 1, alpha, beta, True)
self.board[row][col] = 0
min_eval = min(min_eval, eval_score)
beta = min(beta, eval_score)
if beta <= alpha:
break
return min_eval
def evaluate_position(self):
score = 0
# 考虑更多的布局因素,如活三、活四等
for row in range(BOARD_SIZE):
for col in range(BOARD_SIZE):
if self.board[row][col] == 2:
for dr, dc in [(1, 0), (0, 1), (1, 1), (1, -1)]:
count = 1
r, c = row + dr, col + dc
while 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE and self.board[r][c] == 2:
count += 1
r += dr
c += dc
r, c = row - dr, col - dc
while 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE and self.board[r][c] == 2:
count += 1
r -= dr
c -= dc
if count == 2:
score += 20
elif count == 3:
score += 100
elif count == 4:
score += 1000
elif count == 3 and self.check_open_three(row, col, dr, dc): # 活三
score += 500
elif count == 4 and self.check_open_four(row, col, dr, dc): # 活四
score += 2000
return score
def check_open_three(self, row, col, dr, dc):
count = 1
r, c = row + dr, col + dc
blocked = False
while 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE:
if self.board[r][c] == 0:
break
elif self.board[r][c] == 2:
count += 1
r += dr
c += dc
else:
blocked = True
break
r, c = row - dr, col - dc
while 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE:
if self.board[r][c] == 0:
break
elif self.board[r][c] == 2:
count += 1
r -= dr
c -= dc
else:
blocked = True
break
return count == 3 and not blocked
def check_open_four(self, row, col, dr, dc):
count = 1
r, c = row + dr, col + dc
blocked = False
while 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE:
if self.board[r][c] == 0:
break
elif self.board[r][c] == 2:
count += 1
r += dr
c += dc
else:
blocked = True
break
r, c = row - dr, col - dc
while 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE:
if self.board[r][c] == 0:
break
elif self.board[r][c] == 2:
count += 1
r -= dr
c -= dc
else:
blocked = True
break
return count == 4 and not blocked
def handle_click(self, event):
x = event.x
y = event.y
self.place_piece(x, y)
def run(self):
self.draw_board()
self.root.mainloop()
if __name__ == "__main__":
game = FiveChessGame()
game.run()
以上是原代码,运行可能有点慢,这十分考验了高配置的电脑,低配置的需要等待一段时间初始化。
最后,请大家多多支持老程,老程花了两天时间研究,之后老程后更新更多的作品。