|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
Python五子棋
version:1.0
- 功能介绍:基本实现五子棋功能,判定棋型,给予得分,因为评分机制,导致防御还可以,进攻就和弱智差不多了
- Model.py>>>主要实现的基本功能都在这里
- checkerboard.py>>>和界面UI的交互,待下一版实现
- cmd_board>>>测试用,cmd的打印界面
- setting.py>>>全局变量设置的保存
version:1.1准备实现alpha_beta剪枝搜索,有大佬会的吗?
- # -*- coding: utf-8 -*-
- # !/usr/bin/python3
- """
- @ version: ??
- @ author: Alex
- @ file: Model
- @ datetime: 2021/02/09 - 14:08
- @ explain:
- """
- from setting import SIZE, ChessType, COMPUTER, PLAYER, VECTOR
- from CoreCode.tools import LogHandler
- from typing import List, Set, Tuple
- from itertools import chain
- # TODO =============== 数据类型声明 =============
- # 坐标着点 x
- X: int = int()
- # 坐标着点 y
- Y: int = int()
- # 坐标
- Point: tuple = (X, Y)
- class GoBangBase:
- """棋盘数据和得分规则管理
- GoBangData实现
- 对落子的存储,采用的hash字典存储 self[x,y] = Computer or Player
- ::data -> 五子棋的数据储存
- ::one_spot -> 待搜索的点
- 所有已落子的八个方向,延伸5个点位。不包括已落子和超出边界的子
- def _result(self, p) -> List[str]:
- 获取 p 点四个方向(xy轴及左右斜轴)的落子数据,拼接成字符串返回
- def get_xy_score(self, spot:List[int], flag: str) -> int:
- 获取 spot 落点的具体评分
- def update_one(self, p: List[int]) -> Set[tuple]:
- 获得p落点周围八个方位总计40个点,排除已落子和超出边界的点
- def evaluation(self) -> Tuple[int, Point]:
- 对当前局面进行评价,获得敌我双方最大的收益点。
- """
- data = dict().fromkeys(range(SIZE * SIZE), "0")
- one_spot: set = set()
- vector = chain.from_iterable(list(chain.from_iterable(VECTOR)))
- @staticmethod
- def key(x: X, y: Y): return x * SIZE + y
- # TODO 筛选超出边界的点
- def f(self, p: Point):
- """
- 对着点的筛选,A:没有超出边界. B:没有进行落子. C:不在待搜索的列表
- :param p:
- :return:
- """
- if -1 < p[0] < SIZE and -1 < p[1] < SIZE:
- if p in self and p not in self.one_spot:
- return True
- return False
- def __getitem__(self, key: Point) -> str:
- """
- :param key: List[int, int] -> [x, y] 落点的坐标
- :return: 落点的数据 '0'=空白 '1' or '2' 已经落子
- """
- if -1 < key[0] < SIZE and -1 < key[1] < SIZE:
- return self.data[self.key(*key)]
- return ""
- def __setitem__(self, key: Point, value: str):
- """
- :param key: Set(int, int) -> [x, y] 落点的坐标
- :param value: 落点的数据 1 or 2 玩家或者电脑AI
- :return: None
- """
- self.data[self.key(*key)] = value
- def __contains__(self, key):
- """
- :param key: List[int, int] -> [x, y] 落点的坐标
- :return: bool ->判断当前点是否可以落子
- """
- return self[key] == "0"
- def _result(self, p: Point) -> List[str]:
- """
- :param p: 五子棋落点, 拼接效果:x->x->p->x->x
- :return:四个方向的棋型, List[str]
- """
- def count(x, y): return [p[0] + x, p[1] + y]
- def func(iterator):
- return "".join([self[count(*e)] for e in iterator])
- return [func(v1)[::-1] + self[p] + func(v2) for v1, v2 in VECTOR]
- def get_xy_score(self, spot: Point, flag: str) -> int:
- """
- :param spot:
- :param flag:身份标识
- :return:四个方向的棋型的评分列表
- """
- self[spot] = flag
- chess_type = self._result(spot)
- self[spot] = "0"
- _flag = [COMPUTER, PLAYER][flag == COMPUTER]
- def get(chess):
- for score, rules in ChessType.ScoreRules.items():
- for rule in rules:
- if rule.format(flag, _flag) in chess:
- # TODO 玩家评分修正,玩家活三,单四
- if flag == PLAYER:
- if score == ChessType.LIVE_THREE:
- score = ChessType.PLAYER_LIVE_THREE
- if score == ChessType.RUSH_FOUR:
- score = ChessType.PLAYER_RUSH_FOUR
- return score
- return 0.5
- scores = sorted([get(chess) for chess in chess_type], reverse=True)
- return ChessType.score(scores)
- def update_one(self, p: Point) -> Set[Tuple]:
- """
- 获取落点周围八个方位总计40个点,排除超出边界的点
- :param p: 落点点
- :return: set[tuple] -> ((x, y),(x1, y1)...)
- """
- self.one_spot.discard(p)
- # TODO 对p点计算位移点。
- def count(x, y): return p[0] + x, p[1] + y
- # TODO 对VECTOR展开,转化一维列表,通过count函数计算位移后的点
- vector = [count(*each) for each in chain.from_iterable(list(chain.from_iterable(VECTOR)))]
- r = set(filter(self.f, vector))
- return r
- def _evaluation(self, computer_scores, player_scores):
- """对相同收益进行比较
- 例如对手出现活三,我们有两个点可以堵截,有一个点既能堵截,又能对我们形成收益。
- """
- score, move = player_scores[0]
- moves = []
- for sco, spot in player_scores[:8]:
- if sco == score:
- moves.append(spot)
- for sco, spot in computer_scores[:8]:
- if spot in moves:
- return sco, spot
- return False
- def evaluation(self) -> Tuple[int, tuple]:
- """
- 对当前棋盘局面进行评估
- :return: Tuple[int, tuple] -> (score, (x, y))
- """
- # TODO 遍历待搜索的着点,对双方进行评分.返回双方评分收益大的一个点
- # TODO 遍历得道电脑和玩家落子最大收益
- computer_scores = [(self.get_xy_score(spot, COMPUTER), spot) for spot in self.one_spot]
- player_scores = [(-self.get_xy_score(spot, PLAYER), spot) for spot in self.one_spot]
- # TODO 排序获得最大收益点
- computer_scores = sorted(computer_scores, key=lambda p: p[0], reverse=True)
- player_scores = sorted(player_scores, key=lambda p: p[0])
- # TODO 如果电脑收益大于玩家,直接返回电脑得分点
- if computer_scores[0][0] > abs(player_scores[0][0]):
- return computer_scores[0]
- # TODO 如果电脑收益小于于玩家,进行收益进行比较
- res = self._evaluation(computer_scores, player_scores)
- if res:
- return res
- # TODO 返回玩家收益点
- return player_scores[0]
复制代码
- from CoreCode import GoBangBase
- from setting import COMPUTER
- class Board(GoBangBase):
- """
- 棋盘管理:
- computer_order --> 电脑落子记录
- player_order --> 电脑落子记录
- Method
- """
- computer_order: list = list()
- player_order: list = list()
- def modify(self, spot: list, identity: str) -> None:
- self[spot] = identity
- self.computer_order.append(spot) if identity == COMPUTER else self.player_order.append(spot)
- @staticmethod
- def game_over():
- return False
- def ai_move(self):
- try:
- result = self.evaluation()
- except IndexError:
- return 0, (8, 8)
- return result
复制代码
- # -*- coding: utf-8 -*-
- # !/usr/bin/python3
- """
- @ version: ??
- @ author: Alex
- @ file: cmd_board
- @ datetime: 2021/02/09 - 14:08
- @ explain:
- """
- from CoreCode import Board
- from setting import SIZE, COMPUTER, PLAYER
- from CoreCode.tools import LogHandler
- log = LogHandler("borad", file=True)
- class StrBoard(Board):
- yingshe = {
- "0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,
- "9":9,"a":10,"b":11,"c":12,"d":13
- }
- def print_board(self):
- str1 = "0123456789abcde"
- r = ''
- for x in range(SIZE):
- s = ""
- for y in range(SIZE):
- s += " " + self[(x, y)]
- r += str1[x]+" " + s + "\n"
- str1 ="x/y " + " ".join(str1)
- print(r + str1)
- def main(self):
- next_move = COMPUTER
- while True:
- self.print_board()
- if next_move == PLAYER:
- r = input("请输入你要落子的位置(0-d):0d or 91>>>>")
- x,y = self.yingshe[r[0]], self.yingshe[r[1]]
- self[(x, y)] = PLAYER
- bb = self.update_one((x, y))
- self.one_spot |= bb
- next_move = COMPUTER
- self.player_order.append((x, y))
- else:
- r = self.ai_move()
- print(f"运行结果:{r}\n")
- self[r[1]] = COMPUTER
- bb = self.update_one(r[1])
- self.one_spot |= bb
- next_move = PLAYER
- self.computer_order.append(r[1])
- if __name__ == '__main__':
- s = StrBoard()
- try:
- s.main()
- except KeyError:
- log.warning(f"\n{s.player_order}\n{s.computer_order}\n")
复制代码
- # -*- coding: utf-8 -*-
- # !/usr/bin/python3
- """
- @ version: ??
- @ author: Alex
- @ file: setting
- @ datetime: 2020/10/17 - 21:08
- @ explain:
- """
- # TODO 棋盘大小
- SIZE = 15
- # TODO VECTOR 向量,
- f = lambda x, y: '[(0 {} v, 0 {} v) for v in range(1, 6)]'.format(x, y)
- VECTOR = [
- # TODO X轴
- [eval(f("-", "*")), eval(f("+", "*"))],
- # TODO Y轴
- [eval(f("*", "+")), eval(f("*", "-"))],
- # TODO 左斜轴
- [eval(f("-", "+")), eval(f("+", "-"))],
- # TODO 右斜轴
- [eval(f("-", "-")), eval(f("+", "+"))],
- ]
- # TODO 身份标识符,电脑落子代表1 玩家落子代表2
- COMPUTER = "1"
- PLAYER = "2"
- # TODO 分支8,搜索深度 5,博弈树节点
- BRANCH = 8
- DEPTH = 3
- class ChessType:
- """
- (1) 如果有一个方向已经成5连, 其分值记为5000;
- (2) 未出现 (1) , 有一个方向已经成活4, 其分值记为1000;
- (3) 未出现 (1) 、 (2) , 有两个方向出现单4, 其分值记为1000;
- (4) 未出现 (1) 、 (2) 、 (3) , 有两个方向出现单4和活3, 其分值记为998;
- (5) 未出现 (1) 、 (2) 、 (3) 、 (4) , 有两个方向成活3, 其分值记为954;
- (6) 未出现上述情况, 将4个方向中单子分值最大的2个值相加作为其分值。
- 己方单4 73
- 对方单4 84
- 己方活3 67
- 对方活3 79
- 单3 16
- 活2 13
- 单2 7
- 活1 3
- 单1 1
- """
- ScoreRules = {
- # TODO 得分[int]:棋型List[str]
- # 如果有新的棋型,在此添加,范围仅限,单活1-4棋型
- # TODO 眠一型
- 1: ["0{0}{1}", "{1}{0}0"],
- # TODO 活一型
- 3: ["0{0}0"],
- # TODO 眠二型
- 7: ["0{0}{0}{1}", "0{0}0{0}{1}", "0{0}00{0}{1}", "{0}000{0}"],
- # TODO 活二型
- 13: ["0{0}{0}0", "0{0}0{0}0", "{0}00{0}", "0{0}{0}0"],
- # TODO 眠三型
- 16: [
- "0{0}{0}{0}{1}", "{1}{0}{0}{0}0", "0{0}0{0}{0}{1}",
- "{1}{0}0{0}{0}0", "0{0}{0}0{0}{1}", "{1}{0}{0}0{0}0",
- "{1}0{0}{0}{0}0{1}", "{0}00{0}{0}", "{0}0{0}0{0}"
- ],
- # TODO 活三型
- 67: ["0{0}{0}{0}0", "0{0}0{0}{0}0", "0{0}{0}0{0}0"],
- # TODO 眠四型
- 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"],
- # TODO 活四型
- 1000: ["0{0}{0}{0}{0}0"],
- # TODO 连五型
- 5000: ["{0}{0}{0}{0}{0}"]
- }
- LIVE_THREE: int = 67
- RUSH_FOUR: int = 73
- # TODO 对方活3及单四评分
- PLAYER_LIVE_THREE: int = 79
- PLAYER_RUSH_FOUR: int = 84
- # TODO 异常棋型得分,双活三,单四活三,双单四,
- DOUBLE_LIVE_THREE: int = 954
- RUSH_FOUR_LIVE_THREE: int = 998
- DOUBLE_RUSH_FOUR: int = 1000
- @staticmethod
- def score(scores) -> int:
- """返回最终评分
- 如果有新的异常棋型得分,在此修改。
- scores->List[int]是一个落子的四个方向的棋型得分
- """
- value = scores[0] + scores[1]
- # TODO 连五棋型 5000分
- if value >= 5000:
- return 5000
- # TODO 双冲四棋型 1000分
- if value >= ChessType.RUSH_FOUR * 2:
- return ChessType.DOUBLE_RUSH_FOUR
- # TODO 活四+活三棋型 998分
- elif value >= ChessType.RUSH_FOUR + ChessType.LIVE_THREE:
- return ChessType.RUSH_FOUR_LIVE_THREE
- # TODO 双活三棋型 954分
- elif value >= ChessType.LIVE_THREE * 2:
- return ChessType.DOUBLE_LIVE_THREE
- # TODO 以上都不符
- return value
复制代码 |
评分
-
查看全部评分
|