2023-12-13 18:21:15 [显示全部楼层]
1177浏览
查看: 1177|回复: 5

[项目分享] 用Python做一个下五子棋的“AI”

[复制链接]
本帖最后由 TRIM 于 2024-2-26 18:54 编辑

和AI下棋?当然,我在这个项目中并没有使用AI类的模块,但是,我通过对每一个坐标的计算,让这个程序几乎可以堵住任何情况下的黑子
相关函数请见代码中的the_chance_to_win()

仅仅是堵住?好吧,内容还没有完善,这个“AI”只会防守,不会进攻
“易守难攻”嘛!
但即使是这样,你也不一定下得过它哟!

程序代码放在最后面了,我打包了应用程序,可以直接游玩:
点此下载[gobang10200.exe]

当然,这并不是完全“智能”的。如果你可以找到其中的漏洞,并提出修改建议,那就更好啦!
操作方法:鼠标控制棋子落下,空格键重开
想看看它到底是怎么实现的?代码如下
  1. import sys
  2. import random
  3. import pygame
  4. # 画棋盘
  5. def draw_chessboard():
  6.     window.fill((219, 182, 123))
  7.     # pygame.draw.rect(window, (0, 0, 0), (2, 2, 758, 758), 4)
  8.     pygame.draw.rect(window, (0, 0, 0), (20, 20, 720, 720), 2)
  9.     for x in range(1, 18):
  10.         pygame.draw.line(window, (0, 0, 0), (x * 40 + 20, 20), (x * 40 + 20, 739), 2)
  11.     for y in range(1, 18):
  12.         pygame.draw.line(window, (0, 0, 0), (20, y * 40 + 20), (739, y * 40 + 20), 2)
  13.     pygame.draw.circle(window, (0, 0, 0), (141, 141), 6, 0)
  14.     pygame.draw.circle(window, (0, 0, 0), (381, 141), 6, 0)
  15.     pygame.draw.circle(window, (0, 0, 0), (621, 141), 6, 0)
  16.     pygame.draw.circle(window, (0, 0, 0), (141, 381), 6, 0)
  17.     pygame.draw.circle(window, (0, 0, 0), (381, 381), 6, 0)
  18.     pygame.draw.circle(window, (0, 0, 0), (621, 381), 6, 0)
  19.     pygame.draw.circle(window, (0, 0, 0), (141, 621), 6, 0)
  20.     pygame.draw.circle(window, (0, 0, 0), (381, 621), 6, 0)
  21.     pygame.draw.circle(window, (0, 0, 0), (621, 621), 6, 0)
  22.     text = font_30.render(tips, True, (255, 255, 255))
  23.     window.blit(text, (760, 365))
  24. # 刷新棋盘
  25. def chessboard_refresh():
  26.     draw_chessboard()
  27.     for black_coordinate in black_coordinate_list:
  28.         pygame.draw.circle(window, (0, 0, 0),
  29.                            ((black_coordinate[0] - 1) * 40 + 21, (black_coordinate[1] - 1) * 40 + 21), 17, 0)
  30.     for white_coordinate in white_coordinate_list:
  31.         pygame.draw.circle(window, (255, 255, 255),
  32.                            ((white_coordinate[0] - 1) * 40 + 21, (white_coordinate[1] - 1) * 40 + 21), 17, 0)
  33. # 将鼠标点击坐标转换成棋盘坐标
  34. def mouse_coordinate_to_chessboard_coordinate(mouse_x, mouse_y):
  35.     if mouse_x > 762 or mouse_y > 762:
  36.         return "out of range"
  37.     else:
  38.         return int((mouse_x - 2) / 40) + 1, int((mouse_y - 2) / 40) + 1
  39. # 放置棋子
  40. def place_chess(chessboard_coordinate_f, side_f):
  41.     if side_f == "black":
  42.         black_coordinate_list.append(chessboard_coordinate_f)
  43.     if side_f == "white":
  44.         white_coordinate_list.append(chessboard_coordinate_f)
  45. # 检查是否有某一方胜利
  46. def check_chess():
  47.     winner = " "
  48.     for x in range(1, 20):
  49.         for y in range(1, 20):
  50.             # 检查白子
  51.             count = 0
  52.             for near_x in range(-2, 3):
  53.                 if (x + near_x, y) in white_coordinate_list:
  54.                     count += 1
  55.                 if count == 5:
  56.                     winner = "white"
  57.             count = 0
  58.             for near_y in range(-2, 3):
  59.                 if (x, y + near_y) in white_coordinate_list:
  60.                     count += 1
  61.                 if count == 5:
  62.                     winner = "white"
  63.             count = 0
  64.             for near in range(-2, 3):
  65.                 if (x + near, y + near) in white_coordinate_list:
  66.                     count += 1
  67.                 if count == 5:
  68.                     winner = "white"
  69.             count = 0
  70.             for near in range(-2, 3):
  71.                 if (x + near, y - near) in white_coordinate_list:
  72.                     count += 1
  73.                 if count == 5:
  74.                     winner = "white"
  75.             # 检查黑子
  76.             count = 0
  77.             for near_x in range(-2, 3):
  78.                 if (x + near_x, y) in black_coordinate_list:
  79.                     count += 1
  80.                 if count == 5:
  81.                     winner = "black"
  82.             count = 0
  83.             for near_y in range(-2, 3):
  84.                 if (x, y + near_y) in black_coordinate_list:
  85.                     count += 1
  86.                 if count == 5:
  87.                     winner = "black"
  88.             count = 0
  89.             for near in range(-2, 3):
  90.                 if (x + near, y + near) in black_coordinate_list:
  91.                     count += 1
  92.                 if count == 5:
  93.                     winner = "black"
  94.             count = 0
  95.             for near in range(-2, 3):
  96.                 if (x + near, y - near) in black_coordinate_list:
  97.                     count += 1
  98.                 if count == 5:
  99.                     winner = "black"
  100.     return winner
  101. # 白子下此坐标,能赢的机会
  102. def the_chance_to_win(x, y):  # edge_■ empty_▫ black_● white_◌
  103.     global text_tp
  104.     total_chance = 0
  105.     text_tp += "水平方向:" + "\n"
  106.     # 水平方向,堵住黑子的个数
  107.     # 先获得该坐标附近9点的状态
  108.     chess_list_first = []
  109.     for i in range(-4,5):
  110.         if i == 0:
  111.             chess_list_first.append("◉")
  112.         elif x + i < 1 or x + i > 19:
  113.             chess_list_first.append("■")
  114.         elif (x + i, y) in black_coordinate_list:
  115.             chess_list_first.append("●")
  116.         elif (x + i, y) in white_coordinate_list:
  117.             chess_list_first.append("◌")
  118.         else:
  119.             chess_list_first.append("▫")
  120.     # 列表n1:排除从中间开始,已被白子挡住的黑子
  121.     list_slice_head = 0
  122.     list_slice_end = 9
  123.     for i in range (1,5):
  124.         if chess_list_first[4 - i] == "◌" or chess_list_first[4 - i] == "■":
  125.             if list_slice_head == 0:
  126.                 list_slice_head = 5 - i
  127.         if chess_list_first[4 + i] == "◌" or chess_list_first[4 + i] == "■":
  128.             if list_slice_end == 9:
  129.                 list_slice_end = 4 + i
  130.     chess_list_n1 = chess_list_first[list_slice_head:list_slice_end]
  131.     # 列表n2:从中间开始,与当前坐标紧紧相连的黑子
  132.     list_slice_head = 0
  133.     list_slice_end = 9
  134.     for i in range (1,5):
  135.         if chess_list_first[4 - i] == "▫" or chess_list_first[4 - i] == "◌":
  136.             if list_slice_head == 0:
  137.                 list_slice_head = 5 - i
  138.         if chess_list_first[4 + i] == "▫" or chess_list_first[4 + i] == "◌":
  139.             if list_slice_end == 9:
  140.                 list_slice_end = 4 + i
  141.     chess_list_n2 = chess_list_first[list_slice_head:list_slice_end]
  142.     du_zhu = 1
  143.     try:
  144.         if (x + list_slice_head - 5,y) in white_coordinate_list or x + list_slice_head - 5 < 1:
  145.             du_zhu += 0.5
  146.     except:
  147.         pass
  148.     try:
  149.         if (x + list_slice_end - 4,y) in white_coordinate_list or x + list_slice_end - 4 > 19:
  150.             du_zhu += 0.5
  151.     except:
  152.         pass
  153.     total_chance += chess_list_n2.count("●") * chess_list_n2.count("●")
  154.     total_chance += chess_list_n1.count("●")
  155.     if chess_list_n2.count("●") == 3 and du_zhu <= 1:
  156.         total_chance += 99999
  157.     if chess_list_n2.count("●") == 4 and du_zhu <= 1.5:
  158.         total_chance += 99999
  159.     if chess_list_n2.count("●") == 5 and du_zhu <= 2:
  160.         total_chance += 114514
  161.    
  162.     try:  
  163.         total_chance /= du_zhu
  164.     except:
  165.         pass
  166.     text_tp += "chess_list_first:" + str(chess_list_first) + "\n"
  167.     text_tp += "chess_list_n1:" + str(chess_list_n1) + "\n"
  168.     text_tp += "chess_list_n2:" + str(chess_list_n2) + "\n"
  169.    
  170.     # --------------------------------------------------------------------------------
  171.     text_tp += "竖直方向:" + "\n"
  172.     # 竖直方向,堵住黑子的个数
  173.     # 先获得该坐标附近9点的状态
  174.     chess_list_first = []
  175.     for i in range(-4,5):
  176.         if i == 0:
  177.             chess_list_first.append("◉")
  178.         elif y + i < 1 or y + i > 19:
  179.             chess_list_first.append("■")
  180.         elif (x, y + i) in black_coordinate_list:
  181.             chess_list_first.append("●")
  182.         elif (x, y + i) in white_coordinate_list:
  183.             chess_list_first.append("◌")
  184.         else:
  185.             chess_list_first.append("▫")
  186.     # 列表n1:排除从中间开始,已被白子挡住的黑子
  187.     list_slice_head = 0
  188.     list_slice_end = 9
  189.     for i in range (1,5):
  190.         if chess_list_first[4 - i] == "◌" or chess_list_first[4 - i] == "■":
  191.             if list_slice_head == 0:
  192.                 list_slice_head = 5 - i
  193.         if chess_list_first[4 + i] == "◌" or chess_list_first[4 + i] == "■":
  194.             if list_slice_end == 9:
  195.                 list_slice_end = 4 + i
  196.     chess_list_n1 = chess_list_first[list_slice_head:list_slice_end]
  197.     # 列表n2:从中间开始,与当前坐标紧紧相连的黑子
  198.     list_slice_head = 0
  199.     list_slice_end = 9
  200.     for i in range (1,5):
  201.         if chess_list_first[4 - i] == "▫" or chess_list_first[4 - i] == "◌":
  202.             if list_slice_head == 0:
  203.                 list_slice_head = 5 - i
  204.         if chess_list_first[4 + i] == "▫" or chess_list_first[4 + i] == "◌":
  205.             if list_slice_end == 9:
  206.                 list_slice_end = 4 + i
  207.     chess_list_n2 = chess_list_first[list_slice_head:list_slice_end]
  208.     du_zhu = 1
  209.     try:
  210.         if (x,y + list_slice_head - 5) in white_coordinate_list or y + list_slice_head - 5 < 1:
  211.             du_zhu += 0.5
  212.     except:
  213.         pass
  214.     try:
  215.         if (x,y + list_slice_end - 4) in white_coordinate_list or y + list_slice_end - 4 > 19:
  216.             du_zhu += 0.5
  217.     except:
  218.         pass
  219.     total_chance += chess_list_n2.count("●") * chess_list_n2.count("●")
  220.     total_chance += chess_list_n1.count("●")
  221.     if chess_list_n2.count("●") == 3 and du_zhu <= 1:
  222.         total_chance += 99999
  223.     if chess_list_n2.count("●") == 4 and du_zhu <= 1.5:
  224.         total_chance += 99999
  225.     if chess_list_n2.count("●") == 5 and du_zhu <= 2:
  226.         total_chance += 114514
  227.    
  228.     try:  
  229.         total_chance /= du_zhu
  230.     except:
  231.         pass
  232.     text_tp += "chess_list_first:" + str(chess_list_first) + "\n"
  233.     text_tp += "chess_list_n1:" + str(chess_list_n1) + "\n"
  234.     text_tp += "chess_list_n2:" + str(chess_list_n2) + "\n"
  235.    
  236.     # --------------------------------------------------------------------------------
  237.     text_tp += "斜向下方向:" + "\n"
  238.     # 斜向下方向,堵住黑子的个数
  239.     # 先获得该坐标附近9点的状态
  240.     chess_list_first = []
  241.     for i in range(-4,5):
  242.         if i == 0:
  243.             chess_list_first.append("◉")
  244.         elif x + i < 1 or x + i > 19 or y + i < 1 or y + i > 19:
  245.             chess_list_first.append("■")
  246.         elif (x + i, y + i) in black_coordinate_list:
  247.             chess_list_first.append("●")
  248.         elif (x + i, y + i) in white_coordinate_list:
  249.             chess_list_first.append("◌")
  250.         else:
  251.             chess_list_first.append("▫")
  252.     # 列表n1:排除从中间开始,已被白子挡住的黑子
  253.     list_slice_head = 0
  254.     list_slice_end = 9
  255.     for i in range (1,5):
  256.         if chess_list_first[4 - i] == "◌" or chess_list_first[4 - i] == "■":
  257.             if list_slice_head == 0:
  258.                 list_slice_head = 5 - i
  259.         if chess_list_first[4 + i] == "◌" or chess_list_first[4 + i] == "■":
  260.             if list_slice_end == 9:
  261.                 list_slice_end = 4 + i
  262.     chess_list_n1 = chess_list_first[list_slice_head:list_slice_end]
  263.     # 列表n2:从中间开始,与当前坐标紧紧相连的黑子
  264.     list_slice_head = 0
  265.     list_slice_end = 9
  266.     for i in range (1,5):
  267.         if chess_list_first[4 - i] == "▫" or chess_list_first[4 - i] == "◌":
  268.             if list_slice_head == 0:
  269.                 list_slice_head = 5 - i
  270.         if chess_list_first[4 + i] == "▫" or chess_list_first[4 + i] == "◌":
  271.             if list_slice_end == 9:
  272.                 list_slice_end = 4 + i
  273.     chess_list_n2 = chess_list_first[list_slice_head:list_slice_end]
  274.     du_zhu = 1
  275.     try:
  276.         if (x + list_slice_head - 5,y + list_slice_head - 5) in white_coordinate_list or x + list_slice_head - 5 < 1 or y + list_slice_head - 5 < 1:
  277.             du_zhu += 0.5
  278.     except:
  279.         pass
  280.     try:
  281.         if (x + list_slice_end - 4,y + list_slice_end - 4) in white_coordinate_list or x + list_slice_end - 4 > 19 or y + list_slice_end - 4 > 19:
  282.             du_zhu += 0.5
  283.     except:
  284.         pass
  285.     total_chance += chess_list_n2.count("●") * chess_list_n2.count("●")
  286.     total_chance += chess_list_n1.count("●")
  287.     if chess_list_n2.count("●") == 3 and du_zhu <= 1:
  288.         total_chance += 99999
  289.     if chess_list_n2.count("●") == 4 and du_zhu <= 1.5:
  290.         total_chance += 99999
  291.     if chess_list_n2.count("●") == 5 and du_zhu <= 2:
  292.         total_chance += 114514
  293.    
  294.     try:  
  295.         total_chance /= du_zhu
  296.     except:
  297.         pass
  298.     text_tp += "chess_list_first:" + str(chess_list_first) + "\n"
  299.     text_tp += "chess_list_n1:" + str(chess_list_n1) + "\n"
  300.     text_tp += "chess_list_n2:" + str(chess_list_n2) + "\n"
  301.     # --------------------------------------------------------------------------------
  302.     text_tp += "斜向上方向:" + "\n"
  303.     # 斜向上方向,堵住黑子的个数
  304.     # 先获得该坐标附近9点的状态
  305.     chess_list_first = []
  306.     for i in range(-4,5):
  307.         if i == 0:
  308.             chess_list_first.append("◉")
  309.         elif x + i < 1 or x + i > 19 or y - i < 1 or y - i > 19:
  310.             chess_list_first.append("■")
  311.         elif (x + i, y - i) in black_coordinate_list:
  312.             chess_list_first.append("●")
  313.         elif (x + i, y - i) in white_coordinate_list:
  314.             chess_list_first.append("◌")
  315.         else:
  316.             chess_list_first.append("▫")
  317.     # 列表n1:排除从中间开始,已被白子挡住的黑子
  318.     list_slice_head = 0
  319.     list_slice_end = 9
  320.     for i in range (1,5):
  321.         if chess_list_first[4 - i] == "◌" or chess_list_first[4 - i] == "■":
  322.             if list_slice_head == 0:
  323.                 list_slice_head = 5 - i
  324.         if chess_list_first[4 + i] == "◌" or chess_list_first[4 + i] == "■":
  325.             if list_slice_end == 9:
  326.                 list_slice_end = 4 + i
  327.     chess_list_n1 = chess_list_first[list_slice_head:list_slice_end]
  328.     # 列表n2:从中间开始,与当前坐标紧紧相连的黑子
  329.     list_slice_head = 0
  330.     list_slice_end = 9
  331.     for i in range (1,5):
  332.         if chess_list_first[4 - i] == "▫" or chess_list_first[4 - i] == "◌":
  333.             if list_slice_head == 0:
  334.                 list_slice_head = 5 - i
  335.         if chess_list_first[4 + i] == "▫" or chess_list_first[4 + i] == "◌":
  336.             if list_slice_end == 9:
  337.                 list_slice_end = 4 + i
  338.     chess_list_n2 = chess_list_first[list_slice_head:list_slice_end]
  339.     du_zhu = 1
  340.     try:
  341.         if (x + list_slice_head - 5,y - list_slice_head + 5) in white_coordinate_list or x + list_slice_head - 5 < 1 or y - list_slice_head + 5 < 1:
  342.             du_zhu += 0.5
  343.     except:
  344.         pass
  345.     try:
  346.         if (x + list_slice_end - 4,y - list_slice_end + 4) in white_coordinate_list or x + list_slice_end - 4 > 19 or y - list_slice_end + 4 > 19:
  347.             du_zhu += 0.5
  348.     except:
  349.         pass
  350.     total_chance += chess_list_n2.count("●") * chess_list_n2.count("●")
  351.     total_chance += chess_list_n1.count("●")
  352.     if chess_list_n2.count("●") == 3 and du_zhu <= 1:
  353.         total_chance += 99999
  354.     if chess_list_n2.count("●") == 4 and du_zhu <= 1.5:
  355.         total_chance += 99999
  356.     if chess_list_n2.count("●") == 5 and du_zhu <= 2:
  357.         total_chance += 114514
  358.    
  359.     try:  
  360.         total_chance /= du_zhu
  361.     except:
  362.         pass
  363.     text_tp += "chess_list_first:" + str(chess_list_first) + "\n"
  364.     text_tp += "chess_list_n1:" + str(chess_list_n1) + "\n"
  365.     text_tp += "chess_list_n2:" + str(chess_list_n2) + "\n"
  366.     # --------------------------------------------------------------------------------
  367.     if (x,y) in black_coordinate_list or (x,y) in white_coordinate_list:
  368.         total_chance = -1
  369.     return total_chance
  370. # 初始化
  371. pygame.init()
  372. part_add = 0
  373. delta_list_add = 1
  374. black_coordinate_list = []
  375. white_coordinate_list = []
  376. tips = " "
  377. game_mode = "start"
  378. window = pygame.display.set_mode((1000, 762))
  379. font_30 = pygame.font.Font("C:\\Windows\\Fonts\\simhei.ttf", 30)
  380. draw_chessboard()
  381. pygame.display.flip()
  382. side = "black"
  383. # 主循环
  384. while True:
  385.     for event in pygame.event.get():
  386.         # 检测退出
  387.         if event.type == pygame.QUIT:
  388.             pygame.quit()
  389.             sys.exit()
  390.         # 按空格重开
  391.         if event.type == pygame.KEYDOWN:
  392.             if event.key == pygame.K_SPACE:
  393.                 black_coordinate_list = []
  394.                 white_coordinate_list = []
  395.                 tips = " "
  396.                 game_mode = "start"
  397.                 draw_chessboard()
  398.                 pygame.display.flip()
  399.                 side = "black"
  400.         # 鼠标按下后
  401.         if event.type == pygame.MOUSEBUTTONDOWN:
  402.             if game_mode == "end":
  403.                 black_coordinate_list = []
  404.                 white_coordinate_list = []
  405.                 tips = " "
  406.                 game_mode = "start"
  407.                 draw_chessboard()
  408.                 pygame.display.flip()
  409.                 side = "black"
  410.             chessboard_coordinate = mouse_coordinate_to_chessboard_coordinate(event.pos[0], event.pos[1])
  411.             if not chessboard_coordinate == "out of range":
  412.                 if chessboard_coordinate not in black_coordinate_list and chessboard_coordinate not in white_coordinate_list:
  413.                     place_chess(chessboard_coordinate, side)
  414.                     total_chance = 0
  415.                     chance_xy = [(10, 10)]
  416.                     text_tp = ""
  417.                     text_tp += "edge_■ empty_▫ black_● white_◌\n"
  418.                     # 逐一计算每个坐标的概率
  419.                     for x in range(1, 20):
  420.                         for y in range(1, 20):
  421.                             text_tp += "--------------------------------------------------\n坐标:(" + str(x) + "," + str(y) + ")\n"
  422.                             self_chance = the_chance_to_win(x, y)
  423.                             text_tp += "总共\nchance:" + str(self_chance) + "\n"
  424.                             if self_chance > total_chance:
  425.                                 total_chance = self_chance
  426.                                 chance_xy = [(x, y)]
  427.                             elif self_chance == total_chance:
  428.                                 chance_xy.append((x, y))
  429.                     # 下面这行启用后,会输出7000行多的内容,感兴趣的可自行查看(可能会非常卡)
  430.                     # print(text_tp)
  431.                     last_chance_xy = random.choice(chance_xy)
  432.                     place_chess(last_chance_xy, "white")
  433.                     side = "black"
  434.                     if check_chess() == "black":
  435.                         tips = "黑棋获胜"
  436.                         game_mode = "end"
  437.                     elif check_chess() == "white":
  438.                         tips = "白棋获胜"
  439.                         game_mode = "end"
  440.                     draw_chessboard()
  441.                     chessboard_refresh()
  442.                     pygame.display.flip()
  443.                     
  444. '''
  445. Gobang by TRIM
  446. V 1.2.0
  447. '''
复制代码



JOVI  中级技师

发表于 2023-12-14 10:32:12

学习学习
回复

使用道具 举报

aYYSW8AepLLd  高级技师

发表于 2023-12-16 13:32:28

厉害厉害
回复

使用道具 举报

影神  见习技师

发表于 2024-2-25 20:17:01

很棒,但它虽然会堵住三颗连子,但它无法优势最大化,当它连成四颗时,还是会去堵对方的三颗子,建议加一条如果AI连成四颗AI便先连AI的。if AI == 4:(换行)(缩进)AI = 5,类似这样,但我不是很懂
回复

使用道具 举报

TRIM  初级技匠
 楼主|

发表于 2024-2-26 18:50:41

影神 发表于 2024-2-25 20:17
很棒,但它虽然会堵住三颗连子,但它无法优势最大化,当它连成四颗时,还是会去堵对方的三颗子,建议加一条 ...

好的,谢谢!目前只做了防守,但自己进攻还没做到(需要它自己权衡利弊,有点难),但应该快搞好了
回复

使用道具 举报

伦**  初级技师

发表于 2024-2-27 17:56:13


厉害厉害                                                                                                                                                                          
回复

使用道具 举报

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

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail