288 lines
7.2 KiB
Python
288 lines
7.2 KiB
Python
"""五子棋游戏逻辑模块"""
|
||
from typing import Optional, Tuple, List, Dict, Any
|
||
|
||
|
||
def create_empty_board() -> List[List[int]]:
|
||
"""创建空棋盘
|
||
|
||
Returns:
|
||
15x15的二维列表,0表示空位
|
||
"""
|
||
return [[0] * 15 for _ in range(15)]
|
||
|
||
|
||
def parse_coord(coord_str: str) -> Optional[Tuple[int, int]]:
|
||
"""解析坐标字符串
|
||
|
||
Args:
|
||
coord_str: 如 "A1", "O15", "h8"
|
||
|
||
Returns:
|
||
(row, col) 或 None
|
||
"""
|
||
coord_str = coord_str.strip().upper()
|
||
|
||
if len(coord_str) < 2:
|
||
return None
|
||
|
||
# 解析列(A-O)
|
||
col_char = coord_str[0]
|
||
if not ('A' <= col_char <= 'O'):
|
||
return None
|
||
col = ord(col_char) - ord('A')
|
||
|
||
# 解析行(1-15)
|
||
try:
|
||
row = int(coord_str[1:]) - 1
|
||
if not (0 <= row <= 14):
|
||
return None
|
||
except ValueError:
|
||
return None
|
||
|
||
return (row, col)
|
||
|
||
|
||
def format_coord(row: int, col: int) -> str:
|
||
"""格式化坐标
|
||
|
||
Args:
|
||
row: 0-14
|
||
col: 0-14
|
||
|
||
Returns:
|
||
如 "A1", "O15"
|
||
"""
|
||
col_char = chr(ord('A') + col)
|
||
row_num = row + 1
|
||
return f"{col_char}{row_num}"
|
||
|
||
|
||
def is_valid_position(row: int, col: int) -> bool:
|
||
"""检查坐标是否在棋盘范围内
|
||
|
||
Args:
|
||
row: 行号
|
||
col: 列号
|
||
|
||
Returns:
|
||
是否有效
|
||
"""
|
||
return 0 <= row <= 14 and 0 <= col <= 14
|
||
|
||
|
||
def count_consecutive(board: List[List[int]], row: int, col: int,
|
||
direction: Tuple[int, int], player: int) -> int:
|
||
"""统计某方向连续同色棋子数(包括当前位置)
|
||
|
||
Args:
|
||
board: 棋盘状态
|
||
row, col: 起始位置
|
||
direction: 方向向量 (dr, dc)
|
||
player: 玩家 (1:黑, 2:白)
|
||
|
||
Returns:
|
||
连续棋子数
|
||
"""
|
||
dr, dc = direction
|
||
count = 1 # 包括当前位置
|
||
|
||
# 正方向
|
||
r, c = row + dr, col + dc
|
||
while is_valid_position(r, c) and board[r][c] == player:
|
||
count += 1
|
||
r += dr
|
||
c += dc
|
||
|
||
# 反方向
|
||
r, c = row - dr, col - dc
|
||
while is_valid_position(r, c) and board[r][c] == player:
|
||
count += 1
|
||
r -= dr
|
||
c -= dc
|
||
|
||
return count
|
||
|
||
|
||
def check_win(board: List[List[int]], row: int, col: int, player: int) -> bool:
|
||
"""检查是否获胜(恰好五连珠)
|
||
|
||
Args:
|
||
board: 棋盘状态
|
||
row, col: 最后落子位置
|
||
player: 玩家 (1:黑, 2:白)
|
||
|
||
Returns:
|
||
是否五连珠获胜
|
||
"""
|
||
# 四个方向:横、竖、左斜、右斜
|
||
directions = [(0, 1), (1, 0), (1, 1), (1, -1)]
|
||
|
||
for direction in directions:
|
||
count = count_consecutive(board, row, col, direction, player)
|
||
if count == 5:
|
||
return True
|
||
|
||
return False
|
||
|
||
|
||
def analyze_line(board: List[List[int]], row: int, col: int,
|
||
direction: Tuple[int, int], player: int) -> Dict[str, Any]:
|
||
"""分析某方向的棋型
|
||
|
||
Args:
|
||
board: 棋盘状态
|
||
row, col: 待分析位置(假设已落子)
|
||
direction: 方向向量
|
||
player: 玩家
|
||
|
||
Returns:
|
||
{
|
||
"consecutive": int, # 连续数
|
||
"left_open": bool, # 左侧是否开放
|
||
"right_open": bool, # 右侧是否开放
|
||
"pattern": str # 棋型类型
|
||
}
|
||
"""
|
||
dr, dc = direction
|
||
|
||
# 统计正方向连续数
|
||
right_count = 0
|
||
r, c = row + dr, col + dc
|
||
while is_valid_position(r, c) and board[r][c] == player:
|
||
right_count += 1
|
||
r += dr
|
||
c += dc
|
||
right_open = is_valid_position(r, c) and board[r][c] == 0
|
||
|
||
# 统计反方向连续数
|
||
left_count = 0
|
||
r, c = row - dr, col - dc
|
||
while is_valid_position(r, c) and board[r][c] == player:
|
||
left_count += 1
|
||
r -= dr
|
||
c -= dc
|
||
left_open = is_valid_position(r, c) and board[r][c] == 0
|
||
|
||
# 总连续数(包括当前位置)
|
||
consecutive = left_count + 1 + right_count
|
||
|
||
# 判定棋型
|
||
pattern = "none"
|
||
|
||
if consecutive >= 6:
|
||
pattern = "overline"
|
||
elif consecutive == 5:
|
||
pattern = "five"
|
||
elif consecutive == 4:
|
||
if left_open and right_open:
|
||
pattern = "live_four"
|
||
elif left_open or right_open:
|
||
pattern = "rush_four"
|
||
elif consecutive == 3:
|
||
if left_open and right_open:
|
||
pattern = "live_three"
|
||
elif left_open or right_open:
|
||
pattern = "sleep_three"
|
||
|
||
return {
|
||
"consecutive": consecutive,
|
||
"left_open": left_open,
|
||
"right_open": right_open,
|
||
"pattern": pattern
|
||
}
|
||
|
||
|
||
def check_forbidden(board: List[List[int]], row: int, col: int) -> Tuple[bool, str]:
|
||
"""检查黑方禁手
|
||
|
||
Args:
|
||
board: 棋盘状态(不包含待落子)
|
||
row, col: 待落子位置
|
||
|
||
Returns:
|
||
(是否禁手, 禁手类型)
|
||
"""
|
||
# 只有黑方(玩家1)有禁手
|
||
player = 1
|
||
|
||
# 临时落子
|
||
original_value = board[row][col]
|
||
board[row][col] = player
|
||
|
||
# 四个方向
|
||
directions = [(0, 1), (1, 0), (1, 1), (1, -1)]
|
||
|
||
live_threes = 0
|
||
fours = 0
|
||
has_overline = False
|
||
|
||
for direction in directions:
|
||
analysis = analyze_line(board, row, col, direction, player)
|
||
|
||
if analysis["pattern"] == "overline":
|
||
has_overline = True
|
||
elif analysis["pattern"] == "live_three":
|
||
live_threes += 1
|
||
elif analysis["pattern"] in ["live_four", "rush_four"]:
|
||
fours += 1
|
||
|
||
# 恢复棋盘
|
||
board[row][col] = original_value
|
||
|
||
# 判定禁手
|
||
if has_overline:
|
||
return True, "长连禁手"
|
||
if live_threes >= 2:
|
||
return True, "三三禁手"
|
||
if fours >= 2:
|
||
return True, "四四禁手"
|
||
|
||
return False, ""
|
||
|
||
|
||
def render_board(board: List[List[int]], last_move: Optional[Tuple[int, int]] = None) -> str:
|
||
"""渲染棋盘为字符串
|
||
|
||
Args:
|
||
board: 棋盘状态
|
||
last_move: 最后落子位置(可选,用于标记)
|
||
|
||
Returns:
|
||
棋盘的字符串表示
|
||
"""
|
||
lines = []
|
||
|
||
# 列标题 - 使用全角空格确保对齐
|
||
col_labels = " " + "".join([chr(ord('A') + i) + " " for i in range(15)])
|
||
lines.append(col_labels.rstrip())
|
||
|
||
# 绘制棋盘
|
||
for row in range(15):
|
||
row_num = f"{row + 1:2d}" # 右对齐行号
|
||
row_cells = []
|
||
|
||
for col in range(15):
|
||
cell = board[row][col]
|
||
|
||
# 标记最后落子
|
||
if last_move and last_move == (row, col):
|
||
if cell == 1:
|
||
row_cells.append("⚫")
|
||
elif cell == 2:
|
||
row_cells.append("⚪")
|
||
else:
|
||
row_cells.append("➕")
|
||
else:
|
||
if cell == 0:
|
||
row_cells.append("➕")
|
||
elif cell == 1:
|
||
row_cells.append("⚫")
|
||
elif cell == 2:
|
||
row_cells.append("⚪")
|
||
|
||
# 每个emoji后面加一个空格
|
||
lines.append(f"{row_num} " + "".join([cell + " " for cell in row_cells]).rstrip())
|
||
|
||
return "\n".join(lines)
|
||
|