五子棋
import pygameimport random
import sys
import os
# ===================== 基础配置 =====================
pygame.init()
pygame.mixer.init()
GRID_SIZE = 40
BOARD_LINE = 15
BOARD_WIDTH = GRID_SIZE * (BOARD_LINE - 1)
PADDING = 30
WINDOW_W = BOARD_WIDTH + PADDING * 2
WINDOW_H = BOARD_WIDTH + PADDING * 2 + 60
# 颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
BOARD_COLOR = (220, 179, 92)
RED = (200, 0, 0)
BLUE = (0, 0, 200)
GRAY = (180, 180, 180)
HOVER_COLOR = (150, 150, 150)
# 窗口 & 字体
screen = pygame.display.set_mode((WINDOW_W, WINDOW_H))
pygame.display.set_caption("五子棋 | 双人/人机对战")
def get_chinese_font(size=24):
font_path = "C:/Windows/Fonts/simhei.ttf"
if os.path.exists(font_path):
return pygame.font.Font(font_path, size)
return pygame.font.Font(None, size)
font = get_chinese_font(24)
big_font = get_chinese_font(48)
# 音乐音效(找不到文件自动跳过)
place_sound = win_sound = lose_sound = None
try:
pygame.mixer.music.load("anything/bg_music.mp3")
pygame.mixer.music.set_volume(0.3)
pygame.mixer.music.play(-1)
except:
pass
try:
place_sound = pygame.mixer.Sound("anything/place.wav")
win_sound = pygame.mixer.Sound("anything/win.wav")
lose_sound = pygame.mixer.Sound("anything/lose.wav")
except:
pass
# 游戏状态
board = [ * (BOARD_LINE - 1) for _ in range(BOARD_LINE - 1)]
current_player = 1
game_mode = 0
game_over = False
winner = 0
# 按钮区域
btn_1_rect = pygame.Rect(50, WINDOW_H - 50, 150, 40)
btn_2_rect = pygame.Rect(250, WINDOW_H - 50, 150, 40)
btn_reset_rect = pygame.Rect(450, WINDOW_H - 50, 150, 40)
# 四个方向
DIRS = [(1, 0), (0, 1), (1, 1), (1, -1)]
# ===================== 绘制函数 =====================
def draw_board():
screen.fill(BOARD_COLOR)
for i in range(BOARD_LINE):
pygame.draw.line(screen, BLACK,
(PADDING, PADDING + i * GRID_SIZE),
(PADDING + BOARD_WIDTH, PADDING + i * GRID_SIZE), 1)
pygame.draw.line(screen, BLACK,
(PADDING + i * GRID_SIZE, PADDING),
(PADDING + i * GRID_SIZE, PADDING + BOARD_WIDTH), 1)
pos =
for x in pos:
for y in pos:
cx = PADDING + x * GRID_SIZE
cy = PADDING + y * GRID_SIZE
pygame.draw.circle(screen, BLACK, (cx, cy), 4)
def draw_pieces():
for x in range(BOARD_LINE - 1):
for y in range(BOARD_LINE - 1):
val = board
if val == 0:
continue
cx = PADDING + x * GRID_SIZE
cy = PADDING + y * GRID_SIZE
radius = GRID_SIZE // 2 - 2
if val == 1:
pygame.draw.circle(screen, BLACK, (cx, cy), radius)
else:
pygame.draw.circle(screen, WHITE, (cx, cy), radius)
pygame.draw.circle(screen, BLACK, (cx, cy), radius, 1)
def draw_buttons():
mp = pygame.mouse.get_pos()
# 双人
c = HOVER_COLOR if btn_1_rect.collidepoint(mp) else GRAY
pygame.draw.rect(screen, c, btn_1_rect)
pygame.draw.rect(screen, BLACK, btn_1_rect, 2)
t = font.render("双人对战", True, BLACK)
screen.blit(t, (btn_1_rect.x + 30, btn_1_rect.y + 8))
# 人机
c = HOVER_COLOR if btn_2_rect.collidepoint(mp) else GRAY
pygame.draw.rect(screen, c, btn_2_rect)
pygame.draw.rect(screen, BLACK, btn_2_rect, 2)
t = font.render("人机对战", True, BLACK)
screen.blit(t, (btn_2_rect.x + 30, btn_2_rect.y + 8))
# 重置
c = HOVER_COLOR if btn_reset_rect.collidepoint(mp) else GRAY
pygame.draw.rect(screen, c, btn_reset_rect)
pygame.draw.rect(screen, BLACK, btn_reset_rect, 2)
t = font.render("重置游戏", True, BLACK)
screen.blit(t, (btn_reset_rect.x + 30, btn_reset_rect.y + 8))
def draw_win_popup():
if not game_over:
return
overlay = pygame.Surface((WINDOW_W, WINDOW_H), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 180))
screen.blit(overlay, (0, 0))
rect = pygame.Rect(WINDOW_W//2-150, WINDOW_H//2-80, 300, 160)
pygame.draw.rect(screen, WHITE, rect)
pygame.draw.rect(screen, BLACK, rect, 3)
if winner == 1:
txt = big_font.render("黑棋获胜!", True, BLACK)
elif winner == 2:
txt = big_font.render("白棋获胜!", True, BLACK)
else:
txt = big_font.render("平局!", True, BLACK)
screen.blit(txt, (rect.centerx - txt.get_width()//2, rect.y + 30))
tip = font.render("点击任意位置继续", True, BLACK)
screen.blit(tip, (rect.centerx - tip.get_width()//2, rect.y + 100))
# ===================== 逻辑 & 增强AI =====================
def get_mouse_pos(pos):
x, y = pos
xi = round((x - PADDING) / GRID_SIZE)
yi = round((y - PADDING) / GRID_SIZE)
if 0 <= xi < BOARD_LINE-1 and 0 <= yi < BOARD_LINE-1:
return xi, yi
return -1, -1
def is_win(x, y, player):
for dx, dy in DIRS:
cnt = 1
nx, ny = x+dx, y+dy
while 0<=nx<BOARD_LINE-1 and 0<=ny<BOARD_LINE-1 and board==player:
cnt +=1
nx += dx
ny += dy
nx, ny = x-dx, y-dy
while 0<=nx<BOARD_LINE-1 and 0<=ny<BOARD_LINE-1 and board==player:
cnt +=1
nx -= dx
ny -= dy
if cnt >=5:
return True
return False
# 评估单个点位分数(核心AI算法)
def eval_point(x, y, player):
score = 0
enemy = 1 if player == 2 else 2
for dx, dy in DIRS:
# 向一侧延伸
p1, e1, b1 = 0, 0, 0
nx, ny = x+dx, y+dy
while 0<=nx<BOARD_LINE-1 and 0<=ny<BOARD_LINE-1:
if board == player:
p1 +=1
elif board == enemy:
e1 +=1
break
else:
b1 +=1
break
nx += dx
ny += dy
# 另一侧延伸
p2, e2, b2 = 0, 0, 0
nx, ny = x-dx, y-dy
while 0<=nx<BOARD_LINE-1 and 0<=ny<BOARD_LINE-1:
if board == player:
p2 +=1
elif board == enemy:
e2 +=1
break
else:
b2 +=1
break
nx -= dx
ny -= dy
total_p = p1 + p2
total_e = e1 + e2
# 计分规则(五子棋棋型权重)
if total_p >=4:
score += 100000 # 五连,必胜
elif total_p ==3:
if e1==0 and e2==0:
score += 10000# 活四
elif e1==0 or e2==0:
score += 5000 # 冲四
elif total_p ==2:
if e1==0 and e2==0:
score += 1000 # 活三
elif e1==0 or e2==0:
score += 500 # 眠三
elif total_p ==1:
if e1==0 and e2==0:
score += 100 # 活二
else:
score += 50
# 靠近棋盘中心加分(优先占天元/星位)
cx = (BOARD_LINE-2) // 2
cy = (BOARD_LINE-2) // 2
dis = abs(x-cx) + abs(y-cy)
score += (20 - dis) * 2
return score
# 强化AI:全局选最优点
def ai_play():
best_score = -1
best_pos = None
# 遍历所有空位
for x in range(BOARD_LINE-1):
for y in range(BOARD_LINE-1):
if board != 0:
continue
# 1. AI自己进攻分数
board = 2
s1 = eval_point(x, y, 2)
# 2. 防守:玩家在此落子的分数(必须堵)
s2 = eval_point(x, y, 1)
total = s1 + s2 * 1.2# 防守权重略高
board = 0
if total > best_score:
best_score = total
best_pos = (x, y)
if best_pos:
x, y = best_pos
board = 2
if place_sound:
place_sound.play()
def reset_game():
global board, current_player, game_over, winner
board = [*(BOARD_LINE-1) for _ in range(BOARD_LINE-1)]
current_player = 1
game_over = False
winner = 0
# ===================== 主循环 =====================
def main():
global current_player, game_mode, game_over, winner
clock = pygame.time.Clock()
running = True
while running:
clock.tick(60)
draw_board()
draw_pieces()
draw_buttons()
draw_win_popup()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 胜负弹窗点击重置
if game_over and event.type == pygame.MOUSEBUTTONDOWN:
reset_game()
continue
# 按钮点击
if event.type == pygame.MOUSEBUTTONDOWN:
mx, my = pygame.mouse.get_pos()
if btn_1_rect.collidepoint((mx, my)):
game_mode = 1
reset_game()
elif btn_2_rect.collidepoint((mx, my)):
game_mode = 2
reset_game()
elif btn_reset_rect.collidepoint((mx, my)):
reset_game()
# R键重置
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r:
reset_game()
# 棋盘落子
if event.type == pygame.MOUSEBUTTONDOWN and not game_over and game_mode != 0:
mx, my = pygame.mouse.get_pos()
x, y = get_mouse_pos((mx, my))
if x == -1 or y == -1 or board != 0:
continue
if game_mode == 1:
# 双人模式
board = current_player
if place_sound:
place_sound.play()
if is_win(x, y, current_player):
game_over = True
winner = current_player
if winner == 1 and win_sound:
win_sound.play()
elif winner == 2 and lose_sound:
lose_sound.play()
current_player = 2 if current_player == 1 else 1
else:
# 人机模式
if current_player == 1:
board = 1
if place_sound:
place_sound.play()
if is_win(x, y, 1):
game_over = True
winner = 1
if win_sound:
win_sound.play()
else:
# AI落子
ai_play()
# 检测AI是否获胜
win_flag = False
for i in range(BOARD_LINE-1):
for j in range(BOARD_LINE-1):
if board == 2 and is_win(i, j, 2):
game_over = True
winner = 2
win_flag = True
if lose_sound:
lose_sound.play()
break
if win_flag:
break
pygame.display.update()
pygame.quit()
sys.exit()
if __name__ == "__main__":
main() 以下是针对你提供的五子棋代码的几个关键问题和改进建议,按照 BBCode 格式整理,请对照检查:
[*]board初始化错误:第15行的写法不正确,应该使用嵌套列表推导式。当前写法会导致语法错误。
# 正确的初始化方式
board = [ * (BOARD_LINE - 1) for _ in range(BOARD_LINE - 1)]
[*]缺少方向定义:第39行 DIRS = 不完整,必须补充四个方向的偏移值,用于胜负判断和AI。
DIRS = [(1,0), (0,1), (1,1), (1,-1)]
[*]绘制棋子时的索引错误:第33行写法有误,应使用二维索引 board 而非 boardy。
# 正确写法
val = board
[*]游戏胜负判断功能缺失:代码中没有实现检测五子连珠的逻辑,即使满足胜利条件也不会触发。需要在每次落子后调用检查函数。
[*]鼠标点击处理未完成:get_mouse_pos 函数虽然定义了,但没有被调用。主循环中需要使用鼠标位置来判断落子点。
[*]AI对战功能为空白:虽然写了“人机对战”按钮,但没有任何AI算法代码。建议先实现一个简单的随机AI或基于评分的贪心AI。
此外,建议在代码开头加入 pygame.display.set_mode 时使用 pygame.RESIZABLE 标志,方便调试窗口大小;音乐和音效路径(anything/)若不存在会导致异常,建议用 try-except 处理并友好提示。
你可以根据以上建议逐步修正代码,完成后即可实现一个可玩的双人对战五子棋程序。如果需要进一步的AI教学或完整代码示例,请说明具体需求,我会继续协助你。
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页:
[1]