392 lines
13 KiB
Python
392 lines
13 KiB
Python
|
|
import pygame
|
|||
|
|
import sys
|
|||
|
|
|
|||
|
|
# 初始化pygame
|
|||
|
|
pygame.init()
|
|||
|
|
|
|||
|
|
# 游戏常量设置
|
|||
|
|
SCREEN_SIZE = 600 # 窗口大小
|
|||
|
|
GRID_SIZE = 30 # 每个格子的大小
|
|||
|
|
LINE_WIDTH = 2 # 棋盘线宽度
|
|||
|
|
BOARD_SIZE = 19 # 棋盘大小(19x19)
|
|||
|
|
PIECE_RADIUS = 14 # 棋子半径
|
|||
|
|
PIECE_WIDTH = 2 # 棋子边框宽度
|
|||
|
|
|
|||
|
|
# 颜色定义
|
|||
|
|
BLACK = (0, 0, 0)
|
|||
|
|
WHITE = (255, 255, 255)
|
|||
|
|
BG_COLOR = (240, 220, 180) # 棋盘背景色(木纹色)
|
|||
|
|
LINE_COLOR = (100, 100, 100)# 棋盘线颜色
|
|||
|
|
|
|||
|
|
# 计算棋盘起始位置(居中显示)
|
|||
|
|
START_X = (SCREEN_SIZE - (BOARD_SIZE - 1) * GRID_SIZE) // 2
|
|||
|
|
START_Y = (SCREEN_SIZE - (BOARD_SIZE - 1) * GRID_SIZE) // 2
|
|||
|
|
|
|||
|
|
# 创建游戏窗口
|
|||
|
|
screen = pygame.display.set_mode((SCREEN_SIZE, SCREEN_SIZE))
|
|||
|
|
pygame.display.set_caption("五子棋")
|
|||
|
|
|
|||
|
|
# 游戏模式:0表示双人对战,1表示AI对战
|
|||
|
|
game_mode = 1 # 默认AI对战
|
|||
|
|
mode_selected = False # 是否已选择游戏模式
|
|||
|
|
|
|||
|
|
# 初始化棋盘数据,0表示空,1表示黑子,2表示白子
|
|||
|
|
board = [[0 for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)]
|
|||
|
|
current_player = 1 # 1表示黑方,2表示白方
|
|||
|
|
game_over = False
|
|||
|
|
winner = None
|
|||
|
|
|
|||
|
|
# AI评估权重
|
|||
|
|
WEIGHTS = {
|
|||
|
|
'win': 100000, # 获胜
|
|||
|
|
'block_win': 50000, # 阻挡对方获胜
|
|||
|
|
'four': 10000, # 四子连珠
|
|||
|
|
'block_four': 5000, # 阻挡对方四子
|
|||
|
|
'three': 1000, # 三子连珠
|
|||
|
|
'block_three': 500, # 阻挡对方三子
|
|||
|
|
'two': 100, # 二子连珠
|
|||
|
|
'block_two': 50, # 阻挡对方二子
|
|||
|
|
'one': 10, # 一子
|
|||
|
|
'block_one': 5 # 阻挡对方一子
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
def draw_board():
|
|||
|
|
"""绘制棋盘"""
|
|||
|
|
# 填充背景色
|
|||
|
|
screen.fill(BG_COLOR)
|
|||
|
|
|
|||
|
|
# 绘制棋盘网格线
|
|||
|
|
for i in range(BOARD_SIZE):
|
|||
|
|
# 竖线
|
|||
|
|
pygame.draw.line(
|
|||
|
|
screen, LINE_COLOR,
|
|||
|
|
(START_X + i * GRID_SIZE, START_Y),
|
|||
|
|
(START_X + i * GRID_SIZE, START_Y + (BOARD_SIZE - 1) * GRID_SIZE),
|
|||
|
|
LINE_WIDTH
|
|||
|
|
)
|
|||
|
|
# 横线
|
|||
|
|
pygame.draw.line(
|
|||
|
|
screen, LINE_COLOR,
|
|||
|
|
(START_X, START_Y + i * GRID_SIZE),
|
|||
|
|
(START_X + (BOARD_SIZE - 1) * GRID_SIZE, START_Y + i * GRID_SIZE),
|
|||
|
|
LINE_WIDTH
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 绘制棋子
|
|||
|
|
for row in range(BOARD_SIZE):
|
|||
|
|
for col in range(BOARD_SIZE):
|
|||
|
|
if board[row][col] != 0:
|
|||
|
|
center_x = START_X + col * GRID_SIZE
|
|||
|
|
center_y = START_Y + row * GRID_SIZE
|
|||
|
|
color = BLACK if board[row][col] == 1 else WHITE
|
|||
|
|
# 绘制棋子
|
|||
|
|
pygame.draw.circle(screen, color, (center_x, center_y), PIECE_RADIUS)
|
|||
|
|
# 绘制棋子边框(增加视觉效果)
|
|||
|
|
pygame.draw.circle(screen, BLACK, (center_x, center_y), PIECE_RADIUS, PIECE_WIDTH)
|
|||
|
|
|
|||
|
|
def get_click_pos(pos):
|
|||
|
|
"""将鼠标点击位置转换为棋盘坐标"""
|
|||
|
|
x, y = pos
|
|||
|
|
# 计算所在格子
|
|||
|
|
col = (x - START_X + GRID_SIZE // 2) // GRID_SIZE
|
|||
|
|
row = (y - START_Y + GRID_SIZE // 2) // GRID_SIZE
|
|||
|
|
|
|||
|
|
# 检查是否在棋盘范围内
|
|||
|
|
if 0 <= row < BOARD_SIZE and 0 <= col < BOARD_SIZE:
|
|||
|
|
return (row, col)
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def check_win(row, col):
|
|||
|
|
"""检查落子后是否获胜"""
|
|||
|
|
player = board[row][col]
|
|||
|
|
directions = [
|
|||
|
|
(0, 1), # 横向
|
|||
|
|
(1, 0), # 纵向
|
|||
|
|
(1, 1), # 正对角线
|
|||
|
|
(1, -1) # 反对角线
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
for dr, dc in directions:
|
|||
|
|
count = 1
|
|||
|
|
# 正方向检查
|
|||
|
|
r, c = row + dr, col + dc
|
|||
|
|
while 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE and board[r][c] == player:
|
|||
|
|
count += 1
|
|||
|
|
r += dr
|
|||
|
|
c += dc
|
|||
|
|
|
|||
|
|
# 反方向检查
|
|||
|
|
r, c = row - dr, col - dc
|
|||
|
|
while 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE and board[r][c] == player:
|
|||
|
|
count += 1
|
|||
|
|
r -= dr
|
|||
|
|
c -= dc
|
|||
|
|
|
|||
|
|
# 五子连珠则获胜
|
|||
|
|
if count >= 5:
|
|||
|
|
return True
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def evaluate_position(row, col, player):
|
|||
|
|
"""评估指定位置对玩家的价值"""
|
|||
|
|
if board[row][col] != 0:
|
|||
|
|
return -1000000 # 已经有棋子的位置
|
|||
|
|
|
|||
|
|
score = 0
|
|||
|
|
opponent = 1 if player == 2 else 2
|
|||
|
|
|
|||
|
|
# 模拟落子
|
|||
|
|
board[row][col] = player
|
|||
|
|
|
|||
|
|
# 检查是否获胜
|
|||
|
|
if check_win(row, col):
|
|||
|
|
score += WEIGHTS['win']
|
|||
|
|
|
|||
|
|
# 评估各个方向的连珠情况
|
|||
|
|
directions = [(0, 1), (1, 0), (1, 1), (1, -1)]
|
|||
|
|
for dr, dc in directions:
|
|||
|
|
# 检查当前玩家的连珠
|
|||
|
|
count = 1
|
|||
|
|
# 正方向
|
|||
|
|
r, c = row + dr, col + dc
|
|||
|
|
while 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE and board[r][c] == player:
|
|||
|
|
count += 1
|
|||
|
|
r += dr
|
|||
|
|
c += dc
|
|||
|
|
# 反方向
|
|||
|
|
r, c = row - dr, col - dc
|
|||
|
|
while 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE and board[r][c] == player:
|
|||
|
|
count += 1
|
|||
|
|
r -= dr
|
|||
|
|
c -= dc
|
|||
|
|
|
|||
|
|
# 根据连珠数量加分
|
|||
|
|
if count == 4:
|
|||
|
|
score += WEIGHTS['four']
|
|||
|
|
elif count == 3:
|
|||
|
|
score += WEIGHTS['three']
|
|||
|
|
elif count == 2:
|
|||
|
|
score += WEIGHTS['two']
|
|||
|
|
elif count == 1:
|
|||
|
|
score += WEIGHTS['one']
|
|||
|
|
|
|||
|
|
# 撤销模拟落子
|
|||
|
|
board[row][col] = 0
|
|||
|
|
|
|||
|
|
# 模拟对方落子,检查需要阻挡的情况
|
|||
|
|
board[row][col] = opponent
|
|||
|
|
if check_win(row, col):
|
|||
|
|
score += WEIGHTS['block_win']
|
|||
|
|
|
|||
|
|
for dr, dc in directions:
|
|||
|
|
# 检查对方的连珠
|
|||
|
|
count = 1
|
|||
|
|
# 正方向
|
|||
|
|
r, c = row + dr, col + dc
|
|||
|
|
while 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE and board[r][c] == opponent:
|
|||
|
|
count += 1
|
|||
|
|
r += dr
|
|||
|
|
c += dc
|
|||
|
|
# 反方向
|
|||
|
|
r, c = row - dr, col - dc
|
|||
|
|
while 0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE and board[r][c] == opponent:
|
|||
|
|
count += 1
|
|||
|
|
r -= dr
|
|||
|
|
c -= dc
|
|||
|
|
|
|||
|
|
# 根据连珠数量加分(阻挡)
|
|||
|
|
if count == 4:
|
|||
|
|
score += WEIGHTS['block_four']
|
|||
|
|
elif count == 3:
|
|||
|
|
score += WEIGHTS['block_three']
|
|||
|
|
elif count == 2:
|
|||
|
|
score += WEIGHTS['block_two']
|
|||
|
|
elif count == 1:
|
|||
|
|
score += WEIGHTS['block_one']
|
|||
|
|
|
|||
|
|
# 撤销模拟落子
|
|||
|
|
board[row][col] = 0
|
|||
|
|
|
|||
|
|
# 中心位置加成
|
|||
|
|
center_row, center_col = BOARD_SIZE // 2, BOARD_SIZE // 2
|
|||
|
|
distance = abs(row - center_row) + abs(col - center_col)
|
|||
|
|
score += (10 - distance) * 2
|
|||
|
|
|
|||
|
|
return score
|
|||
|
|
|
|||
|
|
def ai_move():
|
|||
|
|
"""AI落子"""
|
|||
|
|
best_score = -float('inf')
|
|||
|
|
best_move = None
|
|||
|
|
|
|||
|
|
# 遍历棋盘上的所有空位
|
|||
|
|
for row in range(BOARD_SIZE):
|
|||
|
|
for col in range(BOARD_SIZE):
|
|||
|
|
if board[row][col] == 0:
|
|||
|
|
# 评估该位置的价值
|
|||
|
|
score = evaluate_position(row, col, current_player)
|
|||
|
|
if score > best_score:
|
|||
|
|
best_score = score
|
|||
|
|
best_move = (row, col)
|
|||
|
|
|
|||
|
|
# 执行最佳落子
|
|||
|
|
if best_move:
|
|||
|
|
row, col = best_move
|
|||
|
|
board[row][col] = current_player
|
|||
|
|
return (row, col)
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def show_winner():
|
|||
|
|
"""显示获胜信息"""
|
|||
|
|
# 尝试使用系统中文字体
|
|||
|
|
try:
|
|||
|
|
font = pygame.font.SysFont(["SimHei", "Microsoft YaHei", "Arial"], 60)
|
|||
|
|
except:
|
|||
|
|
font = pygame.font.Font(None, 60)
|
|||
|
|
text = f"{'黑方' if winner == 1 else '白方'}获胜!"
|
|||
|
|
text_surface = font.render(text, True, (255, 0, 0))
|
|||
|
|
text_rect = text_surface.get_rect(center=(SCREEN_SIZE//2, SCREEN_SIZE//2))
|
|||
|
|
screen.blit(text_surface, text_rect)
|
|||
|
|
|
|||
|
|
# 显示重新开始提示
|
|||
|
|
try:
|
|||
|
|
font_small = pygame.font.SysFont(["SimHei", "Microsoft YaHei", "Arial"], 30)
|
|||
|
|
except:
|
|||
|
|
font_small = pygame.font.Font(None, 30)
|
|||
|
|
restart_text = "按R键重新开始 | 按Q键退出"
|
|||
|
|
restart_surface = font_small.render(restart_text, True, BLACK)
|
|||
|
|
restart_rect = restart_surface.get_rect(center=(SCREEN_SIZE//2, SCREEN_SIZE//2 + 50))
|
|||
|
|
screen.blit(restart_surface, restart_rect)
|
|||
|
|
|
|||
|
|
def show_mode_selection():
|
|||
|
|
"""显示游戏模式选择界面"""
|
|||
|
|
# 填充背景色
|
|||
|
|
screen.fill(BG_COLOR)
|
|||
|
|
|
|||
|
|
# 显示标题
|
|||
|
|
try:
|
|||
|
|
title_font = pygame.font.SysFont(["SimHei", "Microsoft YaHei", "Arial"], 48)
|
|||
|
|
except:
|
|||
|
|
title_font = pygame.font.Font(None, 48)
|
|||
|
|
title_text = "五子棋"
|
|||
|
|
title_surface = title_font.render(title_text, True, BLACK)
|
|||
|
|
title_rect = title_surface.get_rect(center=(SCREEN_SIZE//2, SCREEN_SIZE//2 - 100))
|
|||
|
|
screen.blit(title_surface, title_rect)
|
|||
|
|
|
|||
|
|
# 显示模式选择
|
|||
|
|
try:
|
|||
|
|
font = pygame.font.SysFont(["SimHei", "Microsoft YaHei", "Arial"], 36)
|
|||
|
|
except:
|
|||
|
|
font = pygame.font.Font(None, 36)
|
|||
|
|
|
|||
|
|
# 双人对战选项
|
|||
|
|
pvp_text = "1. 双人对战"
|
|||
|
|
pvp_surface = font.render(pvp_text, True, BLACK)
|
|||
|
|
pvp_rect = pvp_surface.get_rect(center=(SCREEN_SIZE//2, SCREEN_SIZE//2))
|
|||
|
|
screen.blit(pvp_surface, pvp_rect)
|
|||
|
|
|
|||
|
|
# AI对战选项
|
|||
|
|
ai_text = "2. AI对战"
|
|||
|
|
ai_surface = font.render(ai_text, True, BLACK)
|
|||
|
|
ai_rect = ai_surface.get_rect(center=(SCREEN_SIZE//2, SCREEN_SIZE//2 + 60))
|
|||
|
|
screen.blit(ai_surface, ai_rect)
|
|||
|
|
|
|||
|
|
# 显示操作提示
|
|||
|
|
try:
|
|||
|
|
hint_font = pygame.font.SysFont(["SimHei", "Microsoft YaHei", "Arial"], 24)
|
|||
|
|
except:
|
|||
|
|
hint_font = pygame.font.Font(None, 24)
|
|||
|
|
hint_text = "请按数字键选择游戏模式"
|
|||
|
|
hint_surface = hint_font.render(hint_text, True, BLACK)
|
|||
|
|
hint_rect = hint_surface.get_rect(center=(SCREEN_SIZE//2, SCREEN_SIZE//2 + 120))
|
|||
|
|
screen.blit(hint_surface, hint_rect)
|
|||
|
|
|
|||
|
|
pygame.display.update()
|
|||
|
|
|
|||
|
|
def restart_game():
|
|||
|
|
"""重新开始游戏"""
|
|||
|
|
global board, current_player, game_over, winner, mode_selected
|
|||
|
|
board = [[0 for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)]
|
|||
|
|
current_player = 1
|
|||
|
|
game_over = False
|
|||
|
|
winner = None
|
|||
|
|
mode_selected = False # 重新开始时需要重新选择模式
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
"""游戏主循环"""
|
|||
|
|
global current_player, game_over, winner, game_mode, mode_selected
|
|||
|
|
|
|||
|
|
clock = pygame.time.Clock()
|
|||
|
|
|
|||
|
|
while True:
|
|||
|
|
for event in pygame.event.get():
|
|||
|
|
if event.type == pygame.QUIT:
|
|||
|
|
pygame.quit()
|
|||
|
|
sys.exit()
|
|||
|
|
|
|||
|
|
# 键盘事件处理
|
|||
|
|
if event.type == pygame.KEYDOWN:
|
|||
|
|
if event.key == pygame.K_r: # 按R重新开始
|
|||
|
|
restart_game()
|
|||
|
|
if event.key == pygame.K_q: # 按Q退出
|
|||
|
|
pygame.quit()
|
|||
|
|
sys.exit()
|
|||
|
|
|
|||
|
|
# 游戏模式选择
|
|||
|
|
if not mode_selected:
|
|||
|
|
if event.key == pygame.K_1:
|
|||
|
|
game_mode = 0 # 双人对战
|
|||
|
|
mode_selected = True
|
|||
|
|
elif event.key == pygame.K_2:
|
|||
|
|
game_mode = 1 # AI对战
|
|||
|
|
mode_selected = True
|
|||
|
|
|
|||
|
|
# 鼠标点击落子(游戏未结束且已选择模式时)
|
|||
|
|
if event.type == pygame.MOUSEBUTTONDOWN and not game_over and mode_selected:
|
|||
|
|
pos = pygame.mouse.get_pos()
|
|||
|
|
board_pos = get_click_pos(pos)
|
|||
|
|
|
|||
|
|
if board_pos:
|
|||
|
|
row, col = board_pos
|
|||
|
|
# 检查位置是否为空
|
|||
|
|
if board[row][col] == 0:
|
|||
|
|
board[row][col] = current_player
|
|||
|
|
|
|||
|
|
# 检查是否获胜
|
|||
|
|
if check_win(row, col):
|
|||
|
|
game_over = True
|
|||
|
|
winner = current_player
|
|||
|
|
else:
|
|||
|
|
# 切换玩家
|
|||
|
|
current_player = 2 if current_player == 1 else 1
|
|||
|
|
|
|||
|
|
# 如果是AI对战模式且当前玩家是AI(白方),则AI落子
|
|||
|
|
if game_mode == 1 and current_player == 2:
|
|||
|
|
ai_pos = ai_move()
|
|||
|
|
if ai_pos:
|
|||
|
|
ai_row, ai_col = ai_pos
|
|||
|
|
# 检查AI落子后是否获胜
|
|||
|
|
if check_win(ai_row, ai_col):
|
|||
|
|
game_over = True
|
|||
|
|
winner = current_player
|
|||
|
|
else:
|
|||
|
|
# 切换回玩家
|
|||
|
|
current_player = 1
|
|||
|
|
|
|||
|
|
# 如果未选择模式,显示模式选择界面
|
|||
|
|
if not mode_selected:
|
|||
|
|
show_mode_selection()
|
|||
|
|
else:
|
|||
|
|
# 绘制游戏界面
|
|||
|
|
draw_board()
|
|||
|
|
|
|||
|
|
# 如果游戏结束,显示获胜信息
|
|||
|
|
if game_over:
|
|||
|
|
show_winner()
|
|||
|
|
|
|||
|
|
# 更新屏幕显示
|
|||
|
|
pygame.display.update()
|
|||
|
|
clock.tick(60)
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|