鱼C论坛

 找回密码
 立即注册
查看: 2674|回复: 3

[技术交流] Python五子棋,第一版(弱智电脑)

[复制链接]
发表于 2021-2-16 21:45:30 | 显示全部楼层 |阅读模式

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

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

x
Python五子棋


version:1.0
  • 功能介绍:基本实现五子棋功能,判定棋型,给予得分,因为评分机制,导致防御还可以,进攻就和弱智差不多了
  • Model.py>>>主要实现的基本功能都在这里
  • checkerboard.py>>>和界面UI的交互,待下一版实现
  • cmd_board>>>测试用,cmd的打印界面
  • setting.py>>>全局变量设置的保存


version:1.1准备实现alpha_beta剪枝搜索,有大佬会的吗?


  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/python3
  3. """
  4. @ version: ??
  5. @ author: Alex
  6. @ file: Model
  7. @ datetime: 2021/02/09 - 14:08
  8. @ explain:
  9. """
  10. from setting import SIZE, ChessType, COMPUTER, PLAYER, VECTOR
  11. from CoreCode.tools import LogHandler

  12. from typing import List, Set, Tuple
  13. from itertools import chain

  14. # TODO =============== 数据类型声明 =============
  15. # 坐标着点 x
  16. X: int = int()
  17. # 坐标着点 y
  18. Y: int = int()
  19. # 坐标
  20. Point: tuple = (X, Y)


  21. class GoBangBase:
  22.     """棋盘数据和得分规则管理
  23.         GoBangData实现
  24.                 对落子的存储,采用的hash字典存储 self[x,y] = Computer or Player
  25.             ::data -> 五子棋的数据储存
  26.             ::one_spot -> 待搜索的点
  27.                 所有已落子的八个方向,延伸5个点位。不包括已落子和超出边界的子

  28.             def _result(self, p) -> List[str]:
  29.                 获取 p 点四个方向(xy轴及左右斜轴)的落子数据,拼接成字符串返回

  30.             def get_xy_score(self, spot:List[int], flag: str) -> int:
  31.                 获取 spot 落点的具体评分

  32.             def update_one(self, p: List[int]) -> Set[tuple]:
  33.                 获得p落点周围八个方位总计40个点,排除已落子和超出边界的点

  34.             def evaluation(self) -> Tuple[int, Point]:
  35.                 对当前局面进行评价,获得敌我双方最大的收益点。

  36.     """
  37.     data = dict().fromkeys(range(SIZE * SIZE), "0")
  38.     one_spot: set = set()
  39.     vector = chain.from_iterable(list(chain.from_iterable(VECTOR)))

  40.     @staticmethod
  41.     def key(x: X, y: Y): return x * SIZE + y

  42.     # TODO 筛选超出边界的点
  43.     def f(self, p: Point):
  44.         """
  45.         对着点的筛选,A:没有超出边界. B:没有进行落子. C:不在待搜索的列表
  46.         :param p:
  47.         :return:
  48.         """
  49.         if -1 < p[0] < SIZE and -1 < p[1] < SIZE:
  50.             if p in self and p not in self.one_spot:
  51.                 return True
  52.         return False

  53.     def __getitem__(self, key: Point) -> str:
  54.         """
  55.         :param key: List[int, int] -> [x, y]  落点的坐标
  56.         :return: 落点的数据 '0'=空白 '1' or '2' 已经落子
  57.         """
  58.         if -1 < key[0] < SIZE and -1 < key[1] < SIZE:
  59.             return self.data[self.key(*key)]
  60.         return ""

  61.     def __setitem__(self, key: Point, value: str):
  62.         """
  63.         :param key: Set(int, int) -> [x, y]  落点的坐标
  64.         :param value: 落点的数据  1 or 2 玩家或者电脑AI
  65.         :return: None
  66.         """
  67.         self.data[self.key(*key)] = value

  68.     def __contains__(self, key):
  69.         """
  70.         :param key: List[int, int] -> [x, y]  落点的坐标
  71.         :return: bool ->判断当前点是否可以落子
  72.         """
  73.         return self[key] == "0"

  74.     def _result(self, p: Point) -> List[str]:
  75.         """
  76.         :param p: 五子棋落点, 拼接效果:x->x->p->x->x
  77.         :return:四个方向的棋型, List[str]
  78.         """
  79.         def count(x, y): return [p[0] + x, p[1] + y]

  80.         def func(iterator):
  81.             return "".join([self[count(*e)] for e in iterator])
  82.         return [func(v1)[::-1] + self[p] + func(v2) for v1, v2 in VECTOR]

  83.     def get_xy_score(self, spot: Point, flag: str) -> int:
  84.         """
  85.         :param spot:
  86.         :param flag:身份标识
  87.         :return:四个方向的棋型的评分列表
  88.         """
  89.         self[spot] = flag
  90.         chess_type = self._result(spot)
  91.         self[spot] = "0"
  92.         _flag = [COMPUTER, PLAYER][flag == COMPUTER]

  93.         def get(chess):
  94.             for score, rules in ChessType.ScoreRules.items():
  95.                 for rule in rules:
  96.                     if rule.format(flag, _flag) in chess:
  97.                         # TODO 玩家评分修正,玩家活三,单四
  98.                         if flag == PLAYER:
  99.                             if score == ChessType.LIVE_THREE:
  100.                                 score = ChessType.PLAYER_LIVE_THREE
  101.                             if score == ChessType.RUSH_FOUR:
  102.                                 score = ChessType.PLAYER_RUSH_FOUR
  103.                         return score
  104.             return 0.5
  105.         scores = sorted([get(chess) for chess in chess_type], reverse=True)
  106.         return ChessType.score(scores)

  107.     def update_one(self, p: Point) -> Set[Tuple]:
  108.         """
  109.         获取落点周围八个方位总计40个点,排除超出边界的点
  110.         :param p: 落点点
  111.         :return: set[tuple] -> ((x, y),(x1, y1)...)
  112.         """
  113.         self.one_spot.discard(p)
  114.         # TODO 对p点计算位移点。
  115.         def count(x, y): return p[0] + x, p[1] + y
  116.         # TODO 对VECTOR展开,转化一维列表,通过count函数计算位移后的点
  117.         vector = [count(*each) for each in chain.from_iterable(list(chain.from_iterable(VECTOR)))]
  118.         r = set(filter(self.f, vector))
  119.         return r

  120.     def _evaluation(self, computer_scores, player_scores):
  121.         """对相同收益进行比较
  122.             例如对手出现活三,我们有两个点可以堵截,有一个点既能堵截,又能对我们形成收益。
  123.         """
  124.         score, move = player_scores[0]
  125.         moves = []
  126.         for sco, spot in player_scores[:8]:
  127.             if sco == score:
  128.                 moves.append(spot)
  129.         for sco, spot in computer_scores[:8]:
  130.             if spot in moves:
  131.                 return sco, spot
  132.         return False

  133.     def evaluation(self) -> Tuple[int, tuple]:
  134.         """
  135.         对当前棋盘局面进行评估
  136.         :return: Tuple[int, tuple] -> (score, (x, y))
  137.         """
  138.         # TODO 遍历待搜索的着点,对双方进行评分.返回双方评分收益大的一个点
  139.         # TODO 遍历得道电脑和玩家落子最大收益
  140.         computer_scores = [(self.get_xy_score(spot, COMPUTER), spot) for spot in self.one_spot]
  141.         player_scores = [(-self.get_xy_score(spot, PLAYER), spot) for spot in self.one_spot]
  142.         # TODO 排序获得最大收益点
  143.         computer_scores = sorted(computer_scores, key=lambda p: p[0], reverse=True)
  144.         player_scores = sorted(player_scores, key=lambda p: p[0])
  145.         # TODO 如果电脑收益大于玩家,直接返回电脑得分点
  146.         if computer_scores[0][0] > abs(player_scores[0][0]):
  147.             return computer_scores[0]
  148.         # TODO 如果电脑收益小于于玩家,进行收益进行比较
  149.         res = self._evaluation(computer_scores, player_scores)
  150.         if res:
  151.             return res
  152.         # TODO 返回玩家收益点
  153.         return player_scores[0]
