Frage

用Python写一个和AI对战的五子棋

Gedanke

Determining the approach

First, I will choose an implementation method, possibly using Pygame for a GUI or a text-based terminal game, to create a Gomoku game with an AI opponent.

Evaluating AI strategies

I’m thinking about straightforward AI methods for Gomoku. Monte Carlo Tree Search or a simple heuristic AI might work well, given the large board size and complexity.

Crafting the AI

I’m focusing on a simple heuristic AI for Gomoku, evaluating moves for immediate wins or threats, and ranking possible moves to boost the score, using a 15x15 board.

Formulating the strategy

I’m pulling together a terminal-based command loop for the AI, defining win-checking and heursitic scoring functions, and focusing on moves adjacent to existing pieces for efficiency.

Charting heuristic scoring

I’m mapping out a heuristic by evaluating consecutive stones in all directions, tallying open ends, and applying a scoring table. Immediate winning moves are also factored in.

Evaluating simpler strategies

I'm thinking through a streamlined approach: simulate AI and opponent moves to spot wins or blocks, then score remaining moves by tallying consecutive stones in each direction.

Assessing potential

I’m finalizing the scoring function to evaluate each candidate move, counting consecutive stones in four directions, identifying open ends, and factoring in winning potential for a comprehensive score.

Crunching the numbers

I’m calculating score tables by examining consecutive stone counts in two directions, identifying open ends, and assigning points to potential winning configurations.

Piecing together scoring

I’m thinking through score allocation: summing direction scores, prioritizing multiple directions, ensuring maximum score for wins, and blocking opponent wins. Evaluating potential moves is central.

Spotting immediate victories

I’m noting the importance of identifying winning moves by setting up a function to simulate moves and check for five consecutive stones in four key directions.

Mapping out win checks

I’m finalizing the check_win function with markers for each direction to ensure accurate identification of winning moves by simulating stones to identify five in a row.

Crafting the game loop

I’m constructing a main loop for the game that handles user inputs, updates the board, checks for wins, and includes AI moves, ensuring clear communication and smooth progression.

Implementing the game

I’m planning functions to initialize the board, print it, check wins, evaluate positions, and manage the main game loop, with a focus on a simple greedy algorithm for move selection.

Enhancing move evaluation

I’m refining candidate move scoring to balance offense and defense in Gomoku, prioritizing moves that maximize defense against opponent threats while also considering AI offensive moves.

Assessing AI strategies

Simulating moves for both AI and player, emphasizing defense against potential player threats. Evaluating and comparing candidate moves to find the optimal decision.

Evaluating candidate moves

I’m assessing candidate moves by simulating both AI and player turns, ensuring optimal strategies. This method helps identify the best move while providing insights into potential outcomes.

Evaluating move potential

I’m developing the evaluate_position function to assess the potential of each move by considering factors like threat defense and strategic advantage, using constants for player and AI.

Assessing move impact

I’m analyzing each move's influence by counting consecutive stones in various directions and checking for open ends to assess potential and threats effectively.

Assessing threat levels

I’m refining the scoring function to handle different threats by counting consecutive stones and open ends in all directions, ensuring each line's potential is quantified accurately.

Simulating stone placement

I’m assessing the impact of stone placement at (x, y) by temporarily placing a stone, evaluating the position, and then resetting the board.

Evaluating stone potentials

I’m working on a Python program to simulate stone placements, evaluating positions, and managing board states. This includes player and opponent dynamics.

Setting up the board

I’m establishing a 15x15 board with labeled coordinates and piece symbols for easy readability. It’s interesting to see how the layout enhances user interaction.

Managing game flow

I’m developing a game loop that alternates player and AI turns, checking for board full conditions, and incorporating a main function to monitor these transitions effectively.

Crafting the game plan

Integrating user input for player moves, automating AI decisions, and assessing win conditions after each turn, while also checking for a full board to declare a draw.

Finalizing AI strategy

I'm wrapping up the AI's move planning, utilizing a scoring system to identify the best choice. This involves assessing potential win conditions, simulating stone placements, and evaluating positions.

Refining the approach

I’m considering a method to simulate stone placement by temporarily marking it on the board, assessing potential outcomes, and then removing it for a more accurate evaluation.

Simulating moves

I’m strategizing to simulate both AI and player moves by temporarily marking stones on the board, evaluating potential outcomes, and then clear the board for an accurate assessment.

Evaluating move potential

I’m refining the ai_move function to evaluate each potential move's impact and adjust for immediate win or block scenarios. This involves a detailed assessment of consequences for each move.

Simulating initial placement

Interestingly enough, in the evaluate_position function, if the candidate stone is placed, I simulate the initial placement by setting board[y][x] and then proceed to evaluate potential moves without physically altering the board.

Simulating board analysis

I’m setting board[y][x] to simulate initial stone placement, then examining contiguous stones and open ends in all directions, considering edge cases and ensuring accurate evaluation of potential moves.

Evaluating potential moves

I’m working on this function to evaluate board scores. It both counts contiguous stones and identifies open ends across four directions. The specifics of get_line_score will further refine this.

Assigning importance

I’m detailing get_line_score to attribute values to different scenarios of contiguous stones and open ends. This method aims to strategically evaluate potential moves on the board.

Confirming victory

