使用python的围棋落子策略
我们基于python进行实现了一个完整的围棋自动对弈系统,包含游戏逻辑、AI算法和图形界面。
1. 整体架构
系统包含三个核心组件:
GoGame类:实现围棋核心规则(落子、提子、打劫、胜负判定等)
GoAI类:实现不同难度的AI决策算法
GoGUI类:实现图形界面和用户交互
2. 围棋规则实现 (GoGame类)
核心功能:
落子逻辑:检查位置合法性、打劫规则、提子操作
气(liberty)计算:递归检查棋子是否有气
提子逻辑:移除无气的棋子组
胜负判定:双方连续pass时结束游戏,根据提子数判定胜负
棋局记录:保存完整的棋谱信息到JSON文件
import pygame
import numpy as np
import sys
import random
import time
import json
import os
from datetime import datetime
from enum import Enum# 确保中文字体支持
def get_font_path():"""尝试获取系统中可用的中文字体路径"""# 常见的中文字体路径possible_fonts = ["C:/Windows/Fonts/simhei.ttf", # Windows 黑体"C:/Windows/Fonts/simkai.ttf", # Windows 楷体"C:/Windows/Fonts/simsun.ttc", # Windows 宋体"/System/Library/Fonts/PingFang.ttc", # macOS 苹方"/System/Library/Fonts/STHeiti Medium.ttc", # macOS 黑体"/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf", # Linux"/usr/share/fonts/truetype/wqy/wqy-microhei.ttc", # Linux 文泉驿]for font_path in possible_fonts:if os.path.exists(font_path):return font_path# 如果找不到中文字体,返回None(Pygame会使用默认字体)return Noneclass Stone(Enum):EMPTY = 0BLACK = 1WHITE = 2class GoGame:def __init__(self, board_size=19):self.board_size = board_sizeself.board = np.zeros((board_size, board_size), dtype=int)self.current_player = Stone.BLACKself.last_capture = None # 用于打劫规则self.game_over = Falseself.winner = Noneself.captured_black = 0self.captured_white = 0self.move_history = []self.consecutive_passes = 0self.start_time = datetime.now()def place_stone(self, x, y):"""尝试在指定位置落子"""if self.game_over or self.board[x][y] != Stone.EMPTY.value:return False# 保存当前棋盘状态用于打劫判断temp_board = self.board.copy()# 尝试落子result = self._play_stone(x, y, self.current_player.value)if result == -1: # 位置已有棋子return Falseelif result == -2: # 禁着点return Falseelif result == -3: # 打劫违规return Falseelse:# 更新提子计数if self.current_player == Stone.BLACK:self.captured_white += resultelse:self.captured_black += result# 记录落子历史self.move_history.append((x, y, self.current_player, result))# 重置连续pass计数self.consecutive_passes = 0# 切换玩家self.current_player = Stone.WHITE if self.current_player == Stone.BLACK else Stone.BLACKreturn Truedef pass_turn(self):"""跳过当前回合"""self.consecutive_passes += 1self.move_history.append(("PASS", self.current_player))self.current_player = Stone.WHITE if self.current_player == Stone.BLACK else Stone.BLACK# 检查游戏是否结束(双方连续pass)if self.consecutive_passes >= 2:self.game_over = True# 简单计分规则:提子数多者获胜if self.captured_black > self.captured_white:self.winner = Stone.BLACKelif self.captured_white > self.captured_black:self.winner = Stone.WHITEelse:self.winner = None # 平局def _play_stone(self, x, y, stone):"""核心落子逻辑"""# 检查位置是否已有棋子if self.board[x][y] != Stone.EMPTY.value:return -1# 保存原始棋盘状态original_board = self.board.copy()original_last_capture = self.last_capture# 放置棋子self.board[x][y] = stone# 检查打劫规则if self.last_capture is not None and (x, y) == self.last_capture:# 恢复棋盘self.board = original_boardself.last_capture = original_last_capturereturn -3# 提子captured = self._remove_dead_stones(3 - stone)# 检查自己是否无气if not self._has_liberty(x, y, stone) and captured == 0:# 恢复棋盘self.board = original_boardself.last_capture = original_last_capturereturn -2# 更新打劫记录if captured == 1:self.last_capture = (x, y)else:self.last_capture = Nonereturn captureddef _remove_dead_stones(self, stone):"""移除无气的棋子"""captured = 0visited = np.zeros((self.board_size, self.board_size), dtype=bool)for i in range(self.board_size):for j in range(self.board_size):if self.board[i][j] == stone and not visited[i][j]:if not self._has_group_liberty(i, j, stone):captured += self._remove_group(i, j, stone)visited[i][j] = Truereturn captureddef _has_group_liberty(self, x, y, stone):"""检查棋子组是否有气"""visited = np.zeros((self.board_size, self.board_size), dtype=bool)return self._has_liberty(x, y, stone, visited)def _has_liberty(self, x, y, stone, visited=None):"""递归检查棋子是否有气"""if visited is None:visited = np.zeros((self.board_size, self.board_size), dtype=bool)# 边界检查if x < 0 or x >= self.board_size or y < 0 or y >= self.board_size:return False# 跳过已访问的位置if visited[x][y]:return Falsevisited[x][y] = True# 空位置表示有气if self.board[x][y] == Stone.EMPTY.value:return True# 不是当前棋子类型if self.board[x][y] != stone:return False# 检查四个方向return (self._has_liberty(x - 1, y, stone, visited) orself._has_liberty(x + 1, y, stone, visited) orself._has_liberty(x, y - 1, stone, visited) orself._has_liberty(x, y + 1, stone, visited))def _remove_group(self, x, y, stone):"""移除整个棋子组"""if (x < 0 or x >= self.board_size ory < 0 or y >= self.board_size orself.board[x][y] != stone):return 0# 移除当前棋子self.board[x][y] = Stone.EMPTY.valuecount = 1# 递归移除相邻的同色棋子count += self._remove_group(x - 1, y, stone)count += self._remove_group(x + 1, y, stone)count += self._remove_group(x, y - 1, stone)count += self._remove_group(x, y + 1, stone)return countdef save_game_record(self, filename=None):"""保存棋局记录到文件"""if filename is None:# 生成默认文件名timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")filename = f"go_game_{timestamp}.json"# 创建记录目录record_dir = "game_records"if not os.path.exists(record_dir):os.makedirs(record_dir)filepath = os.path.join(record_dir, filename)# 构建记录数据game_data = {"board_size": self.board_size,"start_time": self.start_time.isoformat(),"end_time": datetime.now().isoformat(),"moves": [],"captured_black": self.captured_black,"captured_white": self.captured_white,"winner": str(self.winner) if self.winner else "Draw","move_count": len(self.move_history)}# 添加每一步棋for i, move in enumerate(self.move_history):if move[0] == "PASS":game_data["moves"].append({"move_number": i + 1,"player": str(move[1]),"action": "PASS"})else:x, y, player, captures = movegame_data["moves"].append({"move_number": i + 1,"player": str(player),"action": "PLACE","x": x,"y": y,"captures": captures})# 保存到文件with open(filepath, 'w', encoding='utf-8') as f:json.dump(game_data, f, ensure_ascii=False, indent=2)return filepathclass GoAI:"""改进的围棋AI,具有攻击性策略"""def __init__(self, level=1, aggressiveness=1.0):self.level = level # AI难度级别 (1-简单, 2-中等, 3-困难)self.aggressiveness = aggressiveness # 攻击性系数 (0.0-1.0)def make_move(self, game):"""根据当前游戏状态做出决策"""# 如果游戏结束,不做任何操作if game.game_over:return None# 简单AI:随机选择空位落子,但优先选择靠近对方棋子的位置if self.level == 1:empty_positions = []for i in range(game.board_size):for j in range(game.board_size):if game.board[i][j] == Stone.EMPTY.value:empty_positions.append((i, j))if not empty_positions:return "PASS"# 计算每个空位的攻击性分数scored_positions = []opponent_value = 3 - game.current_player.valuefor pos in empty_positions:x, y = posscore = 0# 检查四个方向for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:nx, ny = x + dx, y + dyif 0 <= nx < game.board_size and 0 <= ny < game.board_size:if game.board[nx][ny] == opponent_value:score += 3 # 靠近对方棋子加分elif game.board[nx][ny] == game.current_player.value:score += 1 # 靠近己方棋子加分scored_positions.append((score, pos))# 根据攻击性系数调整选择策略if random.random() < self.aggressiveness:# 攻击性策略:选择得分最高的位置scored_positions.sort(key=lambda x: x[0], reverse=True)best_score = scored_positions[0][0]best_moves = [pos for score, pos in scored_positions if score == best_score]return random.choice(best_moves)else:# 保守策略:随机选择return random.choice(empty_positions)# 中等AI:考虑更多攻击性因素elif self.level == 2:# 获取所有空位empty_positions = []for i in range(game.board_size):for j in range(game.board_size):if game.board[i][j] == Stone.EMPTY.value:empty_positions.append((i, j))if not empty_positions:return "PASS"# 计算每个空位的攻击性分数player_value = game.current_player.valueopponent_value = 3 - player_valuescored_positions = []for pos in empty_positions:x, y = posscore = 0capture_potential = 0threat_potential = 0connection_potential = 0# 检查四个方向for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:nx, ny = x + dx, y + dyif 0 <= nx < game.board_size and 0 <= ny < game.board_size:if game.board[nx][ny] == opponent_value:# 检查是否可以吃掉对方棋子if self._can_capture(game, nx, ny, opponent_value):capture_potential += 5# 靠近对方棋子score += 3elif game.board[nx][ny] == player_value:# 连接己方棋子connection_potential += 2score += 1# 检查这个位置是否对对方棋子构成威胁threat_potential = self._evaluate_threat(game, x, y, player_value, opponent_value)# 综合评分total_score = score + capture_potential + threat_potential + connection_potentialscored_positions.append((total_score, pos))# 根据攻击性系数调整选择策略scored_positions.sort(key=lambda x: x[0], reverse=True)if random.random() < self.aggressiveness:# 攻击性策略:选择得分最高的位置best_score = scored_positions[0][0]best_moves = [pos for score, pos in scored_positions if score == best_score]return random.choice(best_moves)else:# 有一定随机性top_moves = scored_positions[:max(3, len(scored_positions) // 3)]return random.choice(top_moves)[1]# 困难AI:模拟落子并选择最佳结果,更加攻击性elif self.level == 3:# 获取所有空位empty_positions = []for i in range(game.board_size):for j in range(game.board_size):if game.board[i][j] == Stone.EMPTY.value:empty_positions.append((i, j))if not empty_positions:return "PASS"# 尝试每个空位,评估结果best_score = -float('inf')best_moves = []for pos in empty_positions:x, y = pos# 创建游戏副本进行模拟game_copy = GoGame(game.board_size)game_copy.board = game.board.copy()game_copy.current_player = game.current_playergame_copy.last_capture = game.last_capturegame_copy.captured_black = game.captured_blackgame_copy.captured_white = game.captured_white# 尝试落子result = game_copy._play_stone(x, y, game.current_player.value)# 评估结果if result >= 0: # 成功落子# 评估函数更加攻击性score = 0# 1. 提子数(高权重)score += result * 10# 2. 棋盘控制度(己方棋子数 - 对方棋子数)player_stones = np.sum(game_copy.board == game.current_player.value)opponent_stones = np.sum(game_copy.board == (3 - game.current_player.value))score += (player_stones - opponent_stones) * 2# 3. 威胁评估(检查是否可以威胁对方棋子)threat_score = self._evaluate_threat_potential(game_copy, game.current_player.value)score += threat_score * 3# 4. 连接性评估(棋子是否连接成势)connection_score = self._evaluate_connection(game_copy, game.current_player.value)score += connection_score# 5. 中心控制(靠近中心的位置更有价值)center_score = self._evaluate_center_control(x, y, game.board_size)score += center_scoreif score > best_score:best_score = scorebest_moves = [pos]elif score == best_score:best_moves.append(pos)if best_moves:# 根据攻击性系数决定是否选择最佳移动if random.random() < self.aggressiveness:return random.choice(best_moves)else:# 有一定随机性,但偏向好棋if len(best_moves) > 3:return random.choice(best_moves[:3])else:return random.choice(best_moves)else:# 如果没有合法落子,选择passreturn "PASS"def _can_capture(self, game, x, y, stone_value):"""检查是否可以吃掉指定位置的棋子"""# 创建游戏副本进行模拟game_copy = GoGame(game.board_size)game_copy.board = game.board.copy()game_copy.current_player = game.current_playergame_copy.last_capture = game.last_capture# 尝试在相邻位置落子for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:nx, ny = x + dx, y + dyif (0 <= nx < game.board_size and 0 <= ny < game.board_size andgame_copy.board[nx][ny] == Stone.EMPTY.value):result = game_copy._play_stone(nx, ny, game.current_player.value)if result > 0: # 成功吃掉棋子return Truereturn Falsedef _evaluate_threat(self, game, x, y, player_value, opponent_value):"""评估落子对对方的威胁程度"""threat_score = 0# 检查四个方向for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:nx, ny = x + dx, y + dyif 0 <= nx < game.board_size and 0 <= ny < game.board_size:if game.board[nx][ny] == opponent_value:# 检查这个对方棋子是否只有一口气if not self._has_multiple_liberties(game, nx, ny, opponent_value):threat_score += 3return threat_scoredef _evaluate_threat_potential(self, game, player_value):"""评估棋盘上的威胁潜力"""threat_score = 0opponent_value = 3 - player_value# 检查所有对方棋子for i in range(game.board_size):for j in range(game.board_size):if game.board[i][j] == opponent_value:# 检查这个棋子是否处于危险中if not self._has_multiple_liberties(game, i, j, opponent_value):threat_score += 2return threat_scoredef _evaluate_connection(self, game, player_value):"""评估棋子的连接性"""connection_score = 0visited = np.zeros((game.board_size, game.board_size), dtype=bool)# 查找所有相连的棋子组for i in range(game.board_size):for j in range(game.board_size):if game.board[i][j] == player_value and not visited[i][j]:group_size = self._get_group_size(game, i, j, player_value, visited)connection_score += group_size # 更大的组得分更高return connection_scoredef _evaluate_center_control(self, x, y, board_size):"""评估位置的中心控制价值"""center = board_size / 2distance = abs(x - center) + abs(y - center)# 距离中心越近,价值越高return int((board_size - distance) / 2)def _has_multiple_liberties(self, game, x, y, stone_value):"""检查棋子是否有多个气(不仅仅是存活)"""# 这里简化处理,只检查是否有至少两个气# 实际实现可能需要更复杂的逻辑liberty_count = 0visited = np.zeros((game.board_size, game.board_size), dtype=bool)# 使用DFS计算气的数量def count_liberties(x, y, stone_value, visited):nonlocal liberty_countif x < 0 or x >= game.board_size or y < 0 or y >= game.board_size:returnif visited[x][y]:returnvisited[x][y] = Trueif game.board[x][y] == Stone.EMPTY.value:liberty_count += 1returnelif game.board[x][y] != stone_value:return# 检查四个方向count_liberties(x - 1, y, stone_value, visited)count_liberties(x + 1, y, stone_value, visited)count_liberties(x, y - 1, stone_value, visited)count_liberties(x, y + 1, stone_value, visited)count_liberties(x, y, stone_value, visited)return liberty_count >= 2def _get_group_size(self, game, x, y, stone_value, visited):"""获取棋子组的大小"""if (x < 0 or x >= game.board_size or y < 0 or y >= game.board_size orvisited[x][y] or game.board[x][y] != stone_value):return 0visited[x][y] = Truesize = 1# 递归计算相邻同色棋子size += self._get_group_size(game, x - 1, y, stone_value, visited)size += self._get_group_size(game, x + 1, y, stone_value, visited)size += self._get_group_size(game, x, y - 1, stone_value, visited)size += self._get_group_size(game, x, y + 1, stone_value, visited)return sizeclass GoGUI:def __init__(self, board_size=19, ai_level_black=1, ai_level_white=1,aggressiveness_black=0.8, aggressiveness_white=0.8,window_width=1000, window_height=800):pygame.init()self.board_size = board_size# 设置窗口尺寸self.window_width = window_widthself.window_height = window_height# 创建可调整大小的窗口self.screen = pygame.display.set_mode((self.window_width, self.window_height), pygame.RESIZABLE)pygame.display.set_caption("围棋自动对弈 - 攻击性AI")self.clock = pygame.time.Clock()# 信息区域高度self.info_height = 100# 全屏模式self.fullscreen = False# 更新棋盘尺寸self.update_board_size()# 解决中文显示问题font_path = get_font_path()if font_path:try:self.font = pygame.font.Font(font_path, 24)self.large_font = pygame.font.Font(font_path, 36)self.small_font = pygame.font.Font(font_path, 20)except:# 如果加载字体失败,使用默认字体self.font = pygame.font.SysFont(None, 24)self.large_font = pygame.font.SysFont(None, 36)self.small_font = pygame.font.SysFont(None, 20)else:# 没有找到中文字体,使用默认字体self.font = pygame.font.SysFont(None, 24)self.large_font = pygame.font.SysFont(None, 36)self.small_font = pygame.font.SysFont(None, 20)self.game = GoGame(board_size)self.ai_black = GoAI(ai_level_black, aggressiveness_black)self.ai_white = GoAI(ai_level_white, aggressiveness_white)self.auto_play = Trueself.move_delay = 1.0 # 落子延迟(秒)self.last_move_time = 0self.show_controls = False # 默认不显示控制提示self.aggressiveness_black = aggressiveness_blackself.aggressiveness_white = aggressiveness_whiteself.last_game_saved = False # 标记上一局游戏是否已保存def toggle_fullscreen(self):"""切换全屏模式"""self.fullscreen = not self.fullscreenif self.fullscreen:self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)else:self.screen = pygame.display.set_mode((self.window_width, self.window_height), pygame.RESIZABLE)# 更新棋盘尺寸self.update_board_size()def update_board_size(self):"""根据窗口大小更新棋盘尺寸"""screen_width, screen_height = self.screen.get_size()# 计算格子大小,确保棋盘能完全显示在屏幕上max_cell_size = min(screen_width, screen_height - self.info_height) // (self.board_size + 1)self.cell_size = max(20, min(60, max_cell_size)) # 限制在20-60像素之间# 计算边距self.margin_x = (screen_width - (self.board_size - 1) * self.cell_size) // 2self.margin_y = (screen_height - self.info_height - (self.board_size - 1) * self.cell_size) // 2def draw_board(self):"""绘制棋盘"""# 使用更深的木质颜色self.screen.fill((200, 170, 80))# 绘制更粗的网格线for i in range(self.board_size):# 横线pygame.draw.line(self.screen, (0, 0, 0),(self.margin_x, self.margin_y + i * self.cell_size),(self.margin_x + (self.board_size - 1) * self.cell_size, self.margin_y + i * self.cell_size),2 # 加粗线条)# 竖线pygame.draw.line(self.screen, (0, 0, 0),(self.margin_x + i * self.cell_size, self.margin_y),(self.margin_x + i * self.cell_size, self.margin_y + (self.board_size - 1) * self.cell_size),2 # 加粗线条)# 绘制星位(19路棋盘的标准星位)star_points = [3, 9, 15]for i in star_points:for j in star_points:# 绘制更大的星位点pygame.draw.circle(self.screen, (0, 0, 0),(self.margin_x + i * self.cell_size, self.margin_y + j * self.cell_size),6 # 增大星位点)def draw_stones(self):"""绘制棋子"""for i in range(self.board_size):for j in range(self.board_size):if self.game.board[i][j] == Stone.BLACK.value:# 绘制黑色棋子,带一点光泽效果pygame.draw.circle(self.screen, (30, 30, 30),(self.margin_x + i * self.cell_size, self.margin_y + j * self.cell_size),self.cell_size // 2 - 2)# 光泽效果pygame.draw.circle(self.screen, (80, 80, 80),(self.margin_x + i * self.cell_size - self.cell_size // 6,self.margin_y + j * self.cell_size - self.cell_size // 6),self.cell_size // 6)elif self.game.board[i][j] == Stone.WHITE.value:# 绘制白色棋子,带一点光泽效果pygame.draw.circle(self.screen, (240, 240, 240),(self.margin_x + i * self.cell_size, self.margin_y + j * self.cell_size),self.cell_size // 2 - 2)pygame.draw.circle(self.screen, (0, 0, 0),(self.margin_x + i * self.cell_size, self.margin_y + j * self.cell_size),self.cell_size // 2 - 2,1)# 光泽效果pygame.draw.circle(self.screen, (255, 255, 255),(self.margin_x + i * self.cell_size - self.cell_size // 6,self.margin_y + j * self.cell_size - self.cell_size // 6),self.cell_size // 8)def draw_info(self):"""绘制游戏信息"""screen_width, screen_height = self.screen.get_size()info_y = screen_height - self.info_height# 绘制信息区域背景pygame.draw.rect(self.screen, (220, 200, 160), (0, info_y, screen_width, self.info_height))pygame.draw.line(self.screen, (150, 130, 90), (0, info_y), (screen_width, info_y), 3)# 当前玩家player_text = f"当前: {'黑棋' if self.game.current_player == Stone.BLACK else '白棋'}"player_surface = self.font.render(player_text, True, (0, 0, 0))self.screen.blit(player_surface, (20, info_y + 10))# 提子计数capture_text = f"提子: 黑:{self.game.captured_black} 白:{self.game.captured_white}"capture_surface = self.font.render(capture_text, True, (0, 0, 0))self.screen.blit(capture_surface, (20, info_y + 40))# 落子历史history_text = f"步数: {len(self.game.move_history)}"history_surface = self.font.render(history_text, True, (0, 0, 0))self.screen.blit(history_surface, (screen_width - 200, info_y + 10))# AI难度和攻击性ai_text = f"AI: 黑(L{self.ai_black.level}/A{self.aggressiveness_black:.1f}) 白(L{self.ai_white.level}/A{self.aggressiveness_white:.1f})"ai_surface = self.font.render(ai_text, True, (0, 0, 0))self.screen.blit(ai_surface, (screen_width - 300, info_y + 40))# 游戏结束信息if self.game.game_over:winner_text = "游戏结束! "if self.game.winner == Stone.BLACK:winner_text += "黑棋胜利!"elif self.game.winner == Stone.WHITE:winner_text += "白棋胜利!"else:winner_text += "平局!"winner_surface = self.large_font.render(winner_text, True, (200, 0, 0))text_rect = winner_surface.get_rect(center=(screen_width // 2, info_y + self.info_height // 2))self.screen.blit(winner_surface, text_rect)# 显示保存信息if not self.last_game_saved:save_text = "棋局已自动保存到 game_records 文件夹"save_surface = self.small_font.render(save_text, True, (0, 100, 0))save_rect = save_surface.get_rect(center=(screen_width // 2, info_y + self.info_height // 2 + 40))self.screen.blit(save_surface, save_rect)def draw_last_move(self):"""标记最后一步落子位置"""if self.game.move_history and self.game.move_history[-1] != "PASS":x, y, player, _ = self.game.move_history[-1]color = (255, 0, 0) if player == Stone.BLACK else (0, 0, 255)# 绘制更大的标记pygame.draw.circle(self.screen, color,(self.margin_x + x * self.cell_size, self.margin_y + y * self.cell_size),8, 2 # 增大标记)def save_current_game(self):"""保存当前棋局"""if self.game.game_over and not self.last_game_saved:filename = self.game.save_game_record()self.last_game_saved = Truereturn filenamereturn Nonedef run(self):"""运行游戏主循环"""while True:current_time = time.time()for event in pygame.event.get():if event.type == pygame.QUIT:pygame.quit()sys.exit()if event.type == pygame.VIDEORESIZE:# 窗口大小改变时更新棋盘尺寸if not self.fullscreen:self.window_width, self.window_height = event.sizeself.screen = pygame.display.set_mode((self.window_width, self.window_height), pygame.RESIZABLE)self.update_board_size()if event.type == pygame.KEYDOWN:if event.key == pygame.K_SPACE:self.auto_play = not self.auto_playelif event.key == pygame.K_r:# 重置游戏self.game = GoGame(self.board_size)self.last_move_time = current_timeself.last_game_saved = Falseelif event.key == pygame.K_1:self.ai_black.level = max(1, self.ai_black.level - 1)elif event.key == pygame.K_2:self.ai_black.level = min(3, self.ai_black.level + 1)elif event.key == pygame.K_3:self.ai_white.level = max(1, self.ai_white.level - 1)elif event.key == pygame.K_4:self.ai_white.level = min(3, self.ai_white.level + 1)elif event.key == pygame.K_UP:self.move_delay = min(2.0, self.move_delay + 0.1)elif event.key == pygame.K_DOWN:self.move_delay = max(0.1, self.move_delay - 0.1)elif event.key == pygame.K_h:self.show_controls = not self.show_controlselif event.key == pygame.K_f:self.toggle_fullscreen()elif event.key == pygame.K_m:# 手动落子模式if self.auto_play:self.auto_play = Falseelse:# 手动落子mouse_pos = pygame.mouse.get_pos()board_x = round((mouse_pos[0] - self.margin_x) / self.cell_size)board_y = round((mouse_pos[1] - self.margin_y) / self.cell_size)if 0 <= board_x < self.board_size and 0 <= board_y < self.board_size:self.game.place_stone(board_x, board_y)self.last_move_time = current_timeelif event.key == pygame.K_a:# 增加黑棋攻击性self.aggressiveness_black = min(1.0, self.aggressiveness_black + 0.1)self.ai_black.aggressiveness = self.aggressiveness_blackelif event.key == pygame.K_z:# 减少黑棋攻击性self.aggressiveness_black = max(0.0, self.aggressiveness_black - 0.1)self.ai_black.aggressiveness = self.aggressiveness_blackelif event.key == pygame.K_s:# 增加白棋攻击性self.aggressiveness_white = min(1.0, self.aggressiveness_white + 0.1)self.ai_white.aggressiveness = self.aggressiveness_whiteelif event.key == pygame.K_x:# 减少白棋攻击性self.aggressiveness_white = max(0.0, self.aggressiveness_white - 0.1)self.ai_white.aggressiveness = self.aggressiveness_whiteelif event.key == pygame.K_g:# 手动保存当前棋局if self.game.move_history: # 确保有棋局可保存filename = self.game.save_game_record()print(f"棋局已保存到: {filename}")# 自动对弈逻辑if self.auto_play and not self.game.game_over and current_time - self.last_move_time > self.move_delay:if self.game.current_player == Stone.BLACK:move = self.ai_black.make_move(self.game)else:move = self.ai_white.make_move(self.game)if move == "PASS":self.game.pass_turn()elif move is not None:x, y = moveself.game.place_stone(x, y)self.last_move_time = current_time# 检查游戏是否结束,如果结束则自动保存if self.game.game_over and not self.last_game_saved:self.save_current_game()# 绘制游戏self.draw_board()self.draw_stones()self.draw_last_move()self.draw_info()# 显示控制提示if self.show_controls:screen_width, screen_height = self.screen.get_size()info_y = screen_height - self.info_heightcontrols = ["空格键: 暂停/继续自动对弈","R键: 重置游戏","1/2键: 调整黑棋AI难度","3/4键: 调整白棋AI难度","A/Z键: 增加/减少黑棋攻击性","S/X键: 增加/减少白棋攻击性","上/下箭头: 调整落子速度","H键: 显示/隐藏控制提示","F键: 全屏切换","M键: 手动落子","G键: 手动保存棋局","窗口: 可拖动调整大小"]# 绘制半透明背景s = pygame.Surface((screen_width - 20, 220), pygame.SRCALPHA)s.fill((255, 255, 255, 200)) # 半透明白色self.screen.blit(s, (10, info_y - 230))for i, text in enumerate(controls):control_surface = self.small_font.render(text, True, (50, 50, 50))self.screen.blit(control_surface, (20, info_y - 225 + i * 20))pygame.display.flip()self.clock.tick(30)if __name__ == "__main__":# 创建并运行游戏# 参数:棋盘大小,黑棋AI难度(1-3),白棋AI难度(1-3),黑棋攻击性(0.0-1.0),白棋攻击性(0.0-1.0),窗口宽度,窗口高度game_gui = GoGUI(board_size=19,ai_level_black=2,ai_level_white=3,aggressiveness_black=0.8, # 黑棋攻击性aggressiveness_white=0.8, # 白棋攻击性window_width=1200, # 初始窗口宽度window_height=900 # 初始窗口高度)game_gui.run()