Tic-Tac-Toe可能棋局遍历的实现(python)
目录
1. 前言
2. 算法流程
3. 代码实现
4. 一个思考题:代码实现中的一个坑
5. 结果正确吗?
1. 前言
在上一篇博客中: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/128506352 实现了搜索Tic-Tac-Toe游戏的某个棋局的python程序。
接下来的问题是,在Tic-Tac-Toe游戏中总共有多少种可能的棋局呢?注意,棋局是指在两个player交替下棋直到终局的过程中所导致的棋盘状态变化的序列。所以,即便所包含的棋盘状态集合完全相同,但是如果棋盘状态出现的顺序不同的话,也是不同的棋局。
游戏规则、基本表示方法等参考上一篇博客,本文不做赘述。
本文在上一篇的基础上进一步实现搜索Tic-Tac-Toe游戏的所有可能棋局的实现。
2. 算法流程
如果只需要搜索某一个可能的棋局,采用深度优先搜索或者广度优先搜索都可以。但是,如果要遍历所有可能的棋局,则需要采用深度优先搜索的方式来实现遍历,也称深度优先路径遍历。
Dfs(path):
s = path[-1] # 取路径列表中最后一个状态作为当前状态
查找s的所有邻接节点(即从s状态再下一手棋可能到达的状态)à neighbor_list
For neighbor in neighbor_list: # 遍历s所有邻接节点
If neighbor not in path: # 如果该节点已经在path中则跳过
Append neighbor to the end of path
If neighbor 是终局状态:
将path加入到path_list中去 # path_list是一个全局变量
Else:
递归调用:dfs(path)
Path.pop() # 这个不能少!
主程序中以初始状态开始调用dfs()即可求出所有的路径(Tic-Tac-Toe的所有可能棋局)。
这个算法流程中其实是留了一个坑。。。
3. 代码实现
# -*- coding: utf-8 -*-
"""
Created on Sun Jan 1 16:15:19 2023@author: chenxy
"""# -*- coding: utf-8 -*-
"""
Created on Sat Dec 31 12:53:10 2022@author: chenxy
"""import random
from collections import deque
import time
import itertoolswin_comb = ((0,1,2),(3,4,5),(6,7,8),(0,3,6),(1,4,7),(2,5,8),(0,4,8),(2,4,6))
path_list = [] # used to hold all the possible paths.
path_cnt = 0
dfs_cnt = 0def is_endofgame(s):def find_neighbor(s):def print_board(s):def dfs(path):global path_listglobal path_cntglobal dfs_cntdfs_cnt = dfs_cnt + 1# if dfs_cnt < 10: # for debug# print('dfs_cnt = {0}: path = {1}'.format(dfs_cnt,path))s = path[-1]neighbors = find_neighbor(s)for neighbor in neighbors:if neighbor not in path:## This segment is wrong!# path.append(neighbor)# end_flag, winner = is_endofgame(neighbor)# if end_flag:# path_list.append(path)# path_cnt = path_cnt + 1# if path_cnt < 10:# # print('path_cnt = {0}, path = {1}, len(path_list) = {2}'.format(path_cnt,path,len(path_list)))# print('path_cnt = {0}, path = {1}, path_list = {2}'.format(path_cnt,path,path_list))# else:# dfs(path)# path.pop() # pop-out the last added node to return to the upper layerend_flag, winner = is_endofgame(neighbor)if end_flag:# path_list.append(tuple([path,winner]))path_list.append(path + [neighbor])path_cnt = path_cnt + 1# if path_cnt < 10: # for debug# print('path_cnt = {0}, path = {1}, path_list = {2}'.format(path_cnt,path,path_list))else:dfs(path + [neighbor])# return path_listif __name__ == '__main__':# Initializations0 = tuple([0] * 9)path = [s0]tStart = time.time()dfs(path)tStop = time.time()state_set = set()# state_list = []# for path in path_list:for k in range(path_cnt):path = path_list[k]# print('k = {0}, path = {1}'.format(k,path))for s in path:state_set.add(s)# state_list = list(itertools.chain(*path_list))print('Totally there are {0} games'.format(len(path_list)))print('Totally there are {0} board states'.format(len(state_set)))print('Time cost: {0:6.2f} seconds'.format(tStop-tStart))
is_endofgame(s),find_neighbor(s),print_board(s)等三个函数的代码参见上一篇。
运行结果:
Totally there are 255168 games
Totally there are 5478 board states
也就是说,总共有255168中棋局,但是可能的盘面状态数要少得多,只有5478种状态。
其中,几种可能的棋局如下所示(调用print_board可以打印出更容易看的盘面状态变化图):
path_list = [
[(0, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0, 1), (0, 0, 0, 0, 0, 0, 0, 2, 1), (0, 0, 0, 0, 0, 0, 1, 2, 1), (0, 0, 0, 0, 0, 2, 1, 2, 1), (0, 0, 0, 0, 1, 2, 1, 2, 1), (0, 0, 0, 2, 1, 2, 1, 2, 1), (0, 0, 1, 2, 1, 2, 1, 2, 1)],
[(0, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0, 1), (0, 0, 0, 0, 0, 0, 0, 2, 1), (0, 0, 0, 0, 0, 0, 1, 2, 1), (0, 0, 0, 0, 0, 2, 1, 2, 1), (0, 0, 0, 0, 1, 2, 1, 2, 1), (0, 0, 0, 2, 1, 2, 1, 2, 1), (0, 1, 0, 2, 1, 2, 1, 2, 1), (0, 1, 2, 2, 1, 2, 1, 2, 1), (1, 1, 2, 2, 1, 2, 1, 2, 1)],
[(0, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0, 1), (0, 0, 0, 0, 0, 0, 0, 2, 1), (0, 0, 0, 0, 0, 0, 1, 2, 1), (0, 0, 0, 0, 0, 2, 1, 2, 1), (0, 0, 0, 0, 1, 2, 1, 2, 1), (0, 0, 0, 2, 1, 2, 1, 2, 1), (0, 1, 0, 2, 1, 2, 1, 2, 1), (2, 1, 0, 2, 1, 2, 1, 2, 1), (2, 1, 1, 2, 1, 2, 1, 2, 1)],
[(0, 0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0, 1), (0, 0, 0, 0, 0, 0, 0, 2, 1), (0, 0, 0, 0, 0, 0, 1, 2, 1), (0, 0, 0, 0, 0, 2, 1, 2, 1), (0, 0, 0, 0, 1, 2, 1, 2, 1), (0, 0, 0, 2, 1, 2, 1, 2, 1), (1, 0, 0, 2, 1, 2, 1, 2, 1)]]
4. 一个思考题:代码实现中的一个坑
前面说了算法流程(伪代码描述)留了一个坑。这个坑把我坑惨了,花了两个小时才终于看明白咋回事。
一上来按照算法流程的描述,在dfs()函数里面是如下实现的:
## This segment is wrong!
# path.append(neighbor)
# end_flag, winner = is_endofgame(neighbor)
# if end_flag:
# path_list.append(path)
# path_cnt = path_cnt + 1
# if path_cnt < 10:
# # print('path_cnt = {0}, path = {1}, len(path_list) = {2}'.format(path_cnt,path,len(path_list)))
# print('path_cnt = {0}, path = {1}, path_list = {2}'.format(path_cnt,path,path_list))
# else:
# dfs(path)
# path.pop() # pop-out the last added node to return to the upper layer
然后,运行结果始终是不对的。。。经过了两个小时的奋斗,终于想明白了这个问题^-^。这里先不解开谜底。有兴趣的小伙伴可以自己那这段错误的代码运行一下然后看看能不能想明白它为什么错了。
5. 结果正确吗?
上面的运行结果表明有255168种棋局,有5478种盘面状态。但是,这个结果对吗?
严格地来说是不对的。
因为以上实现没有考虑Tic-Tac-Toe游戏的棋盘的对称性(包括90旋转对称以及对角线对称)。举个例子说,第一手下在四个角上的任意一个角上本质上都是一样的。考虑了对称性所导致的重复后,总的可能棋局数和盘面状态数会大幅度减小。
但是,如何将对称性考虑进去进行去重(repetition removal)处理以得到真正的不同棋局数和盘面状态数结果呢?
且听下回分解。。。
Tic-Tac-Toe有多少种不同棋局和盘面状态(python实现)_笨牛慢耕的博客-CSDN博客在前两篇博客中实现了遍历搜索所有的Tic-Tac-Toe的棋局的python程序实现。根据上一篇的实现(搜索Tic-Tac-Toe所有可能棋局),结果表明有255168种棋局,有5478种盘面状态。但是,这个结果对吗?严格地来说是不对的。因为以上实现没有考虑Tic-Tac-Toe游戏的棋盘的对称性。举个例子说,第一手下在四个角上的任意一个角上本质上都是一样的。考虑了对称性所导致的重复后,总的可能棋局数和盘面状态数会大幅度减小。https://blog.csdn.net/chenxy_bwave/article/details/128521062
Tic-Tac-Toe可能棋局遍历的实现(python)相关推荐
- python二维游戏示例_Python实现的井字棋(Tic Tac Toe)游戏示例
本文实例讲述了Python实现的井字棋(Tic Tac Toe)游戏.分享给大家供大家参考,具体如下: 说明 用python实现了井字棋,整个框架是本人自己构思的,自认为比较满意.另外,90%+的代码 ...
- python井字棋ai,python 井字棋(Tic Tac Toe)
说明 用python实现了井字棋,整个框架是本人自己构思的,自认为比较满意.另外,90%+的代码也是本人逐字逐句敲的. minimax算法还没完全理解,所以参考了这里的代码,并作了修改. 特点 可以选 ...
- python井字棋游戏代码_Python实现的井字棋(Tic Tac Toe)游戏示例
Python实现的井字棋(Tic Tac Toe)游戏示例 来源:中文源码网 浏览: 次 日期:2018年9月2日 [下载文档: Python实现的井字棋(Tic Tac Toe)游戏示 ...
- react中使用构建缓存_通过在React中构建Tic Tac Toe来学习ReasonML
react中使用构建缓存 3. 7. 2018: UPDATED to ReasonReact v0.4.2 3. 7. 2018:更新为ReasonReact v0.4.2 You may have ...
- C++ 很有趣:编写一个井字游戏 (Tic Tac Toe)
英文原文:C++ is fun: Writing a Tic Tac Toe Game 这个有趣的C++系列打算展示一下使用C++写代码可以和其他主流语言一样高效而有趣.在第二部分,我将向你展示使用C ...
- python游戏代码运行不了_无法使我的tic tac toe游戏在python中正确运行
转不到"玩家1"的原因是你的支票中缺少一个空格.你也没有正确地检查一个玩家何时获胜,这就是为什么你会有这种奇怪的行为.你需要检查每个位置,而不仅仅是最后一个.我还添加了对用户输入的 ...
- Principle of Computing (Python)学习笔记(7) DFS Search + Tic Tac Toe use MiniMax Stratedy
1. Trees Tree is a recursive structure. 1.1 math nodes https://class.coursera.org/principlescomputin ...
- amazon.设计1. tic tac toe
//不觉中 已经全力找工作好久好久了.大概有1年半了.身心疲惫,不要放弃.曙光快来了. 1.tic tac toe //http://www.ntu.edu.sg/home/ehchua/progra ...
- 圈叉游戏 java_【炫光圈叉棋】炫光圈叉棋 Tic Tac Toe Glow 1.8.1下载_安卓(android)软件下载-魅族溜...
一款炫光风格的圈叉棋游戏,支持单/双人模式.圈叉棋,英文:tic-tac-toe,别名:圈叉游戏.是一种游戏,3*3的9个方格子,先下者画圈,后下者画叉,每人可以在任意没有对方棋子的封闭方格里下一次, ...
- python井字棋_python 井字棋(Tic Tac Toe)
说明 用python实现了井字棋,整个框架是本人自己构思的,自认为比较满意.另外,90%+的代码也是本人逐字逐句敲的. minimax算法还没完全理解,所以参考了这里的代码,并作了修改. 特点 可以选 ...
最新文章
- C++ 单例模式析构函数的运用,析构函数的线程安全
- Linux Kernel TCP/IP Stack — L1 Layer — Physical NIC
- python与c语言在语法上的区别-python和c语言的区别是什么
- 关于对Enum的理解
- python怎么清理垃圾_Python 中的“垃圾”是怎么回收的?
- apache arm 交叉编译_MacOS 下交叉编译的折腾笔记
- git 简单使用 基本操作
- switch php 比大小,PHP 基础:比较、If、Switch
- git-ftp:用git管理ftp服务器简单入门
- 利用Nginx+Mono+Fastcgi代替IIS对Asp.Net进行反向代理
- java div和table_详细为你讲解,DIV+CSS布局和TABLE布局的优缺点(经典)
- 高等代数章节知识回顾(干货笔记)
- lwj_C#_集合listT
- 全国各地电信网通铁通DNS服务器IP地址
- 牛客java选择题每日打卡Day9
- 平平无奇的营销小天才——ChatGPT
- Stepper Motor > 步进电机控制相关参数缩写
- 华三模拟器HCL下载与安装教程
- 2014山东省第五届ACM省赛
- PV操作经典例题——银行业务办理问题
热门文章
- c语言,学生信息管理
- 开机卡logope引导不了_TCL MS901主板卡logo故障案例
- Smali语法学习五
- java getclass() 继承_【java】继承和super.getClass()
- 歌木斯阿语词典软件评测
- php的分布式_php分布式是什么
- Shopee虾皮SRE工程师一面面经
- 冬吃萝卜,为何赛过“小人参”
- “10万元电商店”为何一夜之间在三四线城市火了
- IPv4/IPv6组播地址和组播MAC地址的转换