鱼C论坛

 找回密码
 立即注册
查看: 2111|回复: 2

[技术交流] 井字棋

[复制链接]
发表于 2022-8-5 16:42:43 | 显示全部楼层 |阅读模式

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

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

x
  1. from copy import deepcopy

  2. class Board:
  3.     def __init__(self):
  4.         self.state = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
  5.         self.flag = 0

  6.     def __str__(self):
  7.         new_lst = deepcopy(self.state)
  8.         for i in range(len(new_lst)):
  9.             for j in range(len(new_lst[i])):
  10.                 if new_lst[i][j] == 0:
  11.                     new_lst[i][j] = "-"
  12.                 elif new_lst[i][j] == 1:
  13.                     new_lst[i][j] = "X"
  14.                 else:
  15.                     new_lst[i][j] = "O"
  16.         string = "{:^5}|{:^5}|{:^5}\n{:_^5}|{:_^5}|{:_^5}\n{:^5}|{:^5}|{:^5}\n{:^5}|{:^5}|{:^5}\n{:_^5}|{:_^5}|{:_^5}\n{:^5}|{:^5}|{:^5}\n{:^5}|{:^5}|{:^5}\n{:^5}|{:^5}|{:^5}\n"
  17.         return string.format(*new_lst[0], "_", "_", "_", " ", " ", " ", *new_lst[1], "_", "_", "_", " ", " ", " ", *new_lst[2], " ", " ", " ")

  18.     def reset(self):
  19.         self.state = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
  20.         self.flag = 0  # 采用法一的时候必须将flag置为0

  21.     @property
  22.     def is_valid(self):
  23.         count_X = 0
  24.         count_O = 0
  25.         if len(self.state) != 3:
  26.             return False
  27.         for line in self.state:
  28.             if len(line) != 3:
  29.                 return False
  30.             if not(isinstance(line[0], int) and isinstance(line[1], int) and isinstance(line[2], int) and line[0] in [0, 1, 2] and line[1] in [0, 1, 2] and line[2] in [0, 1, 2]):
  31.                 return False
  32.             for each in line:
  33.                 if each == 1:
  34.                     count_X += 1
  35.                 elif each == 2:
  36.                     count_O += 1
  37.         if abs(count_X - count_O) > 1:
  38.             return False
  39.         return True

  40.     @property
  41.     def round(self):
  42.         count = 0
  43.         for line in self.state:
  44.             for each in line:
  45.                 if each != 0:
  46.                     count += 1

  47.         return count

  48.     def __getitem__(self, key):
  49.         return self.state[key.row][key.col]

  50.     def __setitem__(self, key, value):
  51.         if self.state[key.row][key.col] != 0:
  52.             raise ValueError
  53.         # 交替添加就叫一致或者连续
  54.         # 法一
  55.         if value == 1 and self.flag != -1:
  56.             self.state[key.row][key.col] = value
  57.             self.flag = -1
  58.         elif value == 2 and self.flag != 1:
  59.             self.state[key.row][key.col] = value
  60.             self.flag = 1
  61.         else:
  62.             raise ValueError
  63.         # 法二
  64.         # 如果在添加之后,棋盘不合法,则valueerror
  65.          

  66.     @property
  67.     def winner(self):
  68.         # 横竖
  69.         for i in range(3):
  70.             if self.state[i][0] == self.state[i][1] == self.state[i][2] and self.state[i][0] != 0:
  71.                 return self.state[i][0]
  72.             elif self.state[0][i] == self.state[1][i] == self.state[2][i] and self.state[0][i] != 0:
  73.                 return self.state[0][i]
  74.         # 对角线
  75.         if self.state[0][0] == self.state[1][1] == self.state[2][2] and self.state[0][0] != 0:
  76.             return self.state[0][0]
  77.         # 副对角线
  78.         elif self.state[0][2] == self.state[1][1] == self.state[2][0] and self.state[0][2] != 0:
  79.             return self.state[0][2]
  80.         return 0

  81.     @property
  82.     def is_finished(self):
  83.         if self.winner == True or self.round == 9:
  84.             return True
  85.         
  86.             

  87. class Position:
  88.     def __init__(self, row, col):
  89.         if row in [0, 1, 2] and col in [0, 1, 2]:
  90.             self.row = row
  91.             self.col = col
  92.         else:
  93.             raise ValueError

  94. import random
  95. # 井字棋核心算法
  96. class Agent:
  97.     def make_move(self, board):
  98.         iswin = self.get_win_or_lost(board, 1)
  99.         # 如果自己能赢直接赢
  100.         if iswin != None:
  101.             return iswin
  102.         islost = self.get_win_or_lost(board, 2)
  103.         # 如果对方能赢则堵住
  104.         if islost != None:
  105.             return islost
  106.         # 预判一下自己能否必胜
  107.         will_win = self.I_will_win_or_lost(board, 1)
  108.         if will_win != None:
  109.             return will_win
  110.         # 预判一下自己是否必输
  111.         will_lost = self.I_will_win_or_lost(board, 2)
  112.         if will_lost != None:
  113.             return will_lost
  114.         # 如果中间位置没有棋子则下在中间,因为放在中间赢得概率最大
  115.         if board[Position(1, 1)] == 0:
  116.             return Position(1, 1)
  117.         if board.round == 1:
  118.             # 当别人先手时,如果中间位置被占,那么就在(0, 0)下子
  119.             if board[Position(1, 1)] != 0:
  120.                 return Position(0, 0)
  121.         # 其他情况随机放子,基本不影响赢的概率
  122.         row = random.randint(0, 2)
  123.         col = random.randint(0, 2)
  124.         while board[Position(row, col)] != 0:
  125.             row = random.randint(0, 2)
  126.             col = random.randint(0, 2)
  127.         return Position(row, col)

  128.     # 去试一试自己要赢还是要输
  129.     def get_win_or_lost(self, board, player):
  130.         empty_position = []
  131.         for i in range(3):
  132.             for j in range(3):
  133.                 if board[Position(i, j)] == 0:
  134.                     empty_position.append(Position(i, j))
  135.         for each in empty_position:
  136.             board.state[each.row][each.col] = player
  137.             if board.winner == player:
  138.                 board.state[each.row][each.col] = 0
  139.                 return each
  140.             board.state[each.row][each.col] = 0

  141.     def I_will_win_or_lost(self, board, player):
  142.         empty_position = []
  143.         for i in range(3):
  144.             for j in range(3):
  145.                 if board[Position(i, j)] == 0:
  146.                     empty_position.append(Position(i, j))
  147.         for each in empty_position:
  148.             board.state[each.row][each.col] = player
  149.             if self.get_win_lost_count(board, player) > 1:
  150.                 board.state[each.row][each.col] = 0
  151.                 return each
  152.             board.state[each.row][each.col] = 0

  153.     def get_win_lost_count(self, board, player):
  154.         empty_position = []
  155.         for i in range(3):
  156.             for j in range(3):
  157.                 if board[Position(i, j)] == 0:
  158.                     empty_position.append(Position(i, j))
  159.         count = 0
  160.         for each in empty_position:
  161.             board.state[each.row][each.col] = player
  162.             if board.winner == player:
  163.                 board.state[each.row][each.col] = 0
  164.                 count += 1
  165.             board.state[each.row][each.col] = 0
  166.         return count



  167. count_a = 0
  168. count_b = 0

  169. agent1 = Agent()
  170. # agent2 = Agent()

  171. # 完全随机下棋的玩家
  172. class agent_random:
  173.     def make_move(self, board):
  174.         row = random.randint(0, 2)
  175.         col = random.randint(0, 2)
  176.         while board[Position(row, col)] != 0:
  177.             row = random.randint(0, 2)
  178.             col = random.randint(0, 2)
  179.         return Position(row, col)

  180. agent2 = agent_random()

  181. board = Board()
  182. # 先手一定不会输
  183. # for i in range(5000):
  184. #     while board.winner == 0:
  185. #         board[agent1.make_move(board)] = 1
  186. #         if board.winner != 0:
  187. #             # print("agent1 win")
  188. #             # print(board)
  189. #             count_a += 1
  190. #             break
  191. #         if board.is_finished:
  192. #             break
  193. #         board[agent2.make_move(board)] = 2
  194. #         if board.winner != 0:
  195. #             print("agent2 win")
  196. #             print(board)
  197. #             count_b += 1
  198. #             break
  199. #         if board.is_finished:
  200. #             break
  201. #     board.reset()
  202. #     board.flag = 0
  203. #     # print("Finished %s times" % (i+1))

  204. # 后手获胜概率很大
  205. for j in range(5000):
  206.     while board.winner == 0:
  207.         board[agent2.make_move(board)] = 1
  208.         if board.winner != 0:
  209.             print("agent2 win")
  210.             print(board)
  211.             count_b += 1
  212.             break
  213.         if board.is_finished:
  214.             break
  215.         board[agent1.make_move(board)] = 2
  216.         if board.winner != 0:
  217.             # print("agent1 win")
  218.             # print(board)
  219.             count_a += 1
  220.             break
  221.         if board.is_finished:
  222.             break
  223.     board.reset()
  224.     board.flag = 0

  225. print(count_a, count_b)
