鱼C论坛

 找回密码
 立即注册
查看: 903|回复: 0

[技术交流] 五子棋后端代码,评分代码

[复制链接]
发表于 2021-12-7 16:26:16 | 显示全部楼层 |阅读模式

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

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

x
本帖最后由 Stubborn 于 2021-12-7 21:02 编辑

我已经演示了如何使用这两段代码
  • 其中a列表是是当前局面的着点记录,每个数字代表的是 int = x + y * 15  。
  • 其中规定位于棋盘的左上角(0, 0),右上角位(14, 0),左下角(0, 14),右下角(14, 14)
  • 返回的结果里面,是List[Tuple[int,int]] , 元组的第二个元素,表示一个着点,第一个元素,表示当前着点的评分。
  • 该返回的结果也有对手的最优着点,是双方着点的,经过评估分排序后集合,对手的最优着点也是我们的最优着点。
  • 如果你只想做一个初级版的五子棋游戏,那么你可以拿这评分代码直接OK了。如果想继续优化,则选择博弈树扩展搜索最优路径
  • 请注意,这里的都评分,是一个点的评分,比如 AAXAA ,X点可以落子形成连五棋型,那么这个点的返回应该是这样(5000, X) ,即落X着点可以获得5000评分
  • 如果你觉得最后采集的敌我双方评分点不合适,可以自己更换逻辑

# -*- coding: utf-8 -*-
# !/usr/bin/python3
"""
@ version: ??
@ author: Alex
@ file: Chess
@ datetime: 2021/12/1 - 21:00
@ explain:
"""
from typing import List

from settings import VECTOR5, VECTOR3, BLACK, WHITE, ChessType, BRANCH


class CheckTheChessPattern(object):

    @staticmethod
    def _get_xy_score(dat: str, num: int):
        """
        辅助函数,返回当前着点的棋形
        """
        result = []
        for v1, v2 in VECTOR5:
            v1e = ""
            for e in v1:
                if 0 <= e + num <= 224:
                    v1e = dat[e + num] + v1e
            v1e = v1e + dat[num]
            for e in v2:
                if 0 <= e + num <= 224:
                    v1e += dat[e + num]
            result.append(v1e)
        return result

    @staticmethod
    def _update_spots(spots: List[int], dat: str):
        result = set()
        for idx, spot in enumerate(spots):
            # TODO 跟新数据
            value = WHITE if idx % 2 else BLACK
            dat = dat[:spot] + value + dat[spot + 1:]

            for p in VECTOR3:
                # TODO 如果点没有超出边界,则加入待搜索的点
                if 0 <= spot + p <= 225:
                    result.add(spot + p)
        for spot in spots:
            result.discard(spot)

        return result, dat

    @staticmethod
    def get_xy_score(spot: int, flag: str, dat: str, hostility=False) -> int:
        """
        :param spot: 一个着点, int := x + y * 15
        :param flag: 着点身份, ’1‘ or '2'
        :return:  ChessType.score(scores) -> int 当前着点参数落点评估分数
        TODO 尝试落子后,返回该点周围八个方向的五格拼接为四个方向的数据(横轴,竖轴,左斜轴,右斜轴)
            拿到棋形后(例如00011A22200,A是spot着点),在到棋形数据进行比对,如果棋形出现在其中,给与对应分数评估。
            取两个方向的最大值评估分和值作为返回值
        """
        dat = dat[:spot] + dat[spot] + dat[spot + 1:]

        # TODO 获取棋形
        chess_type = CheckTheChessPattern._get_xy_score(dat=dat, num=spot)

        _flag = BLACK if flag == WHITE else WHITE

        def get(chess):
            for score, rules in ChessType.ScoreRules.items():
                for rule in rules:
                    if rule.format(flag, _flag) in chess:
                        # TODO 如果是敌对方,进行修正
                        if hostility:
                            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)

    @staticmethod
    def evaluation(spots: List[int]):
        """返回当前局面最优的BRANCH个着点"""
        # TODO 获得待搜索的点
        dat = '0' * 225
        waiting_for_search_points, dat = CheckTheChessPattern._update_spots(spots=spots, dat=dat)
        # TODO 获得当前身份与对手身份
        our = WHITE if len(spots) % 2 else BLACK
        enemy = BLACK if our == WHITE else WHITE
        # TODO 获得敌我双方所有点的估值
        our_scores = [(CheckTheChessPattern.get_xy_score(spot=spot, flag=our, dat=dat), spot) for spot in
                      waiting_for_search_points]

        enemy_scores = [(CheckTheChessPattern.get_xy_score(spot=spot, flag=enemy, dat=dat, hostility=True), spot) for
                        spot in
                        waiting_for_search_points]
        # TODO 获得敌我双方所有点的估值 排序 获得最大收益点
        socre = our_scores + enemy_scores
        socre = sorted(socre, key=lambda p: p[0], reverse=True)

        return socre[:BRANCH]


if __name__ == '__main__':
    a = [112, 41, 96, 203, 128, 192, 80, 3]
    print(CheckTheChessPattern.evaluation(a))

# -*- coding: utf-8 -*-
# !/usr/bin/python3
"""
@ version: ??
@ author: Alex
@ file: setting
@ datetime: 2021/12/1 - 21:00
@ explain:
"""

# TODO 黑棋子
BLACK = '1'
# TODO 白棋子
WHITE = '2'
# TODO 分支8,从当前棋盘内搜索8个最优点
BRANCH = 10

VECTOR5 = [
    [[-1, -2, -3, -4, -5], [1, 2, 3, 4, 5]],
    [[15, 30, 45, 60, 75], [-15, -30, -45, -60, -75]],
    [[14, 28, 42, 56, 70], [-14, -28, -42, -56, -70]],
    [[-16, -32, -48, -64, -80], [16, 32, 48, 64, 80]]
]

VECTOR3 = [-48, -45, -42, -32, -30, -28, -16, -15, -14, -3, -2, -1, 1, 2, 3, 14, 15, 16, 28, 30, 32, 42, 45, 48]


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  连五型
        5000: ["{0}{0}{0}{0}{0}"],
        #  TODO  活四型
        1000: ["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  活三型
        67: ["0{0}{0}{0}0", "0{0}0{0}{0}0", "0{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  活二型
        13: ["0{0}{0}0", "0{0}0{0}0", "{0}00{0}", "0{0}{0}0"],
        #  TODO  眠二型
        7: ["0{0}{0}{1}", "0{0}0{0}{1}", "0{0}00{0}{1}", "{0}000{0}"],
        #  TODO  活一型
        3: ["0{0}0"],
        #  TODO  眠一型
        1: ["0{0}{1}", "{1}{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

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-1-12 17:43

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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