Stubborn 发表于 2021-2-16 21:45:30

Python五子棋,第一版(弱智电脑)

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 = Computer or Player
            ::data -> 五子棋的数据储存
            ::one_spot -> 待搜索的点
                所有已落子的八个方向,延伸5个点位。不包括已落子和超出边界的子

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

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

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

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

    """
    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 < SIZE and -1 < p < 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 -> 落点的坐标
      :return: 落点的数据 '0'=空白 '1' or '2' 已经落子
      """
      if -1 < key < SIZE and -1 < key < SIZE:
            return self.data
      return ""

    def __setitem__(self, key: Point, value: str):
      """
      :param key: Set(int, int) -> 落点的坐标
      :param value: 落点的数据1 or 2 玩家或者电脑AI
      :return: None
      """
      self.data = value

    def __contains__(self, key):
      """
      :param key: List -> 落点的坐标
      :return: bool ->判断当前点是否可以落子
      """
      return self == "0"

    def _result(self, p: Point) -> List:
      """
      :param p: 五子棋落点, 拼接效果:x->x->p->x->x
      :return:四个方向的棋型, List
      """
      def count(x, y): return + x, p + y]

      def func(iterator):
            return "".join( for e in iterator])
      return + self + func(v2) for v1, v2 in VECTOR]

    def get_xy_score(self, spot: Point, flag: str) -> int:
      """
      :param spot:
      :param flag:身份标识
      :return:四个方向的棋型的评分列表
      """
      self = flag
      chess_type = self._result(spot)
      self = "0"
      _flag =

      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(, reverse=True)
      return ChessType.score(scores)

    def update_one(self, p: Point) -> Set:
      """
      获取落点周围八个方位总计40个点,排除超出边界的点
      :param p: 落点点
      :return: set -> ((x, y),(x1, y1)...)
      """
      self.one_spot.discard(p)
      # TODO 对p点计算位移点。
      def count(x, y): return p + x, p + y
      # TODO 对VECTOR展开,转化一维列表,通过count函数计算位移后的点
      vector =
      r = set(filter(self.f, vector))
      return r

    def _evaluation(self, computer_scores, player_scores):
      """对相同收益进行比较
            例如对手出现活三,我们有两个点可以堵截,有一个点既能堵截,又能对我们形成收益。
      """
      score, move = player_scores
      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:
      """
      对当前棋盘局面进行评估
      :return: 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, reverse=True)
      player_scores = sorted(player_scores, key=lambda p: p)
      # TODO 如果电脑收益大于玩家,直接返回电脑得分点
      if computer_scores > abs(player_scores):
            return computer_scores
      # TODO 如果电脑收益小于于玩家,进行收益进行比较
      res = self._evaluation(computer_scores, player_scores)
      if res:
            return res
      # TODO 返回玩家收益点
      return player_scores

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 = 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+"" + 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], self.yingshe]
                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] = COMPUTER
                bb = self.update_one(r)
                self.one_spot |= bb
                next_move = PLAYER
                self.computer_order.append(r)


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轴
    ,
    # TODO Y轴
    ,
    # TODO 左斜轴
    ,
    # TODO 右斜轴
    ,
]
# 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 得分:棋型List
      #如果有新的棋型,在此添加,范围仅限,单活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是一个落子的四个方向的棋型得分
      """
      value = scores + scores
      # 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

ArouArou 发表于 2021-2-17 08:47:51

顶楼!{:10_256:}

hrp 发表于 2021-2-17 09:23:05

学习

crisfo 发表于 2021-2-17 10:03:59

厉害{:5_106:}
页: [1]
查看完整版本: Python五子棋,第一版(弱智电脑)