I’m mapping out the check_win function to verify if a player has secured a win by spotting five consecutive stones. It methodically investigates all possible winning alignments.

Laying out the options

I’m setting up constants for board size, players, and AI, crafting the has_neighbor function to spot nearby stones. This aims to seamlessly integrate the code for the final implementation.

Confirming functionality

I’m mapping out the main function: it captures player input, checks for victory, initiates AI moves, and manages board printing. Ensuring comprehensive integration and considering edge cases like ties and board exhaustion.

Reasoned about AI五子棋实现,持续 1m 22s

Antwort

下面提供一个简单的 Python 实现,它在终端中运行一个五子棋(又称“连珠”)游戏,玩家(用“X”表示)与 AI(用“O”表示)轮流落子。AI 使用了一个基于局部启发式评分的贪心算法,先检查是否能立即获胜或必须堵住对方的必胜着,再计算每个候选点的得分选择最佳落子位置。

你可以将下面的代码保存为例如 gomoku.py,然后在终端中运行:

python
# -*- coding: utf-8 -*- """ 简单的五子棋(连珠)游戏 玩家(X)与 AI(O)对战 """ import sys # 定义棋盘大小和棋子标识 BOARD_SIZE = 15 PLAYER = 1 # 玩家 AI = 2 # AI def print_board(board): """打印棋盘,行号和列号均从1开始显示""" # 打印列号 print(" ", end="") for x in range(BOARD_SIZE): print(f"{x+1:2}", end=" ") print() for y in range(BOARD_SIZE): print(f"{y+1:2} ", end="") for x in range(BOARD_SIZE): if board[y][x] == 0: print(" .", end=" ") elif board[y][x] == PLAYER: print(" X", end=" ") elif board[y][x] == AI: print(" O", end=" ") print() def check_win(board, x, y, player): """ 检查在 (x, y) 落下 player 的棋子后, 是否形成了连续 5 个棋子(含该点)。 board[y][x] 用来表示棋盘,坐标均从0开始。 """ directions = [(1, 0), (0, 1), (1, 1), (1, -1)] for dx, dy in directions: count = 1 # 正方向 i = 1 while True: nx = x + i * dx ny = y + i * dy if nx < 0 or nx >= BOARD_SIZE or ny < 0 or ny >= BOARD_SIZE or board[ny][nx] != player: break count += 1 i += 1 # 反方向 i = 1 while True: nx = x - i * dx ny = y - i * dy if nx < 0 or nx >= BOARD_SIZE or ny < 0 or ny >= BOARD_SIZE or board[ny][nx] != player: break count += 1 i += 1 if count >= 5: return True return False def get_line_score(count, open_ends): """ 根据某一方向上连续棋子数 count 以及两端的空口数 open_ends 返回一个局部评分。 """ if count >= 5: return 100000 if count == 4: if open_ends == 2: return 10000 elif open_ends == 1: return 1000 if count == 3: if open_ends == 2: return 1000 elif open_ends == 1: return 100 if count == 2: if open_ends == 2: return 100 elif open_ends == 1: return 10 if count == 1: return 10 return 0 def evaluate_position(board, x, y, player): """ 评估在 (x, y) 处落下 player 棋子后的潜力得分, 考察水平方向、垂直方向和两条对角线上的连续棋子情况。 此函数要求在调用前,已经将 board[y][x] 设为 player 。 """ score = 0 directions = [(1, 0), (0, 1), (1, 1), (1, -1)] for dx, dy in directions: count = 1 # 包含候选点本身 open_ends = 0 # 正方向扫描 i = 1 while True: nx = x + i * dx ny = y + i * dy if nx < 0 or nx >= BOARD_SIZE or ny < 0 or ny >= BOARD_SIZE: break if board[ny][nx] == player: count += 1 elif board[ny][nx] == 0: open_ends += 1 break else: break i += 1 # 反方向扫描 i = 1 while True: nx = x - i * dx ny = y - i * dy if nx < 0 or nx >= BOARD_SIZE or ny < 0 or ny >= BOARD_SIZE: break if board[ny][nx] == player: count += 1 elif board[ny][nx] == 0: open_ends += 1 break else: break i += 1 score += get_line_score(count, open_ends) return score def has_neighbor(board, x, y, distance=1): """ 判断 (x, y) 周围 distance 范围内是否存在棋子, 这样可以限制 AI 只在已有棋子附近搜索。 """ for dy in range(-distance, distance + 1): for dx in range(-distance, distance + 1): if dx == 0 and dy == 0: continue nx = x + dx ny = y + dy if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE: if board[ny][nx] != 0: return True return False def ai_move(board): """ AI 搜索所有空位(且附近有棋子),先检查是否有 直接获胜或必须堵住对方必胜着,否则计算启发式得分, 选择得分最高的点。 返回值为 (x, y) 坐标(从 0 开始)。 """ best_score = -1 best_move = None for y in range(BOARD_SIZE): for x in range(BOARD_SIZE): if board[y][x] == 0 and has_neighbor(board, x, y): # 检查 AI 是否可以直接获胜 board[y][x] = AI if check_win(board, x, y, AI): board[y][x] = 0 return (x, y) board[y][x] = 0 # 检查玩家是否在此处落子后会获胜,如果是则堵住 board[y][x] = PLAYER if check_win(board, x, y, PLAYER): board[y][x] = 0 return (x, y) board[y][x] = 0 # 计算该点的启发式评分: # 分别计算 AI 落子和玩家落子时的得分, # 后者乘以一定权重用于防守(这里设为0.9)。 board[y][x] = AI score_ai = evaluate_position(board, x, y, AI) board[y][x] = 0 board[y][x] = PLAYER score_player = evaluate_position(board, x, y, PLAYER) board[y][x] = 0 total_score = score_ai + score_player * 0.9 if total_score > best_score: best_score = total_score best_move = (x, y) return best_move def main(): board = [[0 for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)] print("欢迎来到五子棋游戏!") print("玩家使用 X,AI 使用 O。输入格式为:行 列 (例如 8 8 表示落子于第8行第8列)") while True: print_board(board) # 玩家落子 while True: try: move = input("请输入落子位置 (行 列):") if move.lower() in ["quit", "exit"]: print("退出游戏。") sys.exit(0) row, col = map(int, move.split()) row -= 1 # 转换为 0 索引 col -= 1 if 0 <= row < BOARD_SIZE and 0 <= col < BOARD_SIZE and board[row][col] == 0: break else: print("位置无效或已落子,请重新输入。") except Exception: print("输入格式错误,请输入两个数字,例如:8 8") board[row][col] = PLAYER if check_win(board, col, row, PLAYER): print_board(board) print("恭喜,你赢了!") break # 检查棋盘是否已满 if all(all(cell != 0 for cell in row_data) for row_data in board): print_board(board) print("平局!") break # AI 落子 move_ai = ai_move(board) if move_ai is None: print("平局!") break ai_x, ai_y = move_ai board[ai_y][ai_x] = AI print(f"AI 落子于:第 {ai_y+1} 行,第 {ai_x+1} 列") if check_win(board, ai_x, ai_y, AI): print_board(board) print("很遗憾,你输了!") break # 检查棋盘是否已满 if all(all(cell != 0 for cell in row_data) for row_data in board): print_board(board) print("平局!") break if __name__ == "__main__": main()

