鱼C论坛

 找回密码
 立即注册
查看: 16|回复: 1

[作品展示] 五子棋

[复制链接]
发表于 昨天 23:31 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
  1. import pygame
  2. import random
  3. import sys
  4. import os

  5. # ===================== 基础配置 =====================
  6. pygame.init()
  7. pygame.mixer.init()

  8. GRID_SIZE = 40
  9. BOARD_LINE = 15
  10. BOARD_WIDTH = GRID_SIZE * (BOARD_LINE - 1)
  11. PADDING = 30
  12. WINDOW_W = BOARD_WIDTH + PADDING * 2
  13. WINDOW_H = BOARD_WIDTH + PADDING * 2 + 60

  14. # 颜色
  15. WHITE = (255, 255, 255)
  16. BLACK = (0, 0, 0)
  17. BOARD_COLOR = (220, 179, 92)
  18. RED = (200, 0, 0)
  19. BLUE = (0, 0, 200)
  20. GRAY = (180, 180, 180)
  21. HOVER_COLOR = (150, 150, 150)

  22. # 窗口 & 字体
  23. screen = pygame.display.set_mode((WINDOW_W, WINDOW_H))
  24. pygame.display.set_caption("五子棋 | 双人/人机对战")

  25. def get_chinese_font(size=24):
  26.     font_path = "C:/Windows/Fonts/simhei.ttf"
  27.     if os.path.exists(font_path):
  28.         return pygame.font.Font(font_path, size)
  29.     return pygame.font.Font(None, size)

  30. font = get_chinese_font(24)
  31. big_font = get_chinese_font(48)

  32. # 音乐音效(找不到文件自动跳过)
  33. place_sound = win_sound = lose_sound = None
  34. try:
  35.     pygame.mixer.music.load("anything/bg_music.mp3")
  36.     pygame.mixer.music.set_volume(0.3)
  37.     pygame.mixer.music.play(-1)
  38. except:
  39.     pass
  40. try:
  41.     place_sound = pygame.mixer.Sound("anything/place.wav")
  42.     win_sound = pygame.mixer.Sound("anything/win.wav")
  43.     lose_sound = pygame.mixer.Sound("anything/lose.wav")
  44. except:
  45.     pass

  46. # 游戏状态
  47. board = [[0] * (BOARD_LINE - 1) for _ in range(BOARD_LINE - 1)]
  48. current_player = 1
  49. game_mode = 0
  50. game_over = False
  51. winner = 0

  52. # 按钮区域
  53. btn_1_rect = pygame.Rect(50, WINDOW_H - 50, 150, 40)
  54. btn_2_rect = pygame.Rect(250, WINDOW_H - 50, 150, 40)
  55. btn_reset_rect = pygame.Rect(450, WINDOW_H - 50, 150, 40)

  56. # 四个方向
  57. DIRS = [(1, 0), (0, 1), (1, 1), (1, -1)]

  58. # ===================== 绘制函数 =====================
  59. def draw_board():
  60.     screen.fill(BOARD_COLOR)
  61.     for i in range(BOARD_LINE):
  62.         pygame.draw.line(screen, BLACK,
  63.                          (PADDING, PADDING + i * GRID_SIZE),
  64.                          (PADDING + BOARD_WIDTH, PADDING + i * GRID_SIZE), 1)
  65.         pygame.draw.line(screen, BLACK,
  66.                          (PADDING + i * GRID_SIZE, PADDING),
  67.                          (PADDING + i * GRID_SIZE, PADDING + BOARD_WIDTH), 1)
  68.     pos = [3, 7, 11]
  69.     for x in pos:
  70.         for y in pos:
  71.             cx = PADDING + x * GRID_SIZE
  72.             cy = PADDING + y * GRID_SIZE
  73.             pygame.draw.circle(screen, BLACK, (cx, cy), 4)

  74. def draw_pieces():
  75.     for x in range(BOARD_LINE - 1):
  76.         for y in range(BOARD_LINE - 1):
  77.             val = board[x][y]
  78.             if val == 0:
  79.                 continue
  80.             cx = PADDING + x * GRID_SIZE
  81.             cy = PADDING + y * GRID_SIZE
  82.             radius = GRID_SIZE // 2 - 2
  83.             if val == 1:
  84.                 pygame.draw.circle(screen, BLACK, (cx, cy), radius)
  85.             else:
  86.                 pygame.draw.circle(screen, WHITE, (cx, cy), radius)
  87.                 pygame.draw.circle(screen, BLACK, (cx, cy), radius, 1)

  88. def draw_buttons():
  89.     mp = pygame.mouse.get_pos()
  90.     # 双人
  91.     c = HOVER_COLOR if btn_1_rect.collidepoint(mp) else GRAY
  92.     pygame.draw.rect(screen, c, btn_1_rect)
  93.     pygame.draw.rect(screen, BLACK, btn_1_rect, 2)
  94.     t = font.render("双人对战", True, BLACK)
  95.     screen.blit(t, (btn_1_rect.x + 30, btn_1_rect.y + 8))
  96.     # 人机
  97.     c = HOVER_COLOR if btn_2_rect.collidepoint(mp) else GRAY
  98.     pygame.draw.rect(screen, c, btn_2_rect)
  99.     pygame.draw.rect(screen, BLACK, btn_2_rect, 2)
  100.     t = font.render("人机对战", True, BLACK)
  101.     screen.blit(t, (btn_2_rect.x + 30, btn_2_rect.y + 8))
  102.     # 重置
  103.     c = HOVER_COLOR if btn_reset_rect.collidepoint(mp) else GRAY
  104.     pygame.draw.rect(screen, c, btn_reset_rect)
  105.     pygame.draw.rect(screen, BLACK, btn_reset_rect, 2)
  106.     t = font.render("重置游戏", True, BLACK)
  107.     screen.blit(t, (btn_reset_rect.x + 30, btn_reset_rect.y + 8))

  108. def draw_win_popup():
  109.     if not game_over:
  110.         return
  111.     overlay = pygame.Surface((WINDOW_W, WINDOW_H), pygame.SRCALPHA)
  112.     overlay.fill((0, 0, 0, 180))
  113.     screen.blit(overlay, (0, 0))
  114.     rect = pygame.Rect(WINDOW_W//2-150, WINDOW_H//2-80, 300, 160)
  115.     pygame.draw.rect(screen, WHITE, rect)
  116.     pygame.draw.rect(screen, BLACK, rect, 3)
  117.     if winner == 1:
  118.         txt = big_font.render("黑棋获胜!", True, BLACK)
  119.     elif winner == 2:
  120.         txt = big_font.render("白棋获胜!", True, BLACK)
  121.     else:
  122.         txt = big_font.render("平局!", True, BLACK)
  123.     screen.blit(txt, (rect.centerx - txt.get_width()//2, rect.y + 30))
  124.     tip = font.render("点击任意位置继续", True, BLACK)
  125.     screen.blit(tip, (rect.centerx - tip.get_width()//2, rect.y + 100))

  126. # ===================== 逻辑 & 增强AI =====================
  127. def get_mouse_pos(pos):
  128.     x, y = pos
  129.     xi = round((x - PADDING) / GRID_SIZE)
  130.     yi = round((y - PADDING) / GRID_SIZE)
  131.     if 0 <= xi < BOARD_LINE-1 and 0 <= yi < BOARD_LINE-1:
  132.         return xi, yi
  133.     return -1, -1

  134. def is_win(x, y, player):
  135.     for dx, dy in DIRS:
  136.         cnt = 1
  137.         nx, ny = x+dx, y+dy
  138.         while 0<=nx<BOARD_LINE-1 and 0<=ny<BOARD_LINE-1 and board[nx][ny]==player:
  139.             cnt +=1
  140.             nx += dx
  141.             ny += dy
  142.         nx, ny = x-dx, y-dy
  143.         while 0<=nx<BOARD_LINE-1 and 0<=ny<BOARD_LINE-1 and board[nx][ny]==player:
  144.             cnt +=1
  145.             nx -= dx
  146.             ny -= dy
  147.         if cnt >=5:
  148.             return True
  149.     return False

  150. # 评估单个点位分数(核心AI算法)
  151. def eval_point(x, y, player):
  152.     score = 0
  153.     enemy = 1 if player == 2 else 2
  154.     for dx, dy in DIRS:
  155.         # 向一侧延伸
  156.         p1, e1, b1 = 0, 0, 0
  157.         nx, ny = x+dx, y+dy
  158.         while 0<=nx<BOARD_LINE-1 and 0<=ny<BOARD_LINE-1:
  159.             if board[nx][ny] == player:
  160.                 p1 +=1
  161.             elif board[nx][ny] == enemy:
  162.                 e1 +=1
  163.                 break
  164.             else:
  165.                 b1 +=1
  166.                 break
  167.             nx += dx
  168.             ny += dy
  169.         # 另一侧延伸
  170.         p2, e2, b2 = 0, 0, 0
  171.         nx, ny = x-dx, y-dy
  172.         while 0<=nx<BOARD_LINE-1 and 0<=ny<BOARD_LINE-1:
  173.             if board[nx][ny] == player:
  174.                 p2 +=1
  175.             elif board[nx][ny] == enemy:
  176.                 e2 +=1
  177.                 break
  178.             else:
  179.                 b2 +=1
  180.                 break
  181.             nx -= dx
  182.             ny -= dy
  183.         total_p = p1 + p2
  184.         total_e = e1 + e2

  185.         # 计分规则(五子棋棋型权重)
  186.         if total_p >=4:
  187.             score += 100000   # 五连,必胜
  188.         elif total_p ==3:
  189.             if e1==0 and e2==0:
  190.                 score += 10000  # 活四
  191.             elif e1==0 or e2==0:
  192.                 score += 5000   # 冲四
  193.         elif total_p ==2:
  194.             if e1==0 and e2==0:
  195.                 score += 1000   # 活三
  196.             elif e1==0 or e2==0:
  197.                 score += 500    # 眠三
  198.         elif total_p ==1:
  199.             if e1==0 and e2==0:
  200.                 score += 100    # 活二
  201.             else:
  202.                 score += 50
  203.     # 靠近棋盘中心加分(优先占天元/星位)
  204.     cx = (BOARD_LINE-2) // 2
  205.     cy = (BOARD_LINE-2) // 2
  206.     dis = abs(x-cx) + abs(y-cy)
  207.     score += (20 - dis) * 2
  208.     return score

  209. # 强化AI:全局选最优点
  210. def ai_play():
  211.     best_score = -1
  212.     best_pos = None
  213.     # 遍历所有空位
  214.     for x in range(BOARD_LINE-1):
  215.         for y in range(BOARD_LINE-1):
  216.             if board[x][y] != 0:
  217.                 continue
  218.             # 1. AI自己进攻分数
  219.             board[x][y] = 2
  220.             s1 = eval_point(x, y, 2)
  221.             # 2. 防守:玩家在此落子的分数(必须堵)
  222.             s2 = eval_point(x, y, 1)
  223.             total = s1 + s2 * 1.2  # 防守权重略高
  224.             board[x][y] = 0

  225.             if total > best_score:
  226.                 best_score = total
  227.                 best_pos = (x, y)
  228.     if best_pos:
  229.         x, y = best_pos
  230.         board[x][y] = 2
  231.         if place_sound:
  232.             place_sound.play()

  233. def reset_game():
  234.     global board, current_player, game_over, winner
  235.     board = [[0]*(BOARD_LINE-1) for _ in range(BOARD_LINE-1)]
  236.     current_player = 1
  237.     game_over = False
  238.     winner = 0

  239. # ===================== 主循环 =====================
  240. def main():
  241.     global current_player, game_mode, game_over, winner
  242.     clock = pygame.time.Clock()
  243.     running = True

  244.     while running:
  245.         clock.tick(60)
  246.         draw_board()
  247.         draw_pieces()
  248.         draw_buttons()
  249.         draw_win_popup()

  250.         for event in pygame.event.get():
  251.             if event.type == pygame.QUIT:
  252.                 running = False

  253.             # 胜负弹窗点击重置
  254.             if game_over and event.type == pygame.MOUSEBUTTONDOWN:
  255.                 reset_game()
  256.                 continue

  257.             # 按钮点击
  258.             if event.type == pygame.MOUSEBUTTONDOWN:
  259.                 mx, my = pygame.mouse.get_pos()
  260.                 if btn_1_rect.collidepoint((mx, my)):
  261.                     game_mode = 1
  262.                     reset_game()
  263.                 elif btn_2_rect.collidepoint((mx, my)):
  264.                     game_mode = 2
  265.                     reset_game()
  266.                 elif btn_reset_rect.collidepoint((mx, my)):
  267.                     reset_game()

  268.             # R键重置
  269.             if event.type == pygame.KEYDOWN:
  270.                 if event.key == pygame.K_r:
  271.                     reset_game()

  272.             # 棋盘落子
  273.             if event.type == pygame.MOUSEBUTTONDOWN and not game_over and game_mode != 0:
  274.                 mx, my = pygame.mouse.get_pos()
  275.                 x, y = get_mouse_pos((mx, my))
  276.                 if x == -1 or y == -1 or board[x][y] != 0:
  277.                     continue

  278.                 if game_mode == 1:
  279.                     # 双人模式
  280.                     board[x][y] = current_player
  281.                     if place_sound:
  282.                         place_sound.play()
  283.                     if is_win(x, y, current_player):
  284.                         game_over = True
  285.                         winner = current_player
  286.                         if winner == 1 and win_sound:
  287.                             win_sound.play()
  288.                         elif winner == 2 and lose_sound:
  289.                             lose_sound.play()
  290.                     current_player = 2 if current_player == 1 else 1
  291.                 else:
  292.                     # 人机模式
  293.                     if current_player == 1:
  294.                         board[x][y] = 1
  295.                         if place_sound:
  296.                             place_sound.play()
  297.                         if is_win(x, y, 1):
  298.                             game_over = True
  299.                             winner = 1
  300.                             if win_sound:
  301.                                 win_sound.play()
  302.                         else:
  303.                             # AI落子
  304.                             ai_play()
  305.                             # 检测AI是否获胜
  306.                             win_flag = False
  307.                             for i in range(BOARD_LINE-1):
  308.                                 for j in range(BOARD_LINE-1):
  309.                                     if board[i][j] == 2 and is_win(i, j, 2):
  310.                                         game_over = True
  311.                                         winner = 2
  312.                                         win_flag = True
  313.                                         if lose_sound:
  314.                                             lose_sound.play()
  315.                                         break
  316.                                 if win_flag:
  317.                                     break

  318.         pygame.display.update()

  319.     pygame.quit()
  320.     sys.exit()

  321. if __name__ == "__main__":
  322.     main()
复制代码
wzq.gif
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 昨天 23:32 | 显示全部楼层
以下是针对你提供的五子棋代码的几个关键问题和改进建议,按照 BBCode 格式整理,请对照检查:

  • board初始化错误:第15行的写法不正确,应该使用嵌套列表推导式。当前写法会导致语法错误。
    1. # 正确的初始化方式
    2. board = [[0] * (BOARD_LINE - 1) for _ in range(BOARD_LINE - 1)]
    复制代码

  • 缺少方向定义:第39行 DIRS = 不完整,必须补充四个方向的偏移值,用于胜负判断和AI。
    1. DIRS = [(1,0), (0,1), (1,1), (1,-1)]
    复制代码

  • 绘制棋子时的索引错误:第33行写法有误,应使用二维索引 board[x][y] 而非 board[x]y
    1. # 正确写法
    2. val = board[x][y]
    复制代码

  • 游戏胜负判断功能缺失:代码中没有实现检测五子连珠的逻辑,即使满足胜利条件也不会触发。需要在每次落子后调用检查函数。
  • 鼠标点击处理未完成get_mouse_pos 函数虽然定义了,但没有被调用。主循环中需要使用鼠标位置来判断落子点。
  • AI对战功能为空白:虽然写了“人机对战”按钮,但没有任何AI算法代码。建议先实现一个简单的随机AI或基于评分的贪心AI。


此外,建议在代码开头加入 pygame.display.set_mode 时使用 pygame.RESIZABLE 标志,方便调试窗口大小;音乐和音效路径(anything/)若不存在会导致异常,建议用 try-except 处理并友好提示。

你可以根据以上建议逐步修正代码,完成后即可实现一个可玩的双人对战五子棋程序。如果需要进一步的AI教学或完整代码示例,请说明具体需求,我会继续协助你。

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2026-6-10 02:19

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表