复制代码

代码太臃肿了
时间复杂度还是比较高的
不过好在还是可以跑起来
测试了5000次
先手的话应该是必胜
后手获胜概率也很大
记录一下
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2022-8-5 17:34:56 | 显示全部楼层
好厉害的样子,good
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-5 18:12:15 | 显示全部楼层
人机对战,人类输入想要下子的位置,比如输入0 0,代表在左上角下子,其他情况类似
  1. board = Board()

  2. agent = Agent()
  3. # 机器先手
  4. # while True:
  5. #     board[agent.make_move(board)] = 1
  6. #     print(board)
  7. #     if board.winner != 0:
  8. #         break
  9. #     if board.is_finished:
  10. #         break
  11. #     while True:
  12. #         try:
  13. #             board[Position(*[int(each) for each in input("Please input the position : ").split()])] = 2
  14. #             break
  15. #         except:
  16. #             pass
  17. #     print(board)
  18. #     if board.winner != 0:
  19. #         break
  20. #     if board.is_finished:
  21. #         break
  22. # board.reset()
  23. # board.flag = 0

  24. # 玩家先手,有必胜的策略
  25. # 玩家先放(0, 0),机器会放在中间,玩家放在(2, 2),机器会放在(0, 2),玩家放在(2, 0),人类获胜
  26. while True:
  27.     while True:
  28.         try:
  29.             board[Position(*[int(each) for each in input("Please input the position : ").split()])] = 2
  30.             break
  31.         except:
  32.             pass
  33.    
  34.     print(board)
  35.     if board.winner != 0:
  36.         break
  37.     if board.is_finished:
  38.         break
  39.     board[agent.make_move(board)] = 1
  40.     print(board)
  41.     if board.winner != 0:
  42.         break
  43.     if board.is_finished:
  44.         break
  45. board.reset()
  46. board.flag = 0
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-4-27 18:21

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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