代码说明

  1. 棋盘与显示
    棋盘用二维列表表示,大小为 15×15。函数 print_board 将棋盘打印出来,空位显示“.”,玩家的棋子显示“X”,AI 的棋子显示“O”。

  2. 胜负判断
    函数 check_win 从落子点出发,在四个方向上累计连续相同棋子数,若达到 5 则判定为胜利。

  3. 启发式评估
    函数 evaluate_position(结合 get_line_score)在指定位置模拟落子后,考察四个方向上连续棋子数及两端空口数,为该着法打分。

  4. AI 落子策略
    AI 在所有空位中(并且要求附近有棋子,以减少搜索范围)首先检查是否有能直接获胜的点,再检查是否必须堵住玩家的必胜着,最后计算启发式得分,选择分数最高的位置。

  5. 主循环
    main() 函数中,首先由玩家输入落子位置(输入格式为“行 列”,例如“8 8”),然后检查胜负;接着 AI 落子,并显示其落子位置。若棋盘已满则为平局。

此代码只是一个较为简单的示例,AI 的算法比较初级,如果需要更强的 AI,可以考虑加入更深入的搜索算法(如 α–β 剪枝的 minimax 搜索或 Monte Carlo Tree Search)。

运行该程序后,即可在终端中与 AI 对战。Enjoy!

Frage

要有界面啊

Gedanke

Creating the interface

I’m working on a Python Gomoku game with a GUI using tkinter. This involves designing a window, drawing the game board, and handling player and AI moves.

Crafting the interface

I’m creating a Python script with Tkinter to make a Gomoku game. This involves setting up a window, drawing the board, managing moves, and integrating AI logic and win-checking.

Mapping the layout

I’m thinking through the board's structure: a 15x15 grid with cells either 40 or 30 pixels in size, and margin of 20 pixels, using Tkinter to handle the interface.

Utilizing board functions

I’m enhancing the Gomoku game by integrating functions like check_win, ai_move, and evaluate_position. These will be part of the GomokuGame class, simplifying gameplay and AI interactions.

Setting up TicTacToeGUI

I’m detailing the GomokuGUI class with methods for drawing the board, handling clicks, and placing pieces. It interacts with a game board grid and integrates AI move logic.

Integrating game mechanics

I'm adding the game's flow by drawing the board, marking intersections, and using tkinter for victory messages. The game stops when someone wins.

Crafting check_win function

I’m developing the check_win function to identify five consecutive pieces in any direction. This includes forward and backward analysis along four primary directions.

Mapping out AI strategy

I’m visualizing the ai_move function for Gomoku: iterating the board to spot valid moves, assessing positions, and evaluating for potential wins or strategic advantages.

Crafting the check_win function

I’m establishing the check_win function to identify victorious patterns by scanning the board across four main directions. This function reveals if a player has secured a winning sequence.

Animating the game

I’m guiding you through creating a visual representation of Gomoku: drawing the board, interacting with player moves, and leveraging AI for optimal strategies.

Reasoned about five-in-a-row game with UI,持续 24 秒

Antwort

下面提供一个基于 Tkinter 的图形界面版五子棋示例,玩家与 AI 对战。玩家下黑子,AI 下白子。代码中采用了与之前类似的 AI 策略和胜负判断逻辑,但使用 Tkinter 绘制棋盘和棋子,实现鼠标点击落子的交互界面。

