Tic-Tac-Toe有多少种不同棋局和盘面状态(python实现)

news2024/10/6 4:09:43

目录

1. 前言

2. 如何去重?

3. 代码实现

3.1 对称等价判断

3.2 find_neighbor()改造

3.3 主程序及运行结果

4. 延申思考


1. 前言

        在前两篇博客中实现了遍历搜索所有的Tic-Tac-Toe的棋局的python程序实现。

Tic-Tac-Toe可能棋局搜索的实现(python)_笨牛慢耕的博客-CSDN博客Tic-Tac-Toe中文常译作井字棋,即在3 x 3的棋盘上,双方轮流落子,先将3枚棋子连成一线的一方获得胜利。Tic-Tac-Toe变化简单,可能的局面和棋局数都很有限(相比中国象棋、日本象棋、围棋等来说连九牛一毛都不到!具体有多少可能的局面以及可能的棋局数,本系列完成以后就可以给出答案了),因此常成为和搜寻的教学例子,同时也是的一道好题目。本系列考虑实现一个Tic-Tac-Toe AI,以由浅入深循序渐进的方式来逐步完成这个实现。https://blog.csdn.net/chenxy_bwave/article/details/128506352Tic-Tac-Toe可能棋局遍历的实现(python)_笨牛慢耕的博客-CSDN博客在上一篇博客中实现了搜索Tic-Tac-Toe游戏的某个棋局的python程序。接下来的问题是,在Tic-Tac-Toe游戏中总共有多少种可能的棋局呢?注意,棋局是指在两个player交替下棋直到终局的过程中所导致的棋盘状态变化的序列。所以,即便所包含的棋盘状态集合完全相同,但是如果棋盘状态出现的顺序不同的话,也是不同的棋局。本文在上一篇的基础上进一步实现搜索Tic-Tac-Toe游戏的所有可能棋局的实现。https://blog.csdn.net/chenxy_bwave/article/details/128513299

        根据上一篇的实现(搜索Tic-Tac-Toe所有可能棋局),结果表明有255168种棋局,有5478种盘面状态。但是,这个结果对吗?

        严格地来说是不对的。

        因为以上实现没有考虑Tic-Tac-Toe游戏的棋盘的对称性。举个例子说,第一手下在四个角上的任意一个角上本质上都是一样的。考虑了对称性所导致的重复后,总的可能棋局数和盘面状态数会大幅度减小。

        棋盘的对称性包含以下4种对称:

  1. 旋转对称
  2. 对角线对称
  3. 上下对称
  4. 左右对称

        以上各种对称的示例如下图所示:

 

2. 如何去重?

        如何将对称性考虑进去进行去重(repetition removal)处理以得到真正的不同棋局数和盘面状态数的结果呢?

        本文给出的思路是:在棋局遍历过程计算某个节点的所有邻接节点时,追加去重判断,将满足以上所述各种对称性的状态视为相同的状态,只保留其中一个并剔除其余的。这样最后遍历的结果就代表本质上不相同的棋局。以下举两个例子。

        例1:从初始棋盘状态,player1下第一手棋。

         虽然,player应该可以在9个位置中的任意一处落子,但是考虑到对称性等价后,其实只剩下了3种可能。图中标为红色“X”标志的图即为由于对称等价而排除的局面。

        例2:player1落子后,player2下棋的可能位置

 

       最后在基于棋局统计所有不同盘面状态时同样要进行去重处理,剔除重复的,最后得到的才是所有可能的不同棋盘状态。

3. 代码实现

3.1 对称等价判断

        对称等价判断实现如下所示。这里采用的是最直接朴素的实现方式。应该有更高效的实现方案。

def is_rot_symmetry(s1,s2):
    rot90_symm = \
        s1[0] == s2[6] and s1[1] == s2[3] and s1[2] == s2[0] and \
        s1[3] == s2[7] and s1[4] == s2[4] and s1[5] == s2[1] and \
        s1[6] == s2[8] and s1[7] == s2[5] and s1[8] == s2[2]

    rot180_symm = \
        s1[0] == s2[8] and s1[1] == s2[7] and s1[2] == s2[6] and \
        s1[3] == s2[5] and s1[4] == s2[4] and s1[5] == s2[3] and \
        s1[6] == s2[2] and s1[7] == s2[1] and s1[8] == s2[0]

    rot270_symm = \
        s1[0] == s2[2] and s1[1] == s2[5] and s1[2] == s2[8] and \
        s1[3] == s2[1] and s1[4] == s2[4] and s1[5] == s2[7] and \
        s1[6] == s2[0] and s1[7] == s2[3] and s1[8] == s2[6]
        
    return rot90_symm or rot180_symm or rot270_symm
    
    
def is_diagonal_symmetry(s1,s2):
    symm1 = \
        s1[0] == s2[0] and s1[4] == s2[4] and s1[8] == s2[8] and \
        s1[1] == s2[3] and s1[3] == s2[1] and \
        s1[2] == s2[6] and s1[6] == s2[2] and \
        s1[5] == s2[7] and s1[7] == s2[5]

    symm2 = \
        s1[2] == s2[2] and s1[4] == s2[4] and s1[6] == s2[6] and \
        s1[1] == s2[5] and s1[5] == s2[1] and \
        s1[0] == s2[8] and s1[8] == s2[0] and \
        s1[3] == s2[7] and s1[7] == s2[3]
    
    return symm1 or symm2
    
def is_updown_symmetry(s1,s2):
    return \
        s1[0] == s2[6] and s1[1] == s2[7] and s1[2] == s2[8] and \
        s1[3] == s2[3] and s1[4] == s2[4] and s1[5] == s2[5] and \
        s1[6] == s2[0] and s1[7] == s2[1] and s1[8] == s2[2] 
    
def is_leftright_symmetry(s1,s2):
    return \
        s1[0] == s2[2] and s1[3] == s2[5] and s1[6] == s2[8] and \
        s1[1] == s2[1] and s1[4] == s2[4] and s1[7] == s2[7] and \
        s1[2] == s2[0] and s1[5] == s2[3] and s1[8] == s2[6] 
    
def is_symmetry(s1,s2):
    return is_rot_symmetry(s1,s2) or       \
            is_diagonal_symmetry(s1,s2) or \
            is_updown_symmetry(s1,s2) or   \
            is_leftright_symmetry(s1,s2)

3.2 find_neighbor()改造

        在原来的find_neighbor()的基础上追加对称等价判断以去除重复盘面状态,得到代码如下:

def find_neighbor(s):
    neighbor_list = []
    # decides whose turn
    if s.count(1) == s.count(2):
        turn = 1
    elif s.count(1) == s.count(2) + 1:
        turn = 2
    else:
        print('Invalid input state: ', s)
        return None
    
    for k in range(len(s)):
        if s[k] == 0:
            s_next = list(s)
            s_next[k] = turn
            neighbor_list.append(tuple(s_next))

        
    neighbor_list2 = []
    
    for k in range(0,len(neighbor_list)):
        is_unique = True
        for s in neighbor_list2:
            if is_symmetry(neighbor_list[k], s):
                is_unique = False
                break
        if is_unique:
            neighbor_list2.append(neighbor_list[k])            
            
    return neighbor_list2

3.3 主程序及运行结果

        其它函数没有修改(请参考上一篇)。

        主程序中最后统计所有可能的盘面状态时同样追加基于对称判断的去重处理。代码如下:

if __name__ == '__main__':

    # Initialization
    s0     = tuple([0] * 9)
    path   = [s0]
    tStart = time.time()
    dfs(path)
    tStop  = time.time()
    
    state_set = set() # set() remove the repeated items automatically
    for k in range(len(path_list)):
        path = path_list[k]
        
        for s in path:
            state_set.add(s)
                 
    # Counting the different board status, with symmetrically repeated ones
    # states = state_set
    states = []
    for s1 in state_set:
        is_unique = True
        for s2 in states:
            if is_symmetry(s1, s2):
                is_unique = False
                break
        if is_unique:
            states.append(s1)                        
    
    print('Totally there are {0} games'.format(len(path_list)))
    print('Totally there are {0} board states'.format(len(states)))
    print('Time cost:  {0:6.2f} seconds'.format(tStop-tStart))

        运行结果如下:

Totally there are 26830 games
Totally there are 765 board states
Time cost:    0.34 seconds

        也就是说,只有26830种不同的棋局,而不同的盘面状态则只有765种!

4. 延申思考

        然后不用计算仿真,而是以手动计算的方式计算出以上结果来呢?

        应该是个有难度的组合计数问题。。。

        接下来将考虑:(1) 人机交互Tic-Tac-Toe基本对弈程序的实现 (2)  基于minimax算法以及aplha-beta pruning算法的Tic-Tac-Toe AI的实现。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/134480.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java寒假打卡】Java基础-多态

【Java寒假打卡】Java基础-多态概述多态中成员访问的特点多态的好处和弊端多态中转型多态中转型存在的风险概述 同一个对象在不同时刻表现出来的不同形态 多态的前提和体现 有继承/实现关系有方法重写。子类对父类进行方法重写有父类引用指向子类对象 package com.hfut.edu.…

【阶段二】Python数据分析NumPy工具使用02篇:数组的基本属性与数组的数据获取

本篇的思维导图: 数组的基本属性 NumPy数组的基本属性主要包括数组的形状、大小、类型和维数。 描述 代码 结果

Zookeeper详解(一)——基础介绍

概念 zookeeper官网:https://zookeeper.apache.org/ 大数据生态系统里的很多组件的命名都是某种动物或者昆虫,比如hadoop就是 🐘,hive就是🐝。zookeeper即动物园管理者,顾名思义就是管理大数据生态系统各…

Linux 系统调用的本质

简单概念 fd #include <unistd.h> #include <string.h>int main(int argc,char* argv[]) {char buf[20]{0};read(0,buf,15);write(1,buf,strlen(buf));return 0; }如果想查看某个系统编程的接口&#xff0c;比如想查看 open 函数的用法&#xff0c;可以这样操作…

Python调用C++代码用法——Linux

目录 前言 C/C动态共享库编译 ctype模块 ctype数据类型 使用案例 float数据 指针 结构体及结构体指针 numpy图像当作指针传入 参考资料&#xff1a; 前言 在项目开发中&#xff0c;有时会使用到多种编程语言&#xff0c;比如部分功能是C/C代码实现的&#xff0c;而另一…

《机器学习与应用》实验二:BP神经网络实验

文章目录 一、实验目的二、实验原理BP算法的数学描述三、程序四、实验结论一、实验目的 1、 熟悉MATLAB中神经网络工具箱的使用方法; 2、 通过在MATLAB下面编程实现BP网络逼近标准正弦函数,来加深对BP网络的了解和认识,理解信号的正向传播和误差的反向传递过程。 二、实验…

SAP MM物料与客户主数据的税分类

一&#xff0e;说明 在物料主数据、客户主数据中均有税分类的维护&#xff0c;税分类既不是税码也不代表税率&#xff0c;它们的作用是通过税务条件记录确定税码。所有的税分类在主数据中都是与国家相关的无组织机构数据&#xff0c;例如物料的销售组织有中国&#xff08;ZH&am…

智慧WMS立体仓库管理系统源码 基于springboot框架(已经测试完整带部署搭建教程)源码分享!

淘源码&#xff1a;国内知名的高品质源码免费下载平台 分享一套智慧WMS立体仓库管理系统源码&#xff0c;基于springboot框架 已经测试完整带部署搭建教程。&#xff08;MF00767&#xff09; 需要源码学习可私信我获取。 技术架构 技术框架&#xff1a;SpringBoot layui H…

ESLint插件的使用

官网地址 规范写代码的工具. 多人开发不同规则,提交代码一堆冲突 培养代码风格使用 vscode更改tab缩进空格数----设置—搜索tabsize—找到tab size—修改2(每次按下tab都缩进俩空格)—Vetur > Format Options: Tab Size这个也要修改为2 vscode搜索format----勾选Editor: Fo…

javaweb01--mysql的介绍和增删改查操作

文章目录Mysql的介绍和增删改查说明1. mysql的登陆和退出11 登陆1.2 退出2. SQL语法的简单介绍2.1 语法2.2 SQL分类3. SQL主要操作语句3.1 DDL:操作数据库3.1.1 查询3.1.2 创建数据库3.1.3 删除数据库3.1.4 使用数据库3.2 DDL:操作表3.2.1 查询表3.2.2 创建表3.2.3 数据类型3.2…

