当前位置:   article > 正文

python程序设计基础课程设计--五子棋小游戏

python程序设计基础课程设计--五子棋小游戏

目录

一、项目简介

二、项目采用技术

1、基于的开发环境:

2、用到的框架:

三、功能需求分析

四、项目核心代码1、GameObject类

2、按钮类

3、判断落子位置是否合理

4、胜利说明

5、胜利检测

6、遍历棋盘每行获得的分数

7、 计算某一方的附加分

8、计算棋盘终总分

9、ai落子决策

10、窗体显示

11、棋形价值模型

12、游戏基础框架

五、系统演示操作视频

六、团队成员负责模块

一、项目简介

        使用python语言实现了五子棋小游戏,在游戏中,玩家可以与ai对战,两人轮流放置棋子,玩家在中途可以选着“悔棋”,撤销动作。最终,谁先连成五个棋子,游戏结束,获得胜利。

二、项目采用技术

1、基于的开发环境:

(1)Python解释器:

        从Python官方网站下载并安装适合本机操作系统的Python版本。

(2)PyCharm IDE:

        PyCharm是一个流行的Python集成开发环境(IDE),它提供了丰富的功能和工具,帮助你更高效地开发Python项目。从PyCharm官方网站下载并安装合适的PyCharm版本。

(3)五子棋库:

        为了实现五子棋游戏,需要使用一些额外的库,在PyCharm中,可以使用pip工具安装库。例如此次安装了Pygame游戏库。

2、用到的框架:

(1)pygame:

        pygame是一个用于编写视频游戏的Python库。虽然五子棋不是一个视频游戏,但pygame也可以用于创建图形用户界面,并且它提供了许多有用的工具和函数,可以帮助实现五子棋游戏。

(2)minimax:

        minimax是一种经典的AI算法,用于实现棋类游戏的AI。在五子棋游戏中,使用minimax算法来让计算最大得分。

3、引用的包:

        pygame、config、time。

三、功能需求分析

        玩家在一个15乘15的棋盘上轮流放置黑白棋子,谁先连成五个棋子就获得胜利。在游戏中,玩家可以与ai对战,两人轮流放置棋子,玩家在中途可以选着“悔棋”,点击悔棋按钮撤销一步(或几步)动作。最终,谁先连成五个棋子,游戏结束,获得胜利,并显示获胜者。五秒后,进去下一轮。