你可以将下面代码保存为例如 gomoku_gui.py,然后用 Python 运行即可:

python
import tkinter as tk from tkinter import messagebox # 棋盘和绘图参数 BOARD_SIZE = 15 # 棋盘行列数 CELL_SIZE = 40 # 每个交叉点之间的像素间距 MARGIN = 20 # 棋盘边缘空白 # 棋子标记 PLAYER = 1 # 玩家(黑子) AI = 2 # AI(白子) def check_win(board, x, y, player): """ 检查在 (x, y) 落下 player 棋子后是否获胜(横、竖、斜方向连续 5 个) """ directions = [(1, 0), (0, 1), (1, 1), (1, -1)] for dx, dy in directions: count = 1 # 正方向扫描 i = 1 while True: nx = x + i * dx ny = y + i * dy if nx < 0 or nx >= BOARD_SIZE or ny < 0 or ny >= BOARD_SIZE or board[ny][nx] != player: break count += 1 i += 1 # 反方向扫描 i = 1 while True: nx = x - i * dx ny = y - i * dy if nx < 0 or nx >= BOARD_SIZE or ny < 0 or ny >= BOARD_SIZE or board[ny][nx] != player: break count += 1 i += 1 if count >= 5: return True return False def get_line_score(count, open_ends): """ 根据连续棋子数 count 和两端空口数 open_ends 返回评分 """ if count >= 5: return 100000 if count == 4: if open_ends == 2: return 10000 elif open_ends == 1: return 1000 if count == 3: if open_ends == 2: return 1000 elif open_ends == 1: return 100 if count == 2: if open_ends == 2: return 100 elif open_ends == 1: return 10 if count == 1: return 10 return 0 def evaluate_position(board, x, y, player): """ 评估在 (x, y) 处落下 player 棋子的得分,综合横、竖、斜四个方向 """ score = 0 directions = [(1, 0), (0, 1), (1, 1), (1, -1)] for dx, dy in directions: count = 1 open_ends = 0 # 正方向扫描 i = 1 while True: nx = x + i * dx ny = y + i * dy if nx < 0 or nx >= BOARD_SIZE or ny < 0 or ny >= BOARD_SIZE: break if board[ny][nx] == player: count += 1 elif board[ny][nx] == 0: open_ends += 1 break else: break i += 1 # 反方向扫描 i = 1 while True: nx = x - i * dx ny = y - i * dy if nx < 0 or nx >= BOARD_SIZE or ny < 0 or ny >= BOARD_SIZE: break if board[ny][nx] == player: count += 1 elif board[ny][nx] == 0: open_ends += 1 break else: break i += 1 score += get_line_score(count, open_ends) return score def has_neighbor(board, x, y, distance=1): """ 判断 (x, y) 周围 distance 范围内是否存在棋子, 用以限制 AI 只在已有棋子附近搜索 """ for dy in range(-distance, distance + 1): for dx in range(-distance, distance + 1): if dx == 0 and dy == 0: continue nx = x + dx ny = y + dy if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE: if board[ny][nx] != 0: return True return False def ai_move(board): """ AI 策略: 1. 搜索所有空位(且周围有棋子),首先检查是否能直接获胜, 或者是否需要堵住对方必胜着; 2. 否则计算启发式得分,选择得分最高的点。 返回 (x, y) 坐标(从 0 开始)。 """ best_score = -1 best_move = None for y in range(BOARD_SIZE): for x in range(BOARD_SIZE): if board[y][x] == 0 and has_neighbor(board, x, y): # 检查 AI 是否能直接获胜 board[y][x] = AI if check_win(board, x, y, AI): board[y][x] = 0 return (x, y) board[y][x] = 0 # 检查玩家如果落在此处能获胜,需堵住 board[y][x] = PLAYER if check_win(board, x, y, PLAYER): board[y][x] = 0 return (x, y) board[y][x] = 0 # 计算启发式评分 board[y][x] = AI score_ai = evaluate_position(board, x, y, AI) board[y][x] = 0 board[y][x] = PLAYER score_player = evaluate_position(board, x, y, PLAYER) board[y][x] = 0 total_score = score_ai + score_player * 0.9 if total_score > best_score: best_score = total_score best_move = (x, y) return best_move class GomokuGUI: def __init__(self, master): self.master = master self.master.title("五子棋对战 - 人机对战") # 根据棋盘尺寸计算画布大小 self.canvas_size = MARGIN * 2 + CELL_SIZE * (BOARD_SIZE - 1) self.canvas = tk.Canvas(master, width=self.canvas_size, height=self.canvas_size, bg="#F0D9B5") self.canvas.pack() self.canvas.bind("<Button-1>", self.on_click) # 初始化棋盘(二维列表,0为空,1为玩家,2为AI) self.board = [[0 for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)] self.game_over = False self.draw_board() def draw_board(self): """绘制棋盘网格和已落的棋子""" self.canvas.delete("all") # 绘制网格线 for i in range(BOARD_SIZE): x = MARGIN + i * CELL_SIZE self.canvas.create_line(x, MARGIN, x, self.canvas_size - MARGIN) self.canvas.create_line(MARGIN, x, self.canvas_size - MARGIN, x) # 绘制棋子 for y in range(BOARD_SIZE): for x in range(BOARD_SIZE): if self.board[y][x] != 0: self.draw_piece(x, y, self.board[y][x]) def draw_piece(self, x, y, player): """在 (x, y) 位置绘制棋子""" cx = MARGIN + x * CELL_SIZE cy = MARGIN + y * CELL_SIZE radius = CELL_SIZE // 2 - 2 if player == PLAYER: color = "black" else: color = "white" self.canvas.create_oval(cx - radius, cy - radius, cx + radius, cy + radius, fill=color) def on_click(self, event): """鼠标点击事件:玩家落子,然后由 AI 落子""" if self.game_over: return # 根据点击位置换算出最近的棋盘交点 x = round((event.x - MARGIN) / CELL_SIZE) y = round((event.y - MARGIN) / CELL_SIZE) if x < 0 or x >= BOARD_SIZE or y < 0 or y >= BOARD_SIZE: return if self.board[y][x] != 0: return # 玩家落子 self.board[y][x] = PLAYER self.draw_piece(x, y, PLAYER) if check_win(self.board, x, y, PLAYER): self.game_over = True messagebox.showinfo("游戏结束", "恭喜,你赢了!") return if all(all(cell != 0 for cell in row) for row in self.board): self.game_over = True messagebox.showinfo("游戏结束", "平局!") return # AI 落子 move = ai_move(self.board) if move: ai_x, ai_y = move self.board[ai_y][ai_x] = AI self.draw_piece(ai_x, ai_y, AI) if check_win(self.board, ai_x, ai_y, AI): self.game_over = True messagebox.showinfo("游戏结束", "很遗憾,你输了!") return else: self.game_over = True messagebox.showinfo("游戏结束", "平局!") return if __name__ == "__main__": root = tk.Tk() game = GomokuGUI(root) root.mainloop()