复制代码

  1. from CoreCode import GoBangBase
  2. from setting import COMPUTER


  3. class Board(GoBangBase):
  4.     """
  5.     棋盘管理:
  6.         computer_order -->  电脑落子记录
  7.         player_order   -->  电脑落子记录
  8.     Method

  9.     """
  10.     computer_order: list = list()
  11.     player_order: list = list()

  12.     def modify(self, spot: list, identity: str) -> None:
  13.         self[spot] = identity
  14.         self.computer_order.append(spot) if identity == COMPUTER else self.player_order.append(spot)

  15.     @staticmethod
  16.     def game_over():
  17.         return False

  18.     def ai_move(self):
  19.         try:
  20.             result = self.evaluation()
  21.         except IndexError:
  22.             return 0, (8, 8)
  23.         return result
复制代码


  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/python3
  3. """
  4. @ version: ??
  5. @ author: Alex
  6. @ file: cmd_board
  7. @ datetime: 2021/02/09 - 14:08
  8. @ explain:
  9. """
  10. from CoreCode import Board
  11. from setting import SIZE, COMPUTER, PLAYER
  12. from CoreCode.tools import LogHandler
  13. log = LogHandler("borad", file=True)


  14. class StrBoard(Board):

  15.     yingshe = {
  16.         "0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,
  17.         "9":9,"a":10,"b":11,"c":12,"d":13
  18.     }

  19.     def print_board(self):
  20.         str1 = "0123456789abcde"
  21.         r = ''
  22.         for x in range(SIZE):
  23.             s = ""
  24.             for y in range(SIZE):
  25.                 s += " " + self[(x, y)]
  26.             r += str1[x]+"  " + s + "\n"
  27.         str1 ="x/y " + " ".join(str1)
  28.         print(r + str1)

  29.     def main(self):
  30.         next_move = COMPUTER
  31.         while True:
  32.             self.print_board()
  33.             if next_move == PLAYER:
  34.                 r = input("请输入你要落子的位置(0-d):0d or 91>>>>")
  35.                 x,y = self.yingshe[r[0]], self.yingshe[r[1]]
  36.                 self[(x, y)] = PLAYER
  37.                 bb = self.update_one((x, y))
  38.                 self.one_spot |= bb
  39.                 next_move = COMPUTER
  40.                 self.player_order.append((x, y))
  41.             else:
  42.                 r = self.ai_move()
  43.                 print(f"运行结果:{r}\n")
  44.                 self[r[1]] = COMPUTER
  45.                 bb = self.update_one(r[1])
  46.                 self.one_spot |= bb
  47.                 next_move = PLAYER
  48.                 self.computer_order.append(r[1])


  49. if __name__ == '__main__':
  50.     s = StrBoard()
  51.     try:
  52.         s.main()
  53.     except KeyError:
  54.         log.warning(f"\n{s.player_order}\n{s.computer_order}\n")