四、项目核心代码
1、GameObject类

  1. class GameObject:
  2. # 具有棋子的图像、类别和坐标三个属性
  3. def __init__(self, image, color, pos):
  4. self.image = image
  5. self.color = color
  6. self.pos = image.get_rect(center=pos

2、按钮类

  1. class Button(object):
  2. # 具有图像surface,宽高和坐标属性
  3. def __init__(self, text, color, x=None, y=None):
  4. self.surface = font_big.render(text, True, color)
  5. self.WIDTH = self.surface.get_width()
  6. self.HEIGHT = self.surface.get_height()
  7. self.x = x
  8. self.y = y
  9. # 这个方法用于确定鼠标是否点击了对应的按钮
  10. def check_click(self, position):
  11. x_match = self.x < position[0] < self.x + self.WIDTH
  12. y_match = self.y < position[1] < self.y + self.HEIGHT
  13. if x_match and y_match:
  14. return True
  15. else:
  16. return False

3、判断落子位置是否合理

  1. def set_chess(board_inner, x, y, color):
  2. if board_inner[x][y] != ' ':
  3. print('该位置已有棋子')
  4. print(x, y)
  5. return False
  6. else:
  7. board_inner[x][y] = color
  8. print(x, y)
  9. # for _ in board_inner:
  10. # print(_)
  11. # print()
  12. return True

4、胜利说明

  1. def check_win(board_inner):
  2. for list_str in board_inner:
  3. if ''.join(list_str).find('O' * 5) != -1:
  4. print('白棋获胜')
  5. return 0
  6. elif ''.join(list_str).find('X' * 5) != -1:
  7. print('黑棋获胜')
  8. return 1
  9. else:
  10. return -1

5、胜利检测

  1. def check_win_all(board_inner):
  2. board_c = [[] for _ in range(29)]
  3. for x in range(15):
  4. for y in range(15):
  5. board_c[x - y].append(board_inner[x][y])
  6. board_d = [[] for _ in range(29)]
  7. for x in range(15):
  8. for y in range(15):
  9. board_d[x + y].append(board_inner[x][y])
  10. return [check_win(board_inner), check_win([list(i) for i in zip(*board_inner)]), check_win(board_c),
  11. check_win(board_d)]

6、遍历棋盘每行获得的分数

  1. def value(board_inner, temp_list, value_model):
  2. score = 0
  3. num = 0
  4. # 第一层循环,遍历棋盘,计算每一行的得分
  5. for list_str in board_inner:
  6. # 如果一行里棋子数量少于2个,则跳过这一行
  7. if len(''.join(list_str).replace(' ', '')) < 2:
  8. continue
  9. # a是一个跳过参数,识别到指定棋型后,需要跳过若干位
  10. a = 0
  11. # 第二层循环,双指针第一个指针。每一行需要逐位识别,因为最短的棋型是5位,因此range(11)就够了
  12. for i in range(11):
  13. # 若a为0,则正常处理
  14. if a == 0:
  15. # temp用于存储识别到的棋型
  16. temp = []
  17. # 第三层循环,双指针第二个指针。最短的棋型是5位,最长的棋型是11位;因此需要不断截取指定长度的切片与不同的棋型进行对比
  18. for j in range(5, 12):
  19. # 如果超出本行长度,则跳出这层循环
  20. if i + j > len(list_str):
  21. break
  22. # num用于测试调试时计算循环次数
  23. num += 1
  24. # 如果是本行的开头,则与开头棋型和通用棋型进行对比
  25. if i == 0:
  26. # 第四层循环,把截取的字符串与棋型字符串逐一比对
  27. for k in value_model[0].items():
  28. # 如果切片与棋型完全相等,则把结果记录在temp中
  29. if ''.join(list_str[i:i + j]) == k[1][0]:
  30. temp.append((i, k))
  31. else:
  32. # 如果既不是开头也不是结尾,与通用棋型进行对比
  33. if i + j < len(list_str):
  34. for k in value_model[1].items():
  35. if ''.join(list_str[i:i + j]) == k[1][0]:
  36. temp.append((i, k))
  37. # 如果是结尾,与结尾棋型和通用棋型进行对比
  38. elif i + j == len(list_str):
  39. for k in value_model[2].items():
  40. if ''.join(list_str[i:i + j]) == k[1][0]:
  41. temp.append((i, k))
  42. # 如果a不等于1,则相当于跳过本次比对,a-1,temp要记得重新赋值为[]
  43. else:
  44. a -= 1
  45. temp = []
  46. # 对temp进行判空操作,避免报错
  47. if temp:
  48. # 用列表推导式从temp中抽离出有效信息,获得切片匹配到的最高分
  49. max_value = max([i[1][1][1] for i in temp])
  50. # 基于最高分找到匹配到的棋型
  51. max_shape = [i for i in temp if i[1][1][1] == max_value][0]
  52. # 棋型特殊处理,若匹配到某些棋型,需要在匹配时跳过若干位
  53. if max_shape[1][0] in ['4_3', '3_0', '3_16', '2_3']:
  54. a = 1
  55. elif max_shape[1][0] in ['4_5', '4_13', '3_5', '2_0']:
  56. a = 2
  57. elif max_shape[1][0] in ['4_4']:
  58. a = 5
  59. # 用temp_list保存每一行、每一位匹配到的棋型
  60. temp_list.append(max_shape)
  61. # 用score记录总分值
  62. score += max_value
  63. # print(temp_list)
  64. # print('value函数循环次数{}'.format(num))
  65. return score

7、 计算某一方的附加分

  1. def additional(te_list):
  2. score = 0
  3. # 对te_list做一些处理得到temp_list
  4. temp_list = [i[1][0] for i in te_list]
  5. # 死四 + 活三 >= 2,则附加分加30分
  6. if sum([temp_list.count(i) for i in ['3_0', '3_3', '3_4', '3_5', '3_6', '4_1', '4_2', '4_3', '4_4', '4_5', '4_6''4_7', '4_8', '4_9', '4_10', '4_11', '4_12', '4_13', '4_14', '4_15', '4_16', '4_17', '4_18', '4_19']]) >= 2:
  7. score += 30
  8. # 活三 + 死三 >= 2 且 活三 > 0,则附加分加15分
  9. elif sum([temp_list.count(i) for i in ['3_0', '3_3', '3_4', '3_5', '3_6', '3_1', '3_2', '3_7', '3_8', '3_9', '3_10', '3_11', '3_12', '3_13', '3_14', '3_15', '3_16', '3_17']]) >= 2 \
  10. and sum([temp_list.count(i) for i in ['3_0', '3_3', '3_4', '3_5', '3_6']]) > 0:
  11. score += 15
  12. return score

8、计算棋盘终总分

  1. def value_all(board_inner, temp_list, value_model):
  2. board_c = [[] for _ in range(29)]
  3. for x in range(15):
  4. for y in range(15):
  5. board_c[x + y].append(board_inner[x][y])
  6. board_d = [[] for _ in range(29)]
  7. for x in range(15):
  8. for y in range(15):
  9. board_d[x - y].append(board_inner[x][y])
  10. a = value(board_inner, temp_list, value_model)
  11. b = value([list(i) for i in zip(*board_inner)], temp_list, value_model)
  12. c = value(board_c, temp_list, value_model)
  13. d = value(board_d, temp_list, value_model)
  14. # 进行四个方向检测时,共用一个temp_list,因此附加分是考虑了全部四个方向
  15. add = additional(temp_list)
  16. # print(temp_list)
  17. # print('横{},竖{},正斜{},反斜{},附加{}'.format(a, b, c, d, add))
  18. return a + b + c + d + add

9、ai落子决策

  1. def value_chess(board_inner):
  2. t1 = time.time()
  3. # 如果棋盘为空,则黑棋直接落在天元位置,分数为0
  4. if board_inner == [[' '] * 15 for line in range(15)]:
  5. return 7, 7, 0
  6. # 一系列数据初始化
  7. temp_list_x = []
  8. temp_list_o = []
  9. tp_list_x_2 = []
  10. tp_list_o_2 = []
  11. tp_list_d = []
  12. score_x = value_all(board_inner, temp_list_x, value_model_X) # 落子前,黑棋分数
  13. pos_x = (0, 0)
  14. score_o = value_all(board_inner, temp_list_o, value_model_O) # 落子前,白棋分数
  15. pos_o = (0, 0)
  16. pos_d = (0, 0)
  17. score_x_2 = 0
  18. score_o_2 = 0
  19. score_diff = 0
  20. # 获得横竖两个方向,棋子落子范围;比如最左棋子在第5列,最右棋子在第9列;最上棋子在第5行,最下棋子在第9行;
  21. # 目的是为了缩小遍历范围;离当前棋子过远的区域,影响较小,就不再考虑遍历了;
  22. chess_range_x = [x for x in range(15) if ''.join(board_inner[x]).replace(' ', '') != '']
  23. chess_range_y = [y for y in range(15) if ''.join([list(i) for i in zip(*board_inner)][y]).replace(' ', '') != '']
  24. # 在棋子最大范围的基础上,做一些小小的拓展,得到落子检测区域;在上下左右四个方向各拓展4行/列;
  25. range_x = (max(0, min(chess_range_x) - 4), min(max(chess_range_x) + 4, 15))
  26. range_y = (max(0, min(chess_range_y) - 4), min(max(chess_range_y) + 4, 15))
  27. # 遍历落子检测区域所有的位置
  28. for x in range(*range_x):
  29. for y in range(*range_y):
  30. tp_list_x = []
  31. tp_list_o = []
  32. tp_list_c = []
  33. # 如果该位置已有棋子,则跳过
  34. if board_inner[x][y] != ' ':
  35. continue
  36. else:
  37. board_inner[x][y] = 'X'
  38. score_a = value_all(board_inner, tp_list_x, value_model_X) # 该位置落黑子,黑棋分数
  39. score_c = value_all(board_inner, tp_list_c, value_model_O) # 该位置落黑子,白棋分数
  40. # score_x_2用于记录黑棋最高分对应的落子信息
  41. if score_a > score_x_2:
  42. pos_x = x, y
  43. tp_list_x_2 = tp_list_x
  44. score_x_2 = score_a
  45. # 假定在该位置落白子
  46. board_inner[x][y] = 'O'
  47. score_b = value_all(board_inner, tp_list_o, value_model_O) # 该位置落白子,白棋分数
  48. # score_x_2用于记录白棋最高分对应的落子信息
  49. if score_b > score_o_2:
  50. pos_o = x, y
  51. tp_list_o_2 = tp_list_o
  52. score_o_2 = score_b
  53. # 将该位置棋子信息复原
  54. board_inner[x][y] = ' '
  55. # diff = = 1.1 * 黑棋分数增长 + (白棋原分数 - 落子后白棋分数) + (白棋预期最高分 - 落子后白棋分数)
  56. # 之所以(白棋预期最高分 - 落子后白棋分数),是为了增加防守的逻辑;
  57. # 之所以设置1.1的系数,是为了鼓励进攻,毕竟只有进攻才能获得胜利
  58. diff = 1.1 * (score_a - score_x) + score_o - score_c + score_b - score_c
  59. # score_diff用于记录diff的最大值
  60. if diff > score_diff:
  61. pos_d = x, y
  62. tp_list_d = tp_list_x
  63. score_diff = diff
  64. # 三种不同的策略,打印出对应的信息
  65. if score_x_2 >= 1000:
  66. print('——' * 30)
  67. print('策略1棋面:')
  68. print('黑棋棋面:', temp_list_x)
  69. print('白棋棋面:', temp_list_o)
  70. score = score_x_2
  71. pos = pos_x
  72. x, y = pos
  73. board_inner[x][y] = 'X'
  74. # temp_list_x.clear()
  75. # score = value_all(board_inner, temp_list_x, value_model_X)
  76. score_o_e = value_all(board_inner, temp_list_o, value_model_O)
  77. board_inner[x][y] = ' '
  78. print('执行策略1、直接获胜')
  79. print('黑棋最佳落子:坐标{},黑棋得分{},白棋得分{}'.format(pos, score, score_o_e))
  80. # print('白棋最佳落子:坐标{}'.format(pos_o))
  81. print('白棋原分数{},预期最高分数{},分数差值{}'.format(score_o, score_o_2, score_o_2 - score_o))
  82. print('若白棋落子{},白棋棋型{}'.format(pos_o, tp_list_o_2))
  83. print('黑棋原分数{},预期最高分数{},分数差值{}'.format(score_x, score_x_2, score_x_2 - score_x))
  84. print('若黑棋落子{},黑棋棋型{}'.format(pos_x, tp_list_x_2))
  85. print('——' * 30)
  86. elif score_o_2 >= 1000:
  87. print('——' * 30)
  88. print('策略2棋面:')
  89. print('黑棋棋面:', temp_list_x)
  90. print('白棋棋面:', temp_list_o)
  91. x, y = pos_o
  92. board_inner[x][y] = 'X'
  93. temp_list_x.clear()
  94. score = value_all(board_inner, temp_list_x, value_model_X)
  95. score_o_e = value_all(board_inner, temp_list_o, value_model_O)
  96. board_inner[x][y] = ' '
  97. pos = pos_o
  98. print('执行策略2、防守:防止对方获胜')
  99. print('黑棋最佳落子:坐标{},黑棋得分{},白棋得分{}'.format(pos, score, score_o_e))
  100. # print('白棋最佳落子:坐标{}'.format(pos_o))
  101. print('白棋原分数{},预期最高分数{},分数差值{}'.format(score_o, score_o_2, score_o_2 - score_o))
  102. print('若白棋落子{},白棋棋型{}'.format(pos_o, tp_list_o_2))
  103. print('黑棋原分数{},预期最高分数{},分数差值{}'.format(score_x, score_x_2, score_x_2 - score_x))
  104. print('若黑棋落子{},黑棋棋型{}'.format(pos_x, tp_list_x_2))
  105. print('——' * 30)
  106. else:
  107. print('——' * 30)
  108. print('策略3棋面:')
  109. print('黑棋棋面:', temp_list_x)
  110. print('白棋棋面:', temp_list_o)
  111. x, y = pos_d
  112. board_inner[x][y] = 'X'
  113. temp_list_x.clear()
  114. temp_list_o.clear()
  115. score = value_all(board_inner, temp_list_x, value_model_X)
  116. score_o_e = value_all(board_inner, temp_list_o, value_model_O)
  117. board_inner[x][y] = 'O'
  118. score_test = value_all(board_inner, [], value_model_O)
  119. board_inner[x][y] = ' '
  120. pos = pos_d
  121. print('黑棋原得分', score_x)
  122. print('黑棋得分', score)
  123. print('白棋原得分', score_o)
  124. print('白棋得分', score_o_e)
  125. print('若该位置落白棋,白棋得分', score_test)
  126. print('落子后黑棋棋面', temp_list_x)
  127. print('执行策略3、防守:防守+进攻')
  128. print('我方增长得分+对方减少得分:{}'.format(score_diff))
  129. print('黑棋最佳落子:坐标{},黑棋得分{},白棋得分{}'.format(pos, score, score_o_e))
  130. # print('白棋最佳落子:坐标{}'.format(pos_o))
  131. print('白棋原分数{},预期最高分数{},分数差值{}'.format(score_o, score_o_2, score_o_2 - score_o))
  132. print('若白棋落子{},白棋棋型{}'.format(pos_o, tp_list_o_2))
  133. print('黑棋原分数{},预期最高分数{},分数差值{}'.format(score_x, score_x_2, score_x_2 - score_x))
  134. print('若黑棋落子{},黑棋棋型{}'.format(pos_x, tp_list_x_2))
  135. print('——' * 30)
  136. print("代码执行完毕,用时{}秒".format(round(time.time() - t1, 2)))
  137. # 返回最终策略对应的黑棋落子坐标和黑棋得分
  138. return *pos, score

10、窗体显示

  1. def main(board_inner):
  2. pg.init()
  3. # 一系列数据初始化
  4. # pygame时钟
  5. clock = pg.time.Clock()
  6. # 下棋记录列表
  7. objects = []
  8. # 恢复棋子时用到的列表,即悔棋记录列表
  9. recover_objects = []
  10. # 将以上两个列表放到一个列表中,主要是增强抽象度,简少了代码行数
  11. ob_list = [objects, recover_objects]
  12. # 游戏窗口
  13. screen = pg.display.set_mode((WIDTH, HEIGHT))
  14. # 黑棋棋子图像
  15. black = pg.image.load("data/chess_black.png").convert_alpha()
  16. # 白棋棋子图像
  17. white = pg.image.load("data/chess_white.png").convert_alpha()
  18. # 棋盘背景图像
  19. background = pg.image.load("data/qp.png").convert_alpha()
  20. # 创建悔棋按钮
  21. regret_button = Button('悔棋', RED, 665, 200)
  22. # 创建恢复按钮
  23. recover_button = Button('恢复', BLUE, 665, 300)
  24. # 创建重新开始按钮
  25. restart_button = Button('重新开始', GREEN, 625, 400)
  26. # 把悔棋按钮打印游戏窗口
  27. screen.blit(regret_button.surface, (regret_button.x, regret_button.y))
  28. # 把恢复按钮打印游戏窗口
  29. screen.blit(recover_button.surface, (recover_button.x, recover_button.y))
  30. # 把重新开始按钮打印游戏窗口
  31. screen.blit(restart_button.surface, (restart_button.x, restart_button.y))
  32. # 窗体的标题
  33. pg.display.set_caption("五子棋")
  34. # 回合变量,用于识别当前是哪一方回合
  35. flag = 0
  36. # 主循环变量,用于控制主循环继续或者结束
  37. going = True
  38. # 棋子图像列表,主要是增强抽象度,简少了代码行数
  39. chess_list = [black, white]
  40. # 棋子类型列表,主要是增强抽象度,简少了代码行数
  41. letter_list = ['X', 'O']
  42. # 棋子文字名称列表,主要是增强抽象度,简少了代码行数
  43. word_list = ['黑棋', '白棋']
  44. # 棋子文字颜色列表,主要是增强抽象度,简少了代码行数
  45. word_color = [(0, 0, 0), (255, 255, 255)]
  46. while going:
  47. # 将棋盘背景打印到游戏窗口
  48. screen.blit(background, (0, 0))
  49. # 创建一个文本对象,显示当前是哪方的回合
  50. text = font.render("{}回合".format(word_list[flag]), True, word_color[flag])
  51. # 确定文本对象的显示位置
  52. text_pos = text.get_rect(centerx=background.get_width() / 2, y=2)
  53. # 将文本对象打印到游戏窗口
  54. screen.blit(text, text_pos)
  55. # 通过循环不断识别玩家操作
  56. for event in pg.event.get():
  57. # 如果关闭窗口,主循环结束
  58. if event.type == pg.QUIT:
  59. going = False
  60. # 如果点击键盘ESC键,主循环结束
  61. elif event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE:
  62. going = False
  63. # 如果玩家进行了鼠标点击操作
  64. elif event.type == pg.MOUSEBUTTONDOWN:
  65. # 获取鼠标点击坐标
  66. pos = pg.mouse.get_pos()
  67. # 如果点击了悔棋按钮或者恢复按钮
  68. if regret_button.check_click(pos) or recover_button.check_click(pos):
  69. # 点击悔棋按钮index = 0,点击恢复按钮index = 1
  70. index = 0 if regret_button.check_click(pos) else 1
  71. # 对指定列表进行判空操作,然后对下棋记录列表或者悔棋记录列表进行操作
  72. if ob_list[index]:
  73. # print(ob_list[index][-1].pos)
  74. # 将游戏/悔棋记录列表里的图像坐标,转化为board坐标
  75. # 人机对战需要黑方、白方各悔一步棋;(如果只是玩家悔棋,AI会立即下出一步,导致悔棋失败)
  76. x, y = [round((p + 18 - 27) / 40) for p in ob_list[index][-1].pos[:2]]
  77. # print(y, x)
  78. # 如果是悔棋操作,则board指定元素值恢复为' ';如果是恢复操作,则指定坐标board指定元素重新赋值
  79. board_inner[y][x] = ' ' if index == 0 else ob_list[index][-1].color
  80. # 将游戏/悔棋记录列表的最后一个值添加到悔棋/下棋记录列表
  81. ob_list[index - 1].append(ob_list[index][-1])
  82. # 将游戏/悔棋记录列表的最后一个值删除
  83. ob_list[index].pop()
  84. x, y = [round((p + 18 - 27) / 40) for p in ob_list[index][-1].pos[:2]]
  85. # print(y, x)
  86. # 如果是悔棋操作,则board指定元素值恢复为' ';如果是恢复操作,则指定坐标board指定元素重新赋值
  87. board_inner[y][x] = ' ' if index == 0 else ob_list[index][-1].color
  88. # 将游戏/悔棋记录列表的最后一个值添加到悔棋/下棋记录列表
  89. ob_list[index - 1].append(ob_list[index][-1])
  90. # 将游戏/悔棋记录列表的最后一个值删除
  91. ob_list[index].pop()
  92. # flag = [1, 0][flag] # 变更回合方
  93. elif restart_button.check_click(pos):
  94. # 提示文案
  95. hint_text = font.render("游戏重新开始", True, word_color[flag])
  96. # 提示文案位置
  97. hint_text_pos = hint_text.get_rect(centerx=background.get_width() / 2, y=200)
  98. # 把提示文案打印到游戏窗口
  99. screen.blit(hint_text, hint_text_pos)
  100. # 对游戏窗口进行刷新
  101. pg.display.update()
  102. # 暂停1秒,保证文案能够清晰展示
  103. pg.time.delay(1000)
  104. # 对board进行初始化
  105. board_inner = [[' '] * 15 for _ in range(15)]
  106. # 下棋记录列表初始化
  107. objects.clear()
  108. # 悔棋记录列表初始化
  109. recover_objects.clear()
  110. # flag初始化
  111. flag = 0
  112. # 通过continue跳过下一行代码,从而保证flag赋值不会异常
  113. continue
  114. else:
  115. # 若用户点击的不是悔棋、恢复按钮,则进行落子操作
  116. # 将用户鼠标点击位置的坐标,换算为board坐标
  117. a, b = round((pos[0] - 27) / 40), round((pos[1] - 27) / 40)
  118. # 若坐标非法(即点击到了黑色区域),则不做处理
  119. if a >= 15 or b >= 15:
  120. continue
  121. else:
  122. # 将a、b进行处理得到x和y
  123. x, y = max(0, a) if a < 0 else min(a, 14), max(0, b) if b < 0 else min(b, 14)
  124. # 若落子操作合法,则进行落子
  125. if set_chess(board_inner, y, x, letter_list[flag]):
  126. # 下棋记录列表添加指定棋子
  127. objects.append(GameObject(chess_list[flag], letter_list[flag], (27 + x * 40, 27 + y * 40)))
  128. # 一旦成功落子,则将悔棋记录列表清空;不这么做,一旦在悔棋和恢复中间掺杂落子操作,就会有问题
  129. recover_objects.clear()
  130. # 判断是否出现获胜方
  131. if 0 in check_win_all(board_inner) or 1 in check_win_all(board_inner):
  132. # 将下棋记录的棋子打印到游戏窗口
  133. for o in objects:
  134. screen.blit(o.image, o.pos)
  135. # 根据flag获取到当前获胜方,生成获胜文案
  136. win_text = font.render("{}获胜,游戏5秒后重新开始".format(word_list[flag]), True,
  137. word_color[flag])
  138. # 设定获胜文案的位置
  139. win_text_pos = win_text.get_rect(centerx=background.get_width() / 2, y=200)
  140. # 把获胜文案打印到游戏窗口
  141. screen.blit(win_text, win_text_pos)
  142. # 对游戏窗口进行刷新
  143. pg.display.update()
  144. # 暂停5秒,保证文案能够清晰展示
  145. pg.time.delay(5000)
  146. # 对board进行初始化
  147. board_inner = [[' '] * 15 for _ in range(15)]
  148. # 下棋记录列表初始化
  149. objects.clear()
  150. # 悔棋记录列表初始化
  151. recover_objects.clear()
  152. # flag初始化
  153. flag = 0
  154. # 通过continue跳过下一行代码,从而保证flag赋值不会异常
  155. continue
  156. flag = [1, 0][flag]
  157. # 若落子位置已经有棋子,则进行提示
  158. else:
  159. # 提示文案
  160. hint_text = font.render("该位置已有棋子", True, word_color[flag])
  161. # 提示文案位置
  162. hint_text_pos = hint_text.get_rect(centerx=background.get_width() / 2, y=200)
  163. # 将下棋记录的棋子打印到游戏窗口
  164. for o in objects:
  165. screen.blit(o.image, o.pos)
  166. # 把提示文案打印到游戏窗口
  167. screen.blit(hint_text, hint_text_pos)
  168. # 对游戏窗口进行刷新
  169. pg.display.update()
  170. # 暂停0.3秒,保证文案能够清晰展示
  171. pg.time.delay(300)
  172. # AI执黑,AI进行落子
  173. if flag == 0:
  174. y, x = value_chess(board_inner)[:2]
  175. if set_chess(board_inner, y, x, letter_list[flag]):
  176. objects.append(GameObject(chess_list[flag], letter_list[flag], (27 + x * 40, 27 + y * 40)))
  177. flag = [1, 0][flag]
  178. # 将下棋记录的棋子打印到游戏窗口
  179. for o in objects:
  180. screen.blit(o.image, o.pos)
  181. # 游戏帧率每秒60帧
  182. clock.tick(60)
  183. # 对游戏窗口进行刷新
  184. pg.display.update()

11、棋形价值模型

  1. WIDTH, HEIGHT = 800, 615
  2. RED = (255, 48, 48)
  3. BLUE = (65, 105, 225)
  4. GREEN = (0, 139, 0)
  5. board = [[' '] * 15 for line in range(15)]
  6. # value_model_X_test = {
  7. # '5': ('XXXXX', 1000),
  8. #
  9. # '4_0': (' XXXX ', 400),
  10. # '4_1': (' XXXXO', 100),
  11. # '4_2': ('OXXXX ', 100),
  12. # '4_11': ('XXXX ', 100), # 开头
  13. # '4_10': (' XXXX', 100), # 结尾
  14. #
  15. # '4_9': ('OXXX X', 82),
  16. # '4_17': ('XXX XO', 86),
  17. # '4_15': ('XXX X', 82), # 开头
  18. # '4_19': ('XXX X', 86), # 结尾
  19. # '4_5': (' XXX X', 120),
  20. #
  21. # '4_7': ('XX XXO', 84),
  22. # '4_8': ('OXX XX', 84),
  23. # '4_13': ('XX XX ', 84), # 开头
  24. # '4_4': (' XX XX ', 110),
  25. # '4_14': ('XX XX', 84), # 结尾
  26. #
  27. # '4_3': ('X XXX ', 120),
  28. # '4_6': ('X XXXO', 82),
  29. # '4_16': ('OX XXX', 86),
  30. # '4_18': ('X XXX', 86), # 开头
  31. # '4_12': ('X XXX', 82), # 结尾
  32. #
  33. # '3_0': (' XXX ', 60),
  34. # '3_1': (' XXXO', 25),
  35. # '3_2': ('OXXX ', 25),
  36. # '3_3': (' XXX ', 30),
  37. # '3_4': (' XXX ', 30),
  38. # '3_10': ('XXX ', 25), # 开头
  39. # '3_12': (' XXX', 25), # 结尾
  40. #
  41. # '3_6': (' XX X ', 37),
  42. # '3_17': (' XX X', 23), # 结尾
  43. # '3_15': (' XX XO', 23),
  44. # '3_11': ('XX X ', 21), # 开头
  45. # '3_8': ('OXX X ', 21),
  46. #
  47. # '3_5': (' X XX ', 37),
  48. # '3_9': (' X XXO', 21),
  49. # '3_14': ('OX XX ', 23),
  50. # '3_16': ('X XX ', 23), # 开头
  51. # '3_13': (' X XX', 21), # 结尾
  52. #
  53. # '3_7': (' X X X ', 27),
  54. #
  55. # '2_0': (' XX ', 8),
  56. # '2_1': (' XXO', 2),
  57. # '2_2': ('OXX ', 2),
  58. # '2_3': (' XX ', 5),
  59. # '2_4': (' XX ', 4),
  60. # '2_6': ('XX ', 2), # 开头
  61. # '2_7': (' XX', 2), # 结尾
  62. #
  63. # '2_5': (' X X ', 4)
  64. # }
  65. value_model_X = [
  66. # 开头检测
  67. {
  68. '5': ('XXXXX', 1000),
  69. '4_0': (' XXXX ', 400),
  70. '4_1': (' XXXXO', 100),
  71. '4_2': ('OXXXX ', 100),
  72. '4_3': ('X XXX ', 120),
  73. '4_4': (' XX XX ', 110),
  74. '4_5': (' XXX X', 120),
  75. '4_6': ('X XXXO', 82),
  76. '4_7': ('XX XXO', 84),
  77. '4_8': ('OXX XX', 84),
  78. '4_9': ('OXXX X', 82),
  79. '4_16': ('OX XXX', 86),
  80. '4_17': ('XXX XO', 86),
  81. '4_11': ('XXXX ', 100), # 开头
  82. '4_13': ('XX XX ', 84), # 开头
  83. '4_15': ('XXX X', 82), # 开头
  84. '4_18': ('X XXX', 86), # 开头
  85. '3_0': (' XXX ', 60),
  86. '3_1': (' XXXO', 25),
  87. '3_2': ('OXXX ', 25),
  88. '3_3': (' XXX ', 30),
  89. '3_4': (' XXX ', 30),
  90. '3_5': (' X XX ', 37),
  91. '3_6': (' XX X ', 37),
  92. '3_7': (' X X X ', 27),
  93. '3_8': ('OXX X ', 21),
  94. '3_9': (' X XXO', 21),
  95. '3_14': ('OX XX ', 23),
  96. '3_15': (' XX XO', 23),
  97. '3_10': ('XXX ', 25), # 开头
  98. '3_11': ('XX X ', 21), # 开头
  99. '3_16': ('X XX ', 23), # 开头
  100. '2_0': (' XX ', 8),
  101. '2_1': (' XXO', 2),
  102. '2_2': ('OXX ', 2),
  103. '2_3': (' XX ', 5),
  104. '2_4': (' XX ', 4),
  105. '2_5': (' X X ', 4),
  106. '2_6': ('XX ', 2), # 开头
  107. },
  108. # 中间检测
  109. {
  110. '5': ('XXXXX', 1000),
  111. '4_0': (' XXXX ', 400),
  112. '4_1': (' XXXXO', 100),
  113. '4_2': ('OXXXX ', 100),
  114. '4_3': ('X XXX ', 120),
  115. '4_4': (' XX XX ', 110),
  116. '4_5': (' XXX X', 120),
  117. '4_6': ('X XXXO', 82),
  118. '4_7': ('XX XXO', 84),
  119. '4_8': ('OXX XX', 84),
  120. '4_9': ('OXXX X', 82),
  121. '4_16': ('OX XXX', 86),
  122. '4_17': ('XXX XO', 86),
  123. '3_0': (' XXX ', 60),
  124. '3_1': (' XXXO', 25),
  125. '3_2': ('OXXX ', 25),
  126. '3_3': (' XXX ', 30),
  127. '3_4': (' XXX ', 30),
  128. '3_5': (' X XX ', 37),
  129. '3_6': (' XX X ', 37),
  130. '3_7': (' X X X ', 27),
  131. '3_8': ('OXX X ', 21),
  132. '3_9': (' X XXO', 21),
  133. '3_14': ('OX XX ', 23),
  134. '3_15': (' XX XO', 23),
  135. '2_0': (' XX ', 8),
  136. '2_1': (' XXO', 2),
  137. '2_2': ('OXX ', 2),
  138. '2_3': (' XX ', 5),
  139. '2_4': (' XX ', 4),
  140. '2_5': (' X X ', 4)
  141. },
  142. # 结尾检测
  143. {
  144. '5': ('XXXXX', 1000),
  145. '4_0': (' XXXX ', 400),
  146. '4_1': (' XXXXO', 100),
  147. '4_2': ('OXXXX ', 100),
  148. '4_3': ('X XXX ', 120),
  149. '4_4': (' XX XX ', 110),
  150. '4_5': (' XXX X', 120),
  151. '4_6': ('X XXXO', 82),
  152. '4_7': ('XX XXO', 84),
  153. '4_8': ('OXX XX', 84),
  154. '4_9': ('OXXX X', 82),
  155. '4_16': ('OX XXX', 86),
  156. '4_17': ('XXX XO', 86),
  157. '4_10': (' XXXX', 100), # 结尾
  158. '4_12': ('X XXX', 82), # 结尾
  159. '4_14': ('XX XX', 84), # 结尾
  160. '4_19': ('XXX X', 86), # 结尾
  161. '3_0': (' XXX ', 60),
  162. '3_1': (' XXXO', 25),
  163. '3_2': ('OXXX ', 25),
  164. '3_3': (' XXX ', 30),
  165. '3_4': (' XXX ', 30),
  166. '3_5': (' X XX ', 37),
  167. '3_6': (' XX X ', 37),
  168. '3_7': (' X X X ', 27),
  169. '3_8': ('OXX X ', 21),
  170. '3_9': (' X XXO', 21),
  171. '3_14': ('OX XX ', 23),
  172. '3_15': (' XX XO', 23),
  173. '3_12': (' XXX', 25), # 结尾
  174. '3_13': (' X XX', 21), # 结尾
  175. '3_17': (' XX X', 23), # 结尾
  176. '2_0': (' XX ', 8),
  177. '2_1': (' XXO', 2),
  178. '2_2': ('OXX ', 2),
  179. '2_3': (' XX ', 5),
  180. '2_4': (' XX ', 4),
  181. '2_5': (' X X ', 4),
  182. '2_7': (' XX', 2), # 结尾
  183. }
  184. ]
  185. value_model_O = [
  186. # 开头检测
  187. {
  188. '4_11': ('OOOO ', 100), # 开头
  189. '4_13': ('OO OO ', 84), # 开头
  190. '4_15': ('OOO O', 82), # 开头
  191. '4_18': ('O OOO', 86), # 开头
  192. '3_10': ('OOO ', 25), # 开头
  193. '3_11': ('OO O ', 21), # 开头
  194. '3_16': ('O OO ', 23), # 开头
  195. '2_6': ('OO ', 2), # 开头
  196. '5': ('OOOOO', 1000),
  197. '4_0': (' OOOO ', 400),
  198. '4_1': (' OOOOX', 100),
  199. '4_2': ('XOOOO ', 100),
  200. '4_3': ('O OOO ', 120),
  201. '4_4': (' OO OO ', 110),
  202. '4_5': (' OOO O', 120),
  203. '4_6': ('O OOOX', 82),
  204. '4_7': ('OO OOX', 84),
  205. '4_8': ('XOO OO', 84),
  206. '4_9': ('XOOO O', 84),
  207. '4_16': ('XO OOO', 86),
  208. '4_17': ('OOO OX', 86),
  209. '3_0': (' OOO ', 60),
  210. '3_1': (' OOOX', 25),
  211. '3_2': ('XOOO ', 25),
  212. '3_3': (' OOO ', 30),
  213. '3_4': (' OOO ', 30),
  214. '3_5': (' O OO ', 37),
  215. '3_6': (' OO O ', 37),
  216. '3_7': (' O O O ', 27),
  217. '3_8': ('XOO O ', 21),
  218. '3_9': (' O OOX', 21),
  219. '3_14': ('XO OO ', 23),
  220. '3_15': (' OO OX', 23),
  221. '2_0': (' OO ', 8),
  222. '2_1': (' OOX', 2),
  223. '2_2': ('XOO ', 2),
  224. '2_3': (' OO ', 5),
  225. '2_4': (' OO ', 4),
  226. '2_5': (' O O ', 4)
  227. },
  228. # 中间检测
  229. {
  230. '5': ('OOOOO', 1000),
  231. '4_0': (' OOOO ', 400),
  232. '4_1': (' OOOOX', 100),
  233. '4_2': ('XOOOO ', 100),
  234. '4_3': ('O OOO ', 120),
  235. '4_4': (' OO OO ', 110),
  236. '4_5': (' OOO O', 120),
  237. '4_6': ('O OOOX', 82),
  238. '4_7': ('OO OOX', 84),
  239. '4_8': ('XOO OO', 84),
  240. '4_9': ('XOOO O', 84),
  241. '4_16': ('XO OOO', 86),
  242. '4_17': ('OOO OX', 86),
  243. '3_0': (' OOO ', 60),
  244. '3_1': (' OOOX', 25),
  245. '3_2': ('XOOO ', 25),
  246. '3_3': (' OOO ', 30),
  247. '3_4': (' OOO ', 30),
  248. '3_5': (' O OO ', 37),
  249. '3_6': (' OO O ', 37),
  250. '3_7': (' O O O ', 27),
  251. '3_8': ('XOO O ', 21),
  252. '3_9': (' O OOX', 21),
  253. '3_14': ('XO OO ', 23),
  254. '3_15': (' OO OX', 23),
  255. '2_0': (' OO ', 8),
  256. '2_1': (' OOX', 2),
  257. '2_2': ('XOO ', 2),
  258. '2_3': (' OO ', 5),
  259. '2_4': (' OO ', 4),
  260. '2_5': (' O O ', 4)
  261. },
  262. # 结尾检测
  263. {
  264. '4_10': (' OOOO', 100), # 结尾
  265. '4_12': ('O OOO', 82), # 结尾
  266. '4_14': ('OO OO', 84), # 结尾
  267. '4_19': ('OOO O', 86), # 结尾
  268. '3_12': (' OOO', 25), # 结尾
  269. '3_13': (' O OO', 21), # 结尾
  270. '3_17': (' OO O', 23), # 结尾
  271. '2_7': (' OO', 2), # 结尾
  272. '5': ('OOOOO', 1000),
  273. '4_0': (' OOOO ', 400),
  274. '4_1': (' OOOOX', 100),
  275. '4_2': ('XOOOO ', 100),
  276. '4_3': ('O OOO ', 120),
  277. '4_4': (' OO OO ', 110),
  278. '4_5': (' OOO O', 120),
  279. '4_6': ('O OOOX', 82),
  280. '4_7': ('OO OOX', 84),
  281. '4_8': ('XOO OO', 84),
  282. '4_9': ('XOOO O', 84),
  283. '4_16': ('XO OOO', 86),
  284. '4_17': ('OOO OX', 86),
  285. '3_0': (' OOO ', 60),
  286. '3_1': (' OOOX', 25),
  287. '3_2': ('XOOO ', 25),
  288. '3_3': (' OOO ', 30),
  289. '3_4': (' OOO ', 30),
  290. '3_5': (' O OO ', 37),
  291. '3_6': (' OO O ', 37),
  292. '3_7': (' O O O ', 27),
  293. '3_8': ('XOO O ', 21),
  294. '3_9': (' O OOX', 21),
  295. '3_14': ('XO OO ', 23),
  296. '3_15': (' OO OX', 23),
  297. '2_0': (' OO ', 8),
  298. '2_1': (' OOX', 2),
  299. '2_2': ('XOO ', 2),
  300. '2_3': (' OO ', 5),
  301. '2_4': (' OO ', 4),
  302. '2_5': (' O O ', 4)
  303. }
  304. ]
  305. if __name__ == '__main__':
  306. test_list = list(set([_i[1] for _ in value_model_X for _i in _.items()]))
  307. print(test_list)
  308. print(len(test_list))

12、游戏基础框架

  1. import pygame as pg
  2. if __name__ == '__main__':
  3. # 模块初始化
  4. pg.init()
  5. # 基础设置:游戏窗口,帧率
  6. WIDTH = 615
  7. HEIGHT = 615
  8. # 时钟帧率
  9. clock = pg.time.Clock()
  10. # 游戏窗口
  11. screen = pg.display.set_mode((WIDTH, HEIGHT))
  12. # 导入素材
  13. black = pg.image.load("data/chess_black.png").convert_alpha()
  14. white = pg.image.load("data/chess_white.png").convert_alpha()
  15. background = pg.image.load("data/qp.png").convert_alpha()
  16. # 新创建的元素,全都放到这个列表里
  17. objects = []
  18. # 设置窗口标题
  19. pg.display.set_caption("五子棋")
  20. # 游戏主循环
  21. going = True
  22. while going:
  23. # 把背景图“打印”到窗口上
  24. screen.blit(background, (0, 0))
  25. # 根据玩家操作,执行对应指令
  26. for event in pg.event.get():
  27. # 如果关闭窗口,则主循环结束
  28. if event.type == pg.QUIT:
  29. going = False
  30. # 如果点击键盘esc键,则主循环结束
  31. elif event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE:
  32. going = False
  33. # 如果点击鼠标,则执行某个操作
  34. elif event.type == pg.MOUSEBUTTONDOWN:
  35. # 获取鼠标点击位置的坐标
  36. pos = pg.mouse.get_pos()
  37. print(pos)
  38. objects.append([black,(pos[0]-18,pos[1]-18)])
  39. # 把objects列表里的所有对象都打印到窗口
  40. for o in objects:
  41. screen.blit(o[0], o[1])
  42. # 帧率设置为每秒60帧
  43. clock.tick(60)
  44. # 对游戏窗口进行刷新操作(如果不update,窗口是黑的)
  45. pg.display.update()
  46. # 如果循环结束,则关闭进程
  47. pg.quit()

五、系统演示操作视频

五子棋

六、团队成员负责模块

名字负责模块代码行数
杨晴游戏主函数,悔棋操作的实现,悔棋按钮的函数,判断棋子位置是否合理,胜利检测,游戏基础框架以及游戏最终窗口的实现413
邓书勤

ai落子决策函数,遍历函数每一行的分数,添加附加分,计算每行分数的总和,编写棋形的价值模型,团队博客

432

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/331655
推荐阅读
相关标签
  

闽ICP备14008679号