代码说明

  1. 棋盘与绘图
    使用 Tkinter 的 Canvas 组件绘制棋盘网格。棋盘大小由 BOARD_SIZECELL_SIZEMARGIN 定义。每个交叉点绘制一个圆形表示棋子,黑色表示玩家,白色表示 AI。

  2. 胜负判断和 AI 策略
    函数 check_win 用于检查某个落子后是否获胜。ai_move 函数在所有空位(且周围有棋子)中,先检查是否能直接获胜或需要堵对方,然后依据启发式评分选择最佳位置。

  3. 交互逻辑
    鼠标点击事件绑定在 on_click 函数上,玩家点击棋盘后,会在最近的交点落子,然后调用 AI 落子,并检查输赢。如果游戏结束,则弹出对话框提示结果。

运行该程序后,即可看到一个带有棋盘的窗口,通过点击棋盘进行对战。你可以根据需要扩展完善界面效果和 AI 算法。

Frage

改成AI互相对战

Gedanke

Adjusting the setup

I’m thinking through adapting the game from human vs AI to AI vs AI, possibly using the same algorithm or different parameters. This could include automating moves with buttons or timers.

Developing logic

I’m setting up a game loop to monitor victories and time subsequent moves with tkinter's after method, alternating between two identical AIs using the same heuristic.

Tweaking the function

I’m reshaping ai_move to get_best_move for AI vs AI scenarios, where it computes moves for the given player and opponent, using designated labels for clarity.

Mapping the function

I’m honing get_ai_move to evaluate board positions for both the current player and opponent, utilizing labels like "black" and "white" to ensure accurate move determination.

Simulating and alternating

I’m updating GomokuAIVsAI to simulate moves for the opponent, check for wins, and alternate AI players using after method for real-time updates.

Setting up gameplay

I’m ensuring the board refreshes after each move, implementing a "start" button for auto-start, and scheduling the next move with a delay to keep the game dynamic and engaging.

Assigning roles

I’m setting "AI1" as black and "AI2" as white, using BLACK = 1 and WHITE = 2 for clarity. The get_ai_move function will handle board, player, and opponent parameters.

Forming the strategy

I’m assembling the get_ai_move function to simulate and evaluate moves for both players, using a heuristic to determine the best move based on board and opponent positions.

Setting up moves

I’m detailing the next_move method to manage the game loop, including checking for game status, updating the board, drawing pieces, verifying wins, and scheduling the next move with a delay.

Setting up the game

I’m working on the GomokuAIVsAI class, defining Board and cell sizes, piece colors (BLACK and WHITE), and key functions like check_win and get_ai_move.

Reasoned about AI vs AI gomoku,持续 38 秒

Antwort

下面给出一个基于 Tkinter 的示例程序,实现两位 AI 互相对战的五子棋。程序中采用与之前类似的胜负判断和启发式评分算法,不再响应鼠标点击,而是由程序自动按照一定延时轮流调用 AI 落子,直至分出胜负或平局。

将下面代码保存为例如 gomoku_ai_vs_ai.py,运行后即可看到棋盘窗口,AI 会自动对战。

