ARTICLE · 1083381
AI slop一则:TOPPEN(登山家)调查报告
Salicylic:
其实这里本来应该是我自己手工总结一下和AI一起探究的结果的,但实在是有些懒了,所以就直接堆叠结果。
简单来说,我最近老是在淘宝上看到这个游戏的商品,但是心里总怀疑它可能不耐玩。所以我干脆让AI模拟了一下,看看它 会不会是井字棋那种决策深度很低的游戏。从结果上说,虽然没有预期的那么糟糕,但如果太用力思考的话应该会很快变无聊吧,结论是适合拿来作为派对游戏那样玩而不是像《茂林源记》或者《展翅翱翔》那样狠狠玩。

TOPPEN 调查报告
2026 年 9 月 25 到 26 日,我研究了日本桌游 TOPPEN(トッペン),目标是把它的规则整理成能跑在电脑上的程序,再用程序回答几个关于这个游戏的问题。这份报告是整个过程的事后整理:规则怎么考据的,代码写了什么,提出过哪些猜想,哪些验证了,哪些被我自己的数据推翻。
配套文件有两个。toppen_solver.py 是全部代码,单文件 Python,838 行。输出日志.md 按时间顺序收录了每一次运行的完整终端输出。所有随机实验都固定了种子,数字可以复现。
一、游戏本身
TOPPEN,原版叫《バベルの塔》(巴别塔),设计师新澤大樹,2012 年作为同人游戏颁布,2021 年由游戏店コロコロ堂重制发行。两人对弈,十枚木瓦,两种花纹各五枚,一局十分钟。BGG 上 49 人评分 5.7,权重 1.5,登记为抽象策略游戏。
玩法:十枚瓦片背面朝上洗乱,后手把它们摆成一个边相连的形状,然后翻面。此后轮流行动,每次移动一枚己方颜色的瓦片,必须是它所在塔的顶子,走正交的一格,叠到相邻格子的塔顶上。目的地不能是空格。移动之后,所有瓦片必须仍然连成一片,不许把场地撕成两块。没棋可走就跳过,对手连走。十枚叠成一座塔的时候,塔顶那枚的颜色获胜。
这套规则看起来简单,但有三处当时的资料互相打架,都靠一手证据才定下来。
二、规则考据,以及三个坑
第一个坑是胜利条件。BGG 的搜索摘要写着"用四枚棋子连成直线",来源是一篇实测博客。把博客正文完整抓下来之后发现,"连直线"是同一篇文章里下一款游戏 Flow 的介绍,摘要拼接时错位到了 TOPPEN 段落。五个独立来源(出版社、BGG 词条、两个日本资料站、博客本身)在"叠成一座塔,顶子颜色获胜"上完全一致。此案了结。
第二个坑最要命:能不能把瓦片移进空格。实测博客说可以,BGG 词条明确列在三条限制里说不行。证据一度两比一倾向"可以",我差点按它写引擎。最后是 BGG 图库里一张 2013 年上传的组件照片救了场:照片里摊着原版说明书,分辨率不够,我把它裁成四块放大,读出了关键句"タイルが移動先に1枚以上あれば可能です",移动的前提是目的地至少有一枚瓦片。结论反转,不可移入空格。回头看,博客作者的下一句"场地会逐渐缩小"本来就和自己的说法矛盾,是我当时没有较真。
第三个坑是骏河屋商品页那句"隣り合う他のタイルの横に移動させるか、上に重ねていきます"。我最初把"横に移動"读成"移到旁边的空格",和坑二连起来看差点造成误判。正确读法是"横向移动一格、叠上去",一句话描述移动的两个方面。
考据还留下一条没能完全读出的规则:说明书提到了千日手(局面反复导致游戏无法推进),但裁定条文被照片里的瓦片挡住了,只能确认存在,内容存疑。其余遗留的小项(谁先手、斜向是否允许等)在代码里全部做成了参数,默认值在报告第五节会交代。
三、求解器
代码是一个 int64 状态编码加三套算法。状态编码:十格塔高各占 4 位,颜色序列占 10 位,加行动方,一个整数就是一个局面。着法生成按规则逐条翻译,每次移动后断言不变量(总数 10、两色各 5、占格连通)。局面图用广度优先从初盘枚举,胜负用三值回归式求解:能强制登顶的标胜,任何着法都逃不掉的标负,其余标记为和局。和局采用循环语义,即无限重复等于和局,这正是说明书千日手条款的自然读法。
写回归式的时候踩了一个坑。所有后继都是"对手成塔"的残局,比如一座单枚对一座九枚塔,这种状态的胜负必须在预扫描阶段就裁定,否则它永远进不了传播队列,会被误判成和局。修掉之后,代码通过了换色对称性校验、和局循环的逐步合法性校验,以及十几个随机种子的不变量断言。
可行性的账在开工前就算过:对局树有大约 10 的 20 次方条路径,但不同局面(图上的节点)每盘初盘只有 10 万到 100 万个,全部地形加起来上界约 8200 万。指数的树被线性的图压掉了,这是整个项目能做的根本原因。Python 试点实测每秒 5.5 万状态,全量普查要一周,不可行;Node.js 加类型数组估计几小时、2GB 内存,可行。不过后来的发现让全量方案根本没必要启动,这是后话。
四、采样给的三次教训
第一课:小样本会撒谎。每种地形只试 4 种配色时,直线地形 4 比 0 全是先手胜,十字地形 4 比 0 全是后手胜。加到每种 30 个配色,直线掉到 57%,十字掉到 43%。所谓"全胜地形"不存在,存在的是偏置强度不同的地形。
第二课:筛选不算数。为了找后手优势地形,我快速筛了 40 个随机生成的地形,其中一个筛出 62.5% 后手胜。对它做全 252 种配色的普查,真实数字是 42.5%。先筛后验的做法里,筛选阶段的极端值会被均值回归打回来。这之后我只报告验证过的数字。
第三课:错了要留痕。我自己提过一个"补色配对可以把普查省一半"的省钱思路,推演时发现角色对换的同构会把先手映射成后手,而普查固定的是先手视角,所以补色不提供任何节省。这个想法死在了写代码之前。另一次,我给"分支越多和局越多"下过一个粗结论,后来双十字的数据(分支全谱最多,和局率却低于十字)把它修正成了"排布比计数重要"。这两处修正都写进了猜想的账本,没有抹掉。
五、两个定理
项目从"做实验"升级为"有结构",靠的是两个定理。
图同构不变性。 四种形状完全不同的路径地形(直线、蛇形、L 形、阶梯),252 种配色的普查结果逐位相同,都是先手 122 胜、后手 114 胜、16 和局。两个长得毫不相干的随机地形后来也被发现是同构图,普查同样逐位相同。原因说穿了很朴素:规则只引用"相邻"和"连通",全部通过地形图的抽象结构表达,网格上怎么拐弯从不进入规则。推论是 4655 种十格多联骨牌里,所有路径形坍缩成同一局博弈,全量普查的真实规模是图的同构类数,远小于表面数字。
节奏奇偶定理。 每一局对局恰好发生 9 次合并(塔数从 10 降到 1),第 9 次合并的人把己方瓦片放上唯一的塔顶,所以第 9 合并者获胜。倒腾和被迫跳过都不改变塔数,它们是"浪费着"。设双方浪费着数为 a 和 b,总手数就是 9+a+b,先手走第 1 手,所以先手胜当且仅当 a+b 是偶数。整个博弈归结为一场浪费着的奇偶战争:谁能在需要翻转奇偶的时刻拿得出浪费着,谁就赢。
遗留参数的默认值也在这里交代:非摆放方先手;只允许正交移动;三次重复局面判和;设置时后手选形、颜色随机;重制版规则与原版一致。这些参数在代码里都可以切换。
六、数据
十几个地形的全 252 种配色精确普查结果如下。表里 A 是先手胜率,B 是后手胜率,D 是和局率。
抽样汇总:前后三轮共 348 个抽样根局面加约 2500 个普查根局面,总体先手胜率 63% 到 64%,后手 29% 到 30%,和局 6% 到 10%。
策略深度的精确统计(573 万个已解状态):平均分支因子 2.6,最大 14;30.3% 的局面只有唯一合法手;14.2% 的局面有一枚好棋但陷阱可选;30.6% 的局面存在会改变胜负的错误选择。随机对局长度均值约 50 步,95 分位 84 步,最长 235 步,一万七千多局没有一局失控。
七、和局长什么样
日志第十节有两则完整的和局记录,逐手打印了棋盘,这里只说结构。
纯环的和局配色是环上颜色交替,全 252 种配色里只有 2 个。终局剩三座塔,塔顶全是后手的颜色,先手的瓦片全被埋住,只能被迫跳过;后手握着唯一机动的瓦片,在两格之间来回倒腾。后手为什么不直接合并?因为每座塔的次层都埋着先手的瓦片,后手一削塔,埋着的先手瓦片就在第 9 合并前复活,反偷塔顶。求解器验证过后手所有破环尝试,全部通向先手胜,于是拖和成了双方的最优解。
环8+尾2 的和局终局是两座相邻高塔之间的贸易舞。瓦片在两塔间来回换位,塔高沿 4比6、5比5、6比4 循环振荡,谁都不敢把塔削到单枚,因为塔底埋着的颜色一旦暴露,对面的合并立刻按奇偶定理兑现成胜负。循环里双方各有一次被迫跳过,每一手浪费都翻转"若立即连续合并第 9 手归属"的预测列,而预测列永远追不到头。
这些记录还定格了一个反直觉细节:被迫跳过同样是奇偶武器。浪费着不分敌我,只看奇偶。被埋的一方靠被迫跳过维持对自己有利的奇偶性,和主动倒腾在定理眼里没有区别。
八、词汇与猜想的账本
过程中自然长出了一批只有我们俩懂的词。地形指初盘连通形状;路径形、环族、蜘蛛族、双十字是地形的图结构分类;普查指 252 种配色全部解掉,轨道约化是利用自同构群把 252 压到几十个代表元;浪费着包括倒腾和被迫跳过;奇偶战争、资源剥夺、贸易舞、削塔陷阱描述机制;强制手、唯一好棋、可犯错节点是局面选择自由度的三档;47.2% 这个数字被我们叫做后手天花板;先手优势谱指的是从纯环的 99% 到路径的 48% 这条连续谱。
猜想各有各的命运。图同构不变性和节奏奇偶定理从猜想升级成了定理,前者三次独立验证,后者有严格证明加记录可视化。环长越长先手优势越大,在环族内单调成立。腿长越均匀后手越强、和局越多,在蜘蛛族内成立。存在全胜地形的猜想被加密采样推翻。分支越多和局越多被双十字修正。资源剥夺机制与全部梯度一致,但严格归因还欠一步。后手胜率越过 50% 的地形,已测的七十多个里没有出现,但它是不是真的不存在,以现有普查覆盖面还不能下结论。
九、没解决的问题
三件事挂在墙上。第一,后手胜率超过 50% 的地形是否存在于未普查的图类里,要回答它需要按图同构去重之后的全量普查,估计还有几千个图类。第二,资源剥夺机制的严格归因,需要提取主变例、逐局统计双方的浪费着收支,目前只有梯度证据。第三,也是最贴近真实规则的一个:后手有权挑选地形,那么在"后手选形"的博弈下,这游戏的真实先手胜率是多少。现在地图已经足够密,这一步随时可以做。
十、文件清单与复现
toppen_solver.py,838 行,依赖只有 Python 标准库。运行环境是 Windows 11 下的 conda 环境 datasci_env(纯 Python,任何 3.8+ 都能跑)。用法:
python toppen_solver.py smoke # 单地形烟囱测试python toppen_solver.py batch 4 # 10 地形 x 4 配色抽样python toppen_solver.py dense 30 # 10 地形 x 30 配色加密采样python toppen_solver.py random_shapes 12 4 # 12 个随机地形 x 4 配色python toppen_solver.py exact252 line10,snake10 # 指定地形 252 配色暴力普查python toppen_solver.py census rect2x5,ring8+2 # 轨道约化精确普查(推荐)python toppen_solver.py hunt 40 8 # 随机地形筛选+验证python toppen_solver.py drawrecord C10ring # 提取和局对局记录输出日志.md 按运行顺序收录了十次调用的完整输出,每次开头注明命令行。日志是当时终端输出的誊录,中间的调试轮次(两次 bug 修复、一次语法修正)没有收录,过程在报告第三、四节有交代。
数据快照日期 2026 年 9 月 19 日。引用的外部资料:出版社 korokorodou.com 商品页、BGG 词条 253095 与图库照片 pic5579735、Hiew 的实测博客(2025 年 7 月)、骏河屋原版商品页、JELLY JELLY CAFE 游戏介绍页。
TOPPEN 调查:运行输出日志
按时间顺序收录 toppen_solver.py 各次运行的完整终端输出,每次开头注明命令行。这些是各命令最终一次运行的誊录;中间的调试轮次(两次求解器 bug 修复、一次语法修正、一次几何认知修正)没有单独收录,过程见报告第三、四节。运行环境:Windows 11,conda 环境 datasci_env,纯 Python。日期 2026-09-18 至 09-19。
1. smoke(烟囱测试,rect2x5 交替配色)
python toppen_solver.py smokestates=339391 enumerate=5.04s solve=0.50sroot value: DRAWmean_branch=2.787 frac_L1=0.2554 frac_O1(L>1)=0.2014 frac_blunderable=0.4208L_hist={1: 86668, 2: 79173, 3: 74640, 4: 52987, 5: 24913, 6: 12068, 7: 5477, 8: 2213, 9: 762, 10: 378, 11: 69, 12: 30, 13: 13}layer_hist={2: 15210, 3: 50644, 4: 92506, 5: 99928, 6: 63763, 7: 15966, 8: 1360, 9: 13, 10: 1}symmetry: root=D swapped-root=D match=Truedraw_states=130083/339391draw cycle length: 8 legal=True (1, 2):BBA (1, 3):AAB (1, 4):BBAA mover=A (1, 2):BBA (1, 3):AABA (1, 4):BBA mover=B (1, 2):BBA (1, 3):AABA (1, 4):BBA mover=A (1, 2):BBA (1, 3):AAB (1, 4):BBAA mover=B (1, 2):BBA (1, 3):AA (1, 4):BBAAB mover=A (1, 2):BBA (1, 3):A (1, 4):BBAABA mover=B (1, 2):BBA (1, 3):A (1, 4):BBAABA mover=A (1, 2):BBA (1, 3):AA (1, 4):BBAAB mover=Bno-shuffle control: root=D states=480playouts n=2000 mean_len=46.5 max=229 runaway=0注:no-shuffle 对照组是一个被禁用倒腾着的人工变体,存在"冻结态"伪影,只作 smoke 参考,未进入任何结论。
2. batch(10 地形 × 4 配色抽样)
python toppen_solver.py batch 4[line10] values over 4 colorings: ['A', 'A', 'A', 'A'][rect2x5] values over 4 colorings: ['A', 'A', 'A', 'A'][blob3x3+1] values over 4 colorings: ['A', 'D', 'A', 'A'][cross10] values over 4 colorings: ['B', 'B', 'B', 'B'][snake10] values over 4 colorings: ['B', 'D', 'B', 'A'][L6x4] values over 4 colorings: ['A', 'A', 'A', 'B'][T5x5] values over 4 colorings: ['B', 'A', 'B', 'A'][H10] values over 4 colorings: ['A', 'A', 'A', 'A'][stair10] values over 4 colorings: ['A', 'A', 'A', 'D'][ring8+2] values over 4 colorings: ['A', 'A', 'A', 'A']=== batch summary ===roots=40 values: {'A': 28, 'D': 3, 'B': 9}total_states=5727520 max_root_states=411302 wall=102.9spooled mean_branch=2.607 frac_L1=0.3031 frac_O1(L>1)=0.1423 frac_blunderable=0.3059pooled L_hist: L1:1735732, L2:1378588, L3:1151773, L4:809526, L5:353378, L6:183463, L7:70256, L8:27656, L9:10505, L10:4943, L11:1072, L12:441, L13:153, L14:34draw roots: [('blob3x3+1', 1), ('snake10', 1), ('stair10', 3)]playouts n=15000 mean=49.7 p50=47 p95=84 max=2353. dense(10 地形 × 30 配色加密采样)
python toppen_solver.py dense 30[line10] K=30 A=17 B=11 D=2 A%=57% (17s)[rect2x5] K=30 A=27 B=0 D=3 A%=90% (158s)[blob3x3+1] K=30 A=21 B=6 D=3 A%=70% (221s)[cross10] K=30 A=13 B=13 D=4 A%=43% (61s)[snake10] K=30 A=17 B=12 D=1 A%=57% (18s)[L6x4] K=30 A=15 B=15 D=0 A%=50% (19s)[T5x5] K=30 A=12 B=15 D=3 A%=40% (27s)[H10] K=30 A=21 B=8 D=1 A%=70% (111s)[stair10] K=30 A=18 B=11 D=1 A%=60% (18s)[ring8+2] K=30 A=28 B=1 D=1 A%=93% (52s)TOTAL K=300 A=189 B=92 D=19 A%=63%4. random_shapes(12 个随机地形 × 4 配色)
python toppen_solver.py random_shapes 12 4=== rand1 A=3 B=1 D=0 (first coloring shown) ===AABABBAB...A...B=== rand2 A=2 B=2 D=0 (first coloring shown) ===BABBB.AB...A..AA=== rand3 A=2 B=2 D=0 (first coloring shown) ===B.AABAA..ABB..B.=== rand4 A=2 B=2 D=0 (first coloring shown) ===.BA.BBAAB.B.A.A.=== rand5 A=1 B=2 D=1 (DRAW coloring shown) ===.A....A....B...BABAA...B....B.=== rand6 A=3 B=0 D=1 (DRAW coloring shown) ===AB..ABAA..A.BBB.=== rand7 A=4 B=0 D=0 (first coloring shown) ===..A..ABBBBAB.AA.=== rand8 A=3 B=1 D=0 (first coloring shown) ===.AAB..AB...AABBB=== rand9 A=4 B=0 D=0 (first coloring shown) ===BBA..BABBA..A.A=== rand10 A=4 B=0 D=0 (first coloring shown) ===B.BABBAAB.AA=== rand11 A=4 B=0 D=0 (first coloring shown) ===..ABBABABA..AB.=== rand12 A=2 B=1 D=1 (DRAW coloring shown) ===BABB.A..BABA.A.TOTAL roots=48 A=34 B=11 D=35. exact252(6 个均衡地形的 252 配色暴力普查)
python toppen_solver.py exact252 line10,snake10,L6x4,stair10,T5x5,cross10 [line10] 36/252 ... A=28 B=4 D=4 [line10] 72/252 ... A=54 B=13 D=5 [line10] 108/252 ... A=73 B=27 D=8 [line10] 144/252 ... A=89 B=47 D=8 [line10] 180/252 ... A=104 B=65 D=11 [line10] 216/252 ... A=116 B=88 D=12 [line10] 252/252 ... A=122 B=114 D=16[line10] EXACT n=252 A=122 (48.4%) B=114 (45.2%) D=16 (6.3%) (146s) [snake10] 36/252 ... A=28 B=4 D=4 [snake10] 72/252 ... A=54 B=13 D=5 [snake10] 108/252 ... A=73 B=27 D=8 [snake10] 144/252 ... A=89 B=47 D=8 [snake10] 180/252 ... A=104 B=65 D=11 [snake10] 216/252 ... A=116 B=88 D=12 [snake10] 252/252 ... A=122 B=114 D=16[snake10] EXACT n=252 A=122 (48.4%) B=114 (45.2%) D=16 (6.3%) (143s) [L6x4] 36/252 ... A=1 B=35 D=0 [L6x4] 72/252 ... A=8 B=62 D=2 [L6x4] 108/252 ... A=24 B=76 D=8 [L6x4] 144/252 ... A=45 B=91 D=8 [L6x4] 180/252 ... A=60 B=106 D=14 [L6x4] 216/252 ... A=87 B=113 D=16 [L6x4] 252/252 ... A=122 B=114 D=16[L6x4] EXACT n=252 A=122 (48.4%) B=114 (45.2%) D=16 (6.3%) (146s) [stair10] 36/252 ... A=28 B=4 D=4 [stair10] 72/252 ... A=54 B=13 D=5 [stair10] 108/252 ... A=73 B=27 D=8 [stair10] 144/252 ... A=89 B=47 D=8 [stair10] 180/252 ... A=104 B=65 D=11 [stair10] 216/252 ... A=116 B=88 D=12 [stair10] 252/252 ... A=122 B=114 D=16[stair10] EXACT n=252 A=122 (48.4%) B=114 (45.2%) D=16 (6.3%) (148s) [T5x5] 36/252 ... A=24 B=9 D=3 [T5x5] 72/252 ... A=45 B=21 D=6 [T5x5] 108/252 ... A=62 B=35 D=11 [T5x5] 144/252 ... A=80 B=53 D=11 [T5x5] 180/252 ... A=94 B=71 D=15 [T5x5] 216/252 ... A=106 B=91 D=19 [T5x5] 252/252 ... A=115 B=114 D=23[T5x5] EXACT n=252 A=115 (45.6%) B=114 (45.2%) D=23 (9.1%) (226s) [cross10] 36/252 ... A=0 B=36 D=0 [cross10] 72/252 ... A=6 B=66 D=0 [cross10] 108/252 ... A=14 B=87 D=7 [cross10] 144/252 ... A=22 B=103 D=19 [cross10] 180/252 ... A=32 B=119 D=29 [cross10] 216/252 ... A=65 B=119 D=32 [cross10] 252/252 ... A=101 B=119 D=32[cross10] EXACT n=252 A=101 (40.1%) B=119 (47.2%) D=32 (12.7%) (469s)6. hunt(40 个随机地形筛选 + 3 个候选验证)
python toppen_solver.py hunt 40 8hunt1: A=7 B=1 D=0 cost=6.49s/roothunt2: A=5 B=3 D=0 cost=4.14s/roothunt3: A=5 B=1 D=2 cost=3.56s/roothunt4: A=7 B=1 D=0 cost=5.82s/roothunt5: A=8 B=0 D=0 cost=5.06s/roothunt6: A=1 B=4 D=3 cost=0.99s/roothunt7: A=7 B=0 D=1 cost=3.73s/roothunt8: A=2 B=5 D=1 cost=1.12s/roothunt9: A=5 B=3 D=0 cost=2.86s/roothunt10: A=3 B=4 D=1 cost=1.17s/roothunt11: A=7 B=0 D=1 cost=5.18s/roothunt12: A=3 B=4 D=1 cost=2.31s/roothunt13: A=7 B=1 D=0 cost=2.58s/roothunt14: A=8 B=0 D=0 cost=3.34s/roothunt15: A=5 B=2 D=1 cost=4.70s/roothunt16: A=3 B=4 D=1 cost=1.99s/roothunt17: A=5 B=3 D=0 cost=3.81s/roothunt18: A=8 B=0 D=0 cost=1.24s/roothunt19: A=4 B=3 D=1 cost=2.89s/roothunt20: A=3 B=4 D=1 cost=1.08s/roothunt21: A=5 B=3 D=0 cost=1.09s/roothunt22: A=3 B=4 D=1 cost=2.22s/roothunt23: A=6 B=2 D=0 cost=4.17s/roothunt24: A=6 B=1 D=1 cost=0.83s/roothunt25: A=5 B=2 D=1 cost=1.02s/roothunt26: A=4 B=2 D=2 cost=2.91s/roothunt27: A=7 B=1 D=0 cost=4.89s/roothunt28: A=5 B=3 D=0 cost=2.15s/roothunt29: A=6 B=2 D=0 cost=4.13s/roothunt30: A=4 B=4 D=0 cost=1.38s/roothunt31: A=6 B=2 D=0 cost=3.41s/roothunt32: A=5 B=3 D=0 cost=3.25s/roothunt33: A=6 B=1 D=1 cost=4.06s/roothunt34: A=5 B=1 D=2 cost=1.49s/roothunt35: A=6 B=1 D=1 cost=3.24s/roothunt36: A=7 B=1 D=0 cost=4.17s/roothunt37: A=3 B=2 D=3 cost=2.12s/roothunt38: A=6 B=1 D=1 cost=1.16s/roothunt39: A=7 B=1 D=0 cost=2.35s/roothunt40: A=5 B=3 D=0 cost=0.62s/root=== tier2 verification (3 promoted) ===--- hunt8 [B-lean] verify n=252 (EXACT census): A=122 (48.4%) B=107 (42.5%) D=23 (9.1%) (288s)###.#...####.##.--- hunt30 [BAL] verify n=252 (EXACT census): A=117 (46.4%) B=105 (41.7%) D=30 (11.9%) (366s)...#.####..#.##..##.--- hunt10 [BAL] verify n=252 (EXACT census): A=122 (48.4%) B=107 (42.5%) D=23 (9.1%) (287s)###..##..#.#.###7. 自同构群阶校验
python -c "import toppen_solver as t; [print(n, len(t.find_automorphisms(t.SHAPES[n].adj))) for n in [...]]"rect2x5 4ring8+2 2C10ring 20C6P4 1C4P6 2spider6111 6spider5211 2spider4311 2spider4221 2spider3321 2注:C6P4 的 |Aut|=1 暴露了一个几何错误,2x3 矩形边界不是纯 6 环(带弦),网格上不存在纯 C6。该地形改名为 blk23P4 后照常使用。
8. census(环族 + rect2x5,轨道约化精确普查)
python toppen_solver.py census rect2x5,ring8+2,C10ring,blk23P4,C4P6[rect2x5] |Aut|=4 orbits=66 EXACT: A=210 (83.3%) B=26 (10.3%) D=16 (6.3%) (339s)[ring8+2] |Aut|=2 orbits=138 EXACT: A=218 (86.5%) B=14 (5.6%) D=20 (7.9%) (231s)[C10ring] |Aut|=20 orbits=16 EXACT: A=250 (99.2%) B=0 (0.0%) D=2 (0.8%) (20s)[blk23P4] |Aut|=1 orbits=252 EXACT: A=180 (71.4%) B=56 (22.2%) D=16 (6.3%) (484s)[C4P6] |Aut|=2 orbits=182 EXACT: A=139 (55.2%) B=91 (36.1%) D=22 (8.7%) (158s)9. census(蜘蛛族)
python toppen_solver.py census spider6111,spider5211,spider4311,spider4221,spider3321[spider6111] |Aut|=6 orbits=112 EXACT: A=115 (45.6%) B=114 (45.2%) D=23 (9.1%) (96s)[spider5211] |Aut|=2 orbits=182 EXACT: A=114 (45.2%) B=112 (44.4%) D=26 (10.3%) (205s)[spider4311] |Aut|=2 orbits=182 EXACT: A=107 (42.5%) B=115 (45.6%) D=30 (11.9%) (245s)[spider4221] |Aut|=2 orbits=152 EXACT: A=137 (54.4%) B=101 (40.1%) D=14 (5.6%) (232s)[spider3321] |Aut|=2 orbits=138 EXACT: A=122 (48.4%) B=103 (40.9%) D=27 (10.7%) (225s)10. census(双十字)
python toppen_solver.py census ddagger,ddaggerH[ddagger] |Aut|=72 orbits=26 EXACT: A=124 (49.2%) B=104 (41.3%) D=24 (9.5%) (37s)[ddaggerH] |Aut|=72 orbits=26 EXACT: A=124 (49.2%) B=104 (41.3%) D=24 (9.5%) (37s)注:竖连与横连双十字是同一个抽象树的不同嵌入,普查逐位相同,图同构不变性第三次自证。
11. drawrecord(和局对局记录提取)
python toppen_solver.py drawrecord C10ring,ring8+2##### C10ring: draw orbits=1/16 draw colorings=2/252 |Aut|=20draw coloring (A=先手色/B=后手色):ABABB..AABAB--- 从开局到进入循环共 28 手; 循环从第 25 手起, 长 4 手 --- 1. A 合并#1 (0, 0)->(1, 0) [浪费A=0,B=0 | 若此后再无浪费,第9合并归A] 2. B 合并#2 (0, 1)->(0, 2) [浪费A=0,B=0 | 若此后再无浪费,第9合并归A] 3. A 倒腾(浪费) (1, 0)->(2, 0) [浪费A=1,B=0 | 若此后再无浪费,第9合并归B] 4. B 合并#3 (1, 0)->(2, 0) [浪费A=1,B=0 | 若此后再无浪费,第9合并归B] 5. A 跳过(浪费) — [浪费A=2,B=0 | 若此后再无浪费,第9合并归A] 6. B 倒腾(浪费) (0, 2)->(0, 3) [浪费A=2,B=1 | 若此后再无浪费,第9合并归B] 7. A 合并#4 (0, 2)->(0, 3) [浪费A=2,B=1 | 若此后再无浪费,第9合并归B] 8. B 倒腾(浪费) (2, 0)->(2, 1) [浪费A=2,B=2 | 若此后再无浪费,第9合并归A] 9. A 倒腾(浪费) (0, 3)->(1, 3) [浪费A=3,B=2 | 若此后再无浪费,第9合并归B] 10. B 倒腾(浪费) (0, 3)->(1, 3) [浪费A=3,B=3 | 若此后再无浪费,第9合并归A] 11. A 倒腾(浪费) (2, 0)->(2, 1) [浪费A=4,B=3 | 若此后再无浪费,第9合并归B] 12. B 合并#5 (0, 3)->(1, 3) [浪费A=4,B=3 | 若此后再无浪费,第9合并归B] 13. A 合并#6 (2, 0)->(2, 1) [浪费A=4,B=3 | 若此后再无浪费,第9合并归B] 14. B 倒腾(浪费) (1, 3)->(2, 3) [浪费A=4,B=4 | 若此后再无浪费,第9合并归A] 15. A 倒腾(浪费) (2, 1)->(2, 2) [浪费A=5,B=4 | 若此后再无浪费,第9合并归B] 16. B 倒腾(浪费) (2, 3)->(1, 3) [浪费A=5,B=5 | 若此后再无浪费,第9合并归A] 17. A 倒腾(浪费) (2, 2)->(2, 3) [浪费A=6,B=5 | 若此后再无浪费,第9合并归B] 18. B 倒腾(浪费) (1, 3)->(2, 3) [浪费A=6,B=6 | 若此后再无浪费,第9合并归A] 19. A 倒腾(浪费) (2, 1)->(2, 2) [浪费A=7,B=6 | 若此后再无浪费,第9合并归B] 20. B 倒腾(浪费) (2, 1)->(2, 2) [浪费A=7,B=7 | 若此后再无浪费,第9合并归A] 21. A 跳过(浪费) — [浪费A=8,B=7 | 若此后再无浪费,第9合并归B] 22. B 合并#7 (2, 1)->(2, 2) [浪费A=8,B=7 | 若此后再无浪费,第9合并归B] 23. A 跳过(浪费) — [浪费A=9,B=7 | 若此后再无浪费,第9合并归A] 24. B 倒腾(浪费) (2, 2)->(2, 3) [浪费A=9,B=8 | 若此后再无浪费,第9合并归B] 25. A 跳过(浪费) — [浪费A=10,B=8 | 若此后再无浪费,第9合并归A] 26. B 倒腾(浪费) (2, 3)->(1, 3) [浪费A=10,B=9 | 若此后再无浪费,第9合并归B] 27. A 跳过(浪费) — [浪费A=11,B=9 | 若此后再无浪费,第9合并归A] 28. B 倒腾(浪费) (1, 3)->(2, 3) [浪费A=11,B=10 | 若此后再无浪费,第9合并归B]--- 循环段逐手棋盘(局面在循环首重现, 可无限持续) ---循环第1手 A (跳过 None->). . . . . . . AAB . . AAB BABB循环第2手 B (倒腾 (2, 3)->(1, 3)). . . . . . . AAB . . AAB BABB循环第3手 A (跳过 None->). . . . . . . AABB. . AAB BAB 循环第4手 B (倒腾 (1, 3)->(2, 3)). . . . . . . AABB. . AAB BAB ##### ring8+2: draw orbits=12/138 draw colorings=20/252 |Aut|=2draw coloring (A=先手色/B=后手色):AAAB.BAAB.B..B.--- 从开局到进入循环共 33 手; 循环从第 24 手起, 长 10 手 --- 1. A 合并#1 (0, 0)->(0, 1) [浪费A=0,B=0 | 若此后再无浪费,第9合并归A] 2. B 合并#2 (4, 1)->(3, 1) [浪费A=0,B=0 | 若此后再无浪费,第9合并归A] 3. A 倒腾(浪费) (0, 1)->(0, 2) [浪费A=1,B=0 | 若此后再无浪费,第9合并归B] 4. B 倒腾(浪费) (3, 1)->(2, 1) [浪费A=1,B=1 | 若此后再无浪费,第9合并归A] 5. A 倒腾(浪费) (0, 2)->(1, 2) [浪费A=2,B=1 | 若此后再无浪费,第9合并归B] 6. B 合并#3 (3, 1)->(2, 1) [浪费A=2,B=1 | 若此后再无浪费,第9合并归B] 7. A 合并#4 (0, 1)->(0, 2) [浪费A=2,B=1 | 若此后再无浪费,第9合并归B] 8. B 倒腾(浪费) (2, 1)->(2, 2) [浪费A=2,B=2 | 若此后再无浪费,第9合并归A] 9. A 倒腾(浪费) (1, 2)->(2, 2) [浪费A=3,B=2 | 若此后再无浪费,第9合并归B] 10. B 倒腾(浪费) (2, 1)->(2, 2) [浪费A=3,B=3 | 若此后再无浪费,第9合并归A] 11. A 倒腾(浪费) (0, 2)->(1, 2) [浪费A=4,B=3 | 若此后再无浪费,第9合并归B] 12. B 倒腾(浪费) (2, 2)->(2, 1) [浪费A=4,B=4 | 若此后再无浪费,第9合并归A] 13. A 合并#5 (0, 2)->(1, 2) [浪费A=4,B=4 | 若此后再无浪费,第9合并归A] 14. B 合并#6 (1, 0)->(2, 0) [浪费A=4,B=4 | 若此后再无浪费,第9合并归A] 15. A 倒腾(浪费) (1, 2)->(2, 2) [浪费A=5,B=4 | 若此后再无浪费,第9合并归B] 16. B 倒腾(浪费) (2, 1)->(2, 0) [浪费A=5,B=5 | 若此后再无浪费,第9合并归A] 17. A 倒腾(浪费) (1, 2)->(2, 2) [浪费A=6,B=5 | 若此后再无浪费,第9合并归B] 18. B 合并#7 (1, 2)->(2, 2) [浪费A=6,B=5 | 若此后再无浪费,第9合并归B] 19. A 跳过(浪费) — [浪费A=7,B=5 | 若此后再无浪费,第9合并归A] 20. B 倒腾(浪费) (2, 0)->(2, 1) [浪费A=7,B=6 | 若此后再无浪费,第9合并归B] 21. A 跳过(浪费) — [浪费A=8,B=6 | 若此后再无浪费,第9合并归A] 22. B 倒腾(浪费) (2, 0)->(2, 1) [浪费A=8,B=7 | 若此后再无浪费,第9合并归B] 23. A 合并#8 (2, 0)->(2, 1) [浪费A=8,B=7 | 若此后再无浪费,第9合并归B] 24. B 倒腾(浪费) (2, 2)->(2, 1) [浪费A=8,B=8 | 若此后再无浪费,第9合并归A] 25. A 倒腾(浪费) (2, 2)->(2, 1) [浪费A=9,B=8 | 若此后再无浪费,第9合并归B] 26. B 跳过(浪费) — [浪费A=9,B=9 | 若此后再无浪费,第9合并归A] 27. A 倒腾(浪费) (2, 1)->(2, 2) [浪费A=10,B=9 | 若此后再无浪费,第9合并归B] 28. B 倒腾(浪费) (2, 1)->(2, 2) [浪费A=10,B=10 | 若此后再无浪费,第9合并归A] 29. A 倒腾(浪费) (2, 1)->(2, 2) [浪费A=11,B=10 | 若此后再无浪费,第9合并归B] 30. B 倒腾(浪费) (2, 1)->(2, 2) [浪费A=11,B=11 | 若此后再无浪费,第9合并归A] 31. A 跳过(浪费) — [浪费A=12,B=11 | 若此后再无浪费,第9合并归B] 32. B 倒腾(浪费) (2, 2)->(2, 1) [浪费A=12,B=12 | 若此后再无浪费,第9合并归A] 33. A 倒腾(浪费) (2, 2)->(2, 1) [浪费A=13,B=12 | 若此后再无浪费,第9合并归B]--- 循环段逐手棋盘(局面在循环首重现, 可无限持续) ---循环第1手 B (倒腾 (2, 2)->(2, 1)). . . . . . . ABBA BBAAAB. . . . . . 循环第2手 A (倒腾 (2, 2)->(2, 1)). . . . . . . ABBAB BBAAA. . . . . . 循环第3手 B (跳过 None->). . . . . . . ABBABA BBAA . . . . . . 循环第4手 A (倒腾 (2, 1)->(2, 2)). . . . . . . ABBABA BBAA . . . . . . 循环第5手 B (倒腾 (2, 1)->(2, 2)). . . . . . . ABBAB BBAAA. . . . . . 循环第6手 A (倒腾 (2, 1)->(2, 2)). . . . . . . ABBA BBAAAB. . . . . . 循环第7手 B (倒腾 (2, 1)->(2, 2)). . . . . . . ABB BBAAABA. . . . . . 循环第8手 A (跳过 None->). . . . . . . AB BBAAABAB. . . . . . 循环第9手 B (倒腾 (2, 2)->(2, 1)). . . . . . . AB BBAAABAB. . . . . . 循环第10手 A (倒腾 (2, 2)->(2, 1)). . . . . . . ABB BBAAABA. . . . . . 注:循环段棋盘的列宽随塔高变化,相邻两手之间列位置会有漂移,读图时以格点坐标为准。格内字母左到右为自底向顶。