《梁启超家书》笔记二——一个人若是在舒服的环境中会消磨志气,那么在困苦懊丧的环境中也一定会消磨志气

目录 一、做事的态度 二、学习与未来 三、发挥其个性之特长&#xff0c;以靖献于社会 四、鼓励相信孩子 五、犯错 六、身体健康 七、做事 八、与费用相关 九、在困苦中求快活 十、让孩子自由决策与建议 十一、处事态度&#xff1a;不要悲观 十二、时事分析 一、做事…

在IDEA中获取文件绝对路径(通用方式)

package com.javase.reflect;/*** 关于文件路径问题* 以前我们都是在IDEA中&#xff0c;依据IDEA默认的当前路径&#xff1a;project的根来获取文件&#xff0c;但是这种方法有它的局限性&#xff0c;那就是当代码离开了IDEA* 换到了其他位置&#xff0c;我们就找不到文…

基于 Tensorflow 2.x 使用 MobileNetV2 微调模型优化训练花卉图像识别模型

一、模型微调 上篇文章我们通过搭建三层卷积模型&#xff0c;训练了花卉图像识别模型&#xff0c;最后经验证集验证后准确率大约为 75% &#xff0c;本篇文章对该数据集进行优化&#xff0c;提高识别的准确度。本篇文章中对于数据集的读取强化不做过多的介绍了&#xff0c;大家…

免拆机,Kindle固件版本5.10.3~5.13.3如何越狱?简单、易操作版

前言 之前有出过Kindle的越狱教程&#xff1a; 无需拆机&#xff0c;Kindle 全系列 5.12.2.2 ~ 5.14.2版本如何越狱&#xff1f;如何安装第三方插件 确实可以越狱&#xff0c;使用的漏洞也是&#xff1a; KindleDrip — From Your Kindle’s Email Address to Using Your C…

ubuntu18.04下mysql数据库C语言API封装

mysql C语言API操作数据库比较繁琐&#xff0c;可以将其封装起来&#xff0c;这样使用比较方便&#xff0c;下面是一种封装方式。 目录 1.连接封装 2.连接池封装 3.测试代码 1.连接封装 将数据库连接进行封装&#xff0c;主要提供如下接口&#xff1a; &#xff08;1&…

L2-030 冰岛人

2018年世界杯&#xff0c;冰岛队因1:1平了强大的阿根廷队而一战成名。好事者发现冰岛人的名字后面似乎都有个“松”&#xff08;son&#xff09;&#xff0c;于是有网友科普如下&#xff1a; 冰岛人沿用的是维京人古老的父系姓制&#xff0c;孩子的姓等于父亲的名加后缀&#x…

torchnet.meter使用教程

前言 最近项目开发过程中遇到了torchnet.metertorchnet.metertorchnet.meter来记录模型信息&#xff0c;搜了好多篇博客潦潦草草&#xff0c;没有一点干货&#xff0c;于是根据官方代码和官方文档&#xff0c;基于自己的理解&#xff0c;制定了使用教程: torchnet简介 torch…

一句话实现报表生成PDF同时通过outlook发送

元旦节快乐 哈喽&#xff0c;大家2023年好呀&#xff01; 今天&#xff0c;元旦最后一天&#xff0c;给大家分享什么好玩的示例呢&#xff1f; 让我来想想&#xff0c;嗯&#xff1f;这样可以吗&#xff1f;一句话就实现将报表生成PDF&#xff0c;同时可以编辑一些信息并通过…

【源码分享】java多用户B2B2C商城源码带WAP手机端源码

分享一款非常不错的java多用户B2B2C商城源码&#xff0c;带WAP手机端源码&#xff0c;源码地址在文末。 需要源码学习&#xff0c;可私信我获取。 一、技术构架&#xff1a; 开发语言&#xff1a; Java1.7 数 据 库 &#xff1a; MySQL5.5 数据库持久层&#xff1a;阿里巴巴…

车载诊断协议UDS——会话模式状态机Session

UDS之Session服务 会话模式管控是汽车电子诊断范畴很重要的两个状态机之一(另一个是安全访问),不同的会话模式是用来区分诊断服务执行权限。 一位非常尊敬的业内前辈曾举如下例子来形容这个状态机:不同的场景,喝对应的酒! 公司商务场合下,对应的酒是红酒;长辈酒桌上,对…