python
import tkinter as tk from tkinter import messagebox # 棋盘和绘图参数 BOARD_SIZE = 15 # 棋盘行列数 CELL_SIZE = 40 # 每个交叉点之间的像素间距 MARGIN = 20 # 棋盘边缘空白 # 棋子标记(两位 AI 分别用黑子和白子) BLACK = 1 # 黑子 WHITE = 2 # 白子 def check_win(board, x, y, player): """ 检查在 (x, y) 落下 player 棋子后是否获胜(横、竖、斜方向连续 5 个) """ directions = [(1, 0), (0, 1), (1, 1), (1, -1)] for dx, dy in directions: count = 1 # 正方向扫描 i = 1 while True: nx = x + i * dx ny = y + i * dy if nx < 0 or nx >= BOARD_SIZE or ny < 0 or ny >= BOARD_SIZE or board[ny][nx] != player: break count += 1 i += 1 # 反方向扫描 i = 1 while True: nx = x - i * dx ny = y - i * dy if nx < 0 or nx >= BOARD_SIZE or ny < 0 or ny >= BOARD_SIZE or board[ny][nx] != player: break count += 1 i += 1 if count >= 5: return True return False def get_line_score(count, open_ends): """ 根据连续棋子数 count 和两端空口数 open_ends 返回评分 """ if count >= 5: return 100000 if count == 4: if open_ends == 2: return 10000 elif open_ends == 1: return 1000 if count == 3: if open_ends == 2: return 1000 elif open_ends == 1: return 100 if count == 2: if open_ends == 2: return 100 elif open_ends == 1: return 10 if count == 1: return 10 return 0 def evaluate_position(board, x, y, player): """ 评估在 (x, y) 处落下 player 棋子的得分,综合横、竖、斜四个方向 此函数要求在调用前,已将 board[y][x] 设为 player """ score = 0 directions = [(1, 0), (0, 1), (1, 1), (1, -1)] for dx, dy in directions: count = 1 # 包含当前棋子 open_ends = 0 # 正方向扫描 i = 1 while True: nx = x + i * dx ny = y + i * dy if nx < 0 or nx >= BOARD_SIZE or ny < 0 or ny >= BOARD_SIZE: break if board[ny][nx] == player: count += 1 elif board[ny][nx] == 0: open_ends += 1 break else: break i += 1 # 反方向扫描 i = 1 while True: nx = x - i * dx ny = y - i * dy if nx < 0 or nx >= BOARD_SIZE or ny < 0 or ny >= BOARD_SIZE: break if board[ny][nx] == player: count += 1 elif board[ny][nx] == 0: open_ends += 1 break else: break i += 1 score += get_line_score(count, open_ends) return score def has_neighbor(board, x, y, distance=1): """ 判断 (x, y) 周围 distance 范围内是否存在棋子, 用以限制 AI 只在已有棋子附近搜索 """ for dy in range(-distance, distance + 1): for dx in range(-distance, distance + 1): if dx == 0 and dy == 0: continue nx = x + dx ny = y + dy if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE: if board[ny][nx] != 0: return True return False def get_ai_move(board, player, opponent): """ 为当前 player 寻找最佳落子位置,考虑: 1. 是否能直接获胜; 2. 是否需要堵住对方必胜; 3. 否则计算启发式评分。 返回 (x, y) 坐标(从 0 开始),如果找不到则返回 None """ best_score = -1 best_move = None for y in range(BOARD_SIZE): for x in range(BOARD_SIZE): if board[y][x] == 0 and has_neighbor(board, x, y): # 检查当前 player 是否能直接获胜 board[y][x] = player if check_win(board, x, y, player): board[y][x] = 0 return (x, y) board[y][x] = 0 # 检查对方如果落子能获胜,则必须堵住 board[y][x] = opponent if check_win(board, x, y, opponent): board[y][x] = 0 return (x, y) board[y][x] = 0 # 启发式评分 board[y][x] = player score_player = evaluate_position(board, x, y, player) board[y][x] = 0 board[y][x] = opponent score_opponent = evaluate_position(board, x, y, opponent) board[y][x] = 0 total_score = score_player + score_opponent * 0.9 if total_score > best_score: best_score = total_score best_move = (x, y) return best_move class GomokuAIVsAI: def __init__(self, master): self.master = master self.master.title("五子棋 AI 互相对战") # 根据棋盘尺寸计算画布大小 self.canvas_size = MARGIN * 2 + CELL_SIZE * (BOARD_SIZE - 1) self.canvas = tk.Canvas(master, width=self.canvas_size, height=self.canvas_size, bg="#F0D9B5") self.canvas.pack() # 初始化棋盘(二维列表,0为空,BLACK 为黑子,WHITE 为白子) self.board = [[0 for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)] self.game_over = False # 先手为黑子 self.current_turn = BLACK self.draw_board() # 开始自动对战,每隔一定时间执行下一步 self.master.after(500, self.next_move) # 500 毫秒后开始下一步 def draw_board(self): """绘制棋盘网格和所有已落棋子""" self.canvas.delete("all") # 绘制网格 for i in range(BOARD_SIZE): x = MARGIN + i * CELL_SIZE self.canvas.create_line(x, MARGIN, x, self.canvas_size - MARGIN) self.canvas.create_line(MARGIN, x, self.canvas_size - MARGIN, x) # 绘制已落的棋子 for y in range(BOARD_SIZE): for x in range(BOARD_SIZE): if self.board[y][x] != 0: self.draw_piece(x, y, self.board[y][x]) def draw_piece(self, x, y, player): """在棋盘坐标 (x, y) 处绘制棋子""" cx = MARGIN + x * CELL_SIZE cy = MARGIN + y * CELL_SIZE radius = CELL_SIZE // 2 - 2 if player == BLACK: color = "black" else: color = "white" self.canvas.create_oval(cx - radius, cy - radius, cx + radius, cy + radius, fill=color) def next_move(self): """由当前 AI 落子,并判断输赢,然后切换回合""" if self.game_over: return # 根据当前回合决定双方角色 if self.current_turn == BLACK: opponent = WHITE else: opponent = BLACK move = get_ai_move(self.board, self.current_turn, opponent) if move is None: self.game_over = True messagebox.showinfo("游戏结束", "平局!") return x, y = move self.board[y][x] = self.current_turn self.draw_piece(x, y, self.current_turn) if check_win(self.board, x, y, self.current_turn): self.game_over = True winner = "黑子" if self.current_turn == BLACK else "白子" messagebox.showinfo("游戏结束", f"{winner}获胜!") return # 检查棋盘是否已满 if all(all(cell != 0 for cell in row) for row in self.board): self.game_over = True messagebox.showinfo("游戏结束", "平局!") return # 切换回合:利用 3 - current_turn (1变2,2变1) self.current_turn = 3 - self.current_turn # 每隔一定延时后调用下一步 self.master.after(500, self.next_move) if __name__ == "__main__": root = tk.Tk() game = GomokuAIVsAI(root) root.mainloop()