复制代码

  1. # -*- coding: utf-8 -*-
  2. # !/usr/bin/python3
  3. """
  4. @ version: ??
  5. @ author: Alex
  6. @ file: setting
  7. @ datetime: 2020/10/17 - 21:08
  8. @ explain:
  9. """
  10. # TODO 棋盘大小
  11. SIZE = 15

  12. # TODO VECTOR 向量,
  13. f = lambda x, y: '[(0 {} v, 0 {} v) for v in range(1, 6)]'.format(x, y)
  14. VECTOR = [
  15.     # TODO X轴
  16.     [eval(f("-", "*")), eval(f("+", "*"))],
  17.     # TODO Y轴
  18.     [eval(f("*", "+")), eval(f("*", "-"))],
  19.     # TODO 左斜轴
  20.     [eval(f("-", "+")), eval(f("+", "-"))],
  21.     # TODO 右斜轴
  22.     [eval(f("-", "-")), eval(f("+", "+"))],
  23. ]
  24. # TODO 身份标识符,电脑落子代表1 玩家落子代表2
  25. COMPUTER = "1"
  26. PLAYER = "2"
  27. # TODO 分支8,搜索深度 5,博弈树节点
  28. BRANCH = 8
  29. DEPTH = 3


  30. class ChessType:
  31.     """
  32.     (1) 如果有一个方向已经成5连, 其分值记为5000;
  33.     (2) 未出现 (1) , 有一个方向已经成活4, 其分值记为1000;
  34.     (3) 未出现 (1) 、 (2) , 有两个方向出现单4, 其分值记为1000;
  35.     (4) 未出现 (1) 、 (2) 、 (3) , 有两个方向出现单4和活3, 其分值记为998;
  36.     (5) 未出现 (1) 、 (2) 、 (3) 、 (4) , 有两个方向成活3, 其分值记为954;
  37.     (6) 未出现上述情况, 将4个方向中单子分值最大的2个值相加作为其分值。
  38.     己方单4   73
  39.     对方单4   84
  40.     己方活3   67
  41.     对方活3   79
  42.     单3       16
  43.     活2       13
  44.     单2       7
  45.     活1       3
  46.     单1       1
  47.     """
  48.     ScoreRules = {
  49.         # TODO 得分[int]:棋型List[str]
  50.         #  如果有新的棋型,在此添加,范围仅限,单活1-4棋型
  51.         #  TODO  眠一型
  52.         1: ["0{0}{1}", "{1}{0}0"],
  53.         #  TODO  活一型
  54.         3: ["0{0}0"],
  55.         #  TODO  眠二型
  56.         7: ["0{0}{0}{1}", "0{0}0{0}{1}", "0{0}00{0}{1}", "{0}000{0}"],
  57.         #  TODO  活二型
  58.         13: ["0{0}{0}0", "0{0}0{0}0", "{0}00{0}", "0{0}{0}0"],
  59.         #  TODO  眠三型
  60.         16: [
  61.             "0{0}{0}{0}{1}", "{1}{0}{0}{0}0", "0{0}0{0}{0}{1}",
  62.             "{1}{0}0{0}{0}0", "0{0}{0}0{0}{1}", "{1}{0}{0}0{0}0",
  63.             "{1}0{0}{0}{0}0{1}", "{0}00{0}{0}", "{0}0{0}0{0}"
  64.         ],
  65.         #  TODO  活三型
  66.         67: ["0{0}{0}{0}0", "0{0}0{0}{0}0", "0{0}{0}0{0}0"],
  67.         #  TODO  眠四型
  68.         73: ["0{0}{0}{0}{0}{1}", "{0}0{0}{0}{0}", "{0}{0}0{0}{0}", "{0}{0}{0}0{0}", "{1}{0}{0}{0}{0}0"],
  69.         #  TODO  活四型
  70.         1000: ["0{0}{0}{0}{0}0"],
  71.         #  TODO  连五型
  72.         5000: ["{0}{0}{0}{0}{0}"]
  73.     }
  74.     LIVE_THREE: int = 67
  75.     RUSH_FOUR: int = 73
  76.     # TODO 对方活3及单四评分
  77.     PLAYER_LIVE_THREE: int = 79
  78.     PLAYER_RUSH_FOUR: int = 84
  79.     # TODO 异常棋型得分,双活三,单四活三,双单四,
  80.     DOUBLE_LIVE_THREE: int = 954
  81.     RUSH_FOUR_LIVE_THREE: int = 998
  82.     DOUBLE_RUSH_FOUR: int = 1000

  83.     @staticmethod
  84.     def score(scores) -> int:
  85.         """返回最终评分
  86.             如果有新的异常棋型得分,在此修改。
  87.             scores->List[int]是一个落子的四个方向的棋型得分
  88.         """
  89.         value = scores[0] + scores[1]
  90.         # TODO 连五棋型 5000分
  91.         if value >= 5000:
  92.             return 5000
  93.         # TODO 双冲四棋型 1000分
  94.         if value >= ChessType.RUSH_FOUR * 2:
  95.             return ChessType.DOUBLE_RUSH_FOUR
  96.         # TODO 活四+活三棋型 998分
  97.         elif value >= ChessType.RUSH_FOUR + ChessType.LIVE_THREE:
  98.             return ChessType.RUSH_FOUR_LIVE_THREE
  99.         # TODO 双活三棋型 954分
  100.         elif value >= ChessType.LIVE_THREE * 2:
  101.             return ChessType.DOUBLE_LIVE_THREE
  102.         # TODO 以上都不符
  103.         return value
复制代码

评分

参与人数 3荣誉 +13 鱼币 +8 贡献 +6 收起 理由
hrp + 3 + 3
ArouArou + 5 + 5 + 3 厉害了 老哥!
昨非 + 5 + 3 鱼C有你更精彩^_^

查看全部评分

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2021-2-17 08:47:51 | 显示全部楼层
顶楼!
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2021-2-17 09:23:05 From FishC Mobile | 显示全部楼层
学习
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2021-2-17 10:03:59 | 显示全部楼层
厉害
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-6-27 21:08

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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