代码说明

  1. 公共函数

    • check_win:判断在某个位置落子后是否形成连续五子。
    • evaluate_positionget_line_score:根据当前落子后各个方向的棋型,给出启发式评分。
    • has_neighbor:用于限制 AI 只在已有棋子附近搜索。
    • get_ai_move:为给定的当前玩家(player)计算最佳落子点,同时考虑直接获胜和阻止对方必胜。
  2. 界面与对战逻辑

    • GomokuAIVsAI 负责绘制棋盘、显示棋子,并在 next_move 方法中自动由当前 AI 落子。
    • 每步落子后检查是否有胜者或平局,若游戏未结束,则利用 after 方法延时调用下一步,使得对战过程自动进行。

运行该程序后,会弹出一个窗口显示棋盘,黑白两方 AI 会自动落子并互相对战,直到分出胜负或平局。你可以根据需要调整延时或进一步完善 AI 算法。

Frage

要有过程啊,我要看,不要之间计算出结果

Gedanke

考虑开发需求

这让我想到使用Python编写一个能与AI对战五子棋的程序,同时要有界面并且显示AI互相对战的过程,而不仅仅是展现计算结果。

考虑需求

用户想要用Python创建一个能与AI对战五子棋的程序,且显示AI之间的对战过程。对需求进行了详细分析。

已推理,持续 几秒

Antwort

如果你希望在 AI 对战过程中能够看到每一步的执行过程,而不是直接计算出最终结果,我们可以通过添加逐步显示的功能来实现。具体来说,我们会让程序在每一步落子后显示当前棋盘状态,并且添加一个按钮来让游戏继续进行。这样你可以在界面上看到每一步的AI决策过程。

下面是修改后的代码:

python
import tkinter as tk from tkinter import messagebox # 棋盘和绘图参数 BOARD_SIZE = 15 # 棋盘行列数 CELL_SIZE = 40 # 每个交叉点之间的像素间距 MARGIN = 20 # 棋盘边缘空白 # 棋子标记(两位 AI 分别用黑子和白子) BLACK = 1 # 黑子 WHITE = 2 # 白子 def check_win(board, x, y, player): """ 检查在 (x, y) 落下 player 棋子后是否获胜(横、竖、斜方向连续 5 个) """ directions = [(1, 0), (0, 1), (1, 1), (1, -1)] for dx, dy in directions: count = 1 # 正方向扫描 i = 1 while True: nx = x + i * dx ny = y + i * dy if nx < 0 or nx >= BOARD_SIZE or ny < 0 or ny >= BOARD_SIZE or board[ny][nx] != player: break count += 1 i += 1 # 反方向扫描 i = 1 while True: nx = x - i * dx ny = y - i * dy if nx < 0 or nx >= BOARD_SIZE or ny < 0 or ny >= BOARD_SIZE or board[ny][nx] != player: break count += 1 i += 1 if count >= 5: return True return False def get_line_score(count, open_ends): """ 根据连续棋子数 count 和两端空口数 open_ends 返回评分 """ if count >= 5: return 100000 if count == 4: if open_ends == 2: return 10000 elif open_ends == 1: return 1000 if count == 3: if open_ends == 2: return 1000 elif open_ends == 1: return 100 if count == 2: if open_ends == 2: return 100 elif open_ends == 1: return 10 if count == 1: return 10 return 0 def evaluate_position(board, x, y, player): """ 评估在 (x, y) 处落下 player 棋子的得分,综合横、竖、斜四个方向 此函数要求在调用前,已将 board[y][x] 设为 player """ score = 0 directions = [(1, 0), (0, 1), (1, 1), (1, -1)] for dx, dy in directions: count = 1 # 包含当前棋子 open_ends = 0 # 正方向扫描 i = 1 while True: nx = x + i * dx ny = y + i * dy if nx < 0 or nx >= BOARD_SIZE or ny < 0 or ny >= BOARD_SIZE: break if board[ny][nx] == player: count += 1 elif board[ny][nx] == 0: open_ends += 1 break else: break i += 1 # 反方向扫描 i = 1 while True: nx = x - i * dx ny = y - i * dy if nx < 0 or nx >= BOARD_SIZE or ny < 0 or ny >= BOARD_SIZE: break if board[ny][nx] == player: count += 1 elif board[ny][nx] == 0: open_ends += 1 break else: break i += 1 score += get_line_score(count, open_ends) return score def has_neighbor(board, x, y, distance=1): """ 判断 (x, y) 周围 distance 范围内是否存在棋子, 用以限制 AI 只在已有棋子附近搜索 """ for dy in range(-distance, distance + 1): for dx in range(-distance, distance + 1): if dx == 0 and dy == 0: continue nx = x + dx ny = y + dy if 0 <= nx < BOARD_SIZE and 0 <= ny < BOARD_SIZE: if board[ny][nx] != 0: return True return False def get_ai_move(board, player, opponent): """ 为当前 player 寻找最佳落子位置,考虑: 1. 是否能直接获胜; 2. 是否需要堵住对方必胜; 3. 否则计算启发式评分。 返回 (x, y) 坐标(从 0 开始),如果找不到则返回 None """ best_score = -1 best_move = None for y in range(BOARD_SIZE): for x in range(BOARD_SIZE): if board[y][x] == 0 and has_neighbor(board, x, y): # 检查当前 player 是否能直接获胜 board[y][x] = player if check_win(board, x, y, player): board[y][x] = 0 return (x, y) board[y][x] = 0 # 检查对方如果落子能获胜,则必须堵住 board[y][x] = opponent if check_win(board, x, y, opponent): board[y][x] = 0 return (x, y) board[y][x] = 0 # 启发式评分 board[y][x] = player score_player = evaluate_position(board, x, y, player) board[y][x] = 0 board[y][x] = opponent score_opponent = evaluate_position(board, x, y, opponent) board[y][x] = 0 total_score = score_player + score_opponent * 0.9 if total_score > best_score: best_score = total_score best_move = (x, y) return best_move class GomokuAIVsAI: def __init__(self, master): self.master = master self.master.title("五子棋 AI 互相对战") # 根据棋盘尺寸计算画布大小 self.canvas_size = MARGIN * 2 + CELL_SIZE * (BOARD_SIZE - 1) self.canvas = tk.Canvas(master, width=self.canvas_size, height=self.canvas_size, bg="#F0D9B5") self.canvas.pack() # 初始化棋盘(二维列表,0为空,BLACK 为黑子,WHITE 为白子) self.board = [[0 for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)] self.game_over = False self.current_turn = BLACK # 先手为黑子 self.draw_board() # 创建控制按钮 self.step_button = tk.Button(master, text="下一步", command=self.next_move) self.step_button.pack() def draw_board(self): """绘制棋盘网格和所有已落棋子""" self.canvas.delete("all") # 绘制网格 for i in range(BOARD_SIZE): x = MARGIN + i * CELL_SIZE self.canvas.create_line(x, MARGIN, x, self.canvas_size - MARGIN) self.canvas.create_line(MARGIN, x, self.canvas_size - MARGIN, x) # 绘制已落的棋子 for y in range(BOARD_SIZE): for x in range(BOARD_SIZE): if self.board[y][x] != 0: self.draw_piece(x, y, self.board[y][x]) def draw_piece(self, x, y, player): """在棋盘坐标 (x, y) 处绘制棋子""" cx = MARGIN + x * CELL_SIZE cy = MARGIN + y * CELL_SIZE radius = CELL_SIZE // 2 - 2 if player == BLACK: color = "black" else: color = "white" self.canvas.create_oval(cx - radius, cy - radius, cx + radius, cy + radius, fill=color) def next_move(self): """由当前 AI 落子,并判断输赢,然后切换回合""" if self.game_over: return # 根据当前回合决定双方角色 if self.current_turn == BLACK: opponent = WHITE else: opponent = BLACK move = get_ai_move(self.board, self.current_turn, opponent) if move is None: self.game_over = True messagebox.showinfo("游戏结束", "平局!") return x, y = move self.board[y][x] = self.current_turn self.draw_piece(x, y, self.current_turn) if check_win(self.board, x, y, self.current_turn): self.game_over = True winner = "黑子" if self.current_turn == BLACK else "白子" messagebox.showinfo("游戏结束", f"{winner}获胜!") return # 检查棋盘是否已满 if all(all(cell != 0 for cell in row) for row in self.board): self.game_over = True messagebox.showinfo("游戏结束", "平局!") return # 切换回合:利用 3 - current_turn (1变2,2变1) self.current_turn = 3 - self.current_turn if __name__ == "__main__": root = tk.Tk() game = GomokuAIVsAI(root) root.mainloop()

关键修改

  1. 步骤控制
    通过 self.step_button 按钮手动触发每一步,用户可以点击按钮,看到每一步 AI 的决策过程,而不是计算出所有结果。

  2. 逐步展示每一步
    每点击一次按钮,AI 会根据当前局面计算出最佳落子位置并更新棋盘,直到游戏结束。

  3. 游戏结束提示
    游戏结束时会弹出一个消息框,提示胜者或平局。

如何使用

  1. 程序运行后,你会看到一个棋盘。
  2. 每点击一次“下一步”按钮,AI 就会落下一步棋,并刷新棋盘。
  3. 游戏会自动进行,直到分出胜负或平局。

Diesen Q&A teilen