数织游戏中的程序思维和数织的程序解法

数织游戏简介

数织是一种逻辑解谜游戏,拥有简单的规则,解谜的过程也富有趣味。
引用nonograms网站的游戏规则介绍:
游戏棋盘是一张正方形网格,其中的每个格子最终需要涂成黑色或标记为X。 棋盘每一行左边或每一列上方的数字表示该行或该列上每一组相邻的黑色方格的长度。 游戏目标是要找出所有的黑色方格。
规则粗看似乎有些云里雾里,但只要一开始游玩,或是观看他人完成一局,就能立刻上手这个游戏。
为了给出具体的印象,下面给出一个30x30大小的数织游戏网格:
30x30数织盘
为了辅助对游戏规则的理解,下面给出一张数织游戏的未完成图:
15x15残局
数织的规则正是其游戏性的精华所在,这个游戏与其说是挑战智商的游戏,倒不如说是一个挑战大脑运算速度和准确度的游戏。

数织中的程序思维

数织游戏的答案无法通过暴力的手段破解,因为这种手段的时间复杂度是指数级的。但游玩数织游戏的途中,玩家们无意中展现出的程序思维,却给数织的自动化解答提供了帮助。
数织有两个很有意思的特点:
一是确定性。所有方格的状态最终都必然能够依靠已知的线索确认,一旦确定,该状态就不会被新的线索所改变,即所有的线索都只指向唯一的答案。反例是:知名的游戏‘扫雷’的高级难度中,就经常会因为线索不足,在最后面临‘二选一’的拼运气局面;而一些解谜游戏中,也常常出现‘多解’的情况。这个特性使得数织的游戏进度只有前进而不会后退。(除非某个格子推理错误)
二是变量少。数织几乎没有什么强大的限制条件(譬如数独每行每列填入1~x数字的强大限制),而整个核心线索也只有简单的一些数字,这使得玩家只需要不停地轮番套入几个公式,就足以完成一场数织游戏。事实是,在玩家进行游戏时,的确总是有意无意地执行着几个核心推理公式来推进游戏。这使得机器完成游戏和玩家完成游戏的差异极少。
而正是这两个特点,使得数织游戏充满了程序思维,也使得它的解法可以很好且简单地用机器重现。唯一的区别是,玩家通常用自己的数学逻辑直觉,机器直接执行函数。
下面,我将自己游玩游戏时产生的逻辑直觉数学化,把数织游戏的攻略定理列出。某些定理没有经过严格的数学证明,纰漏业余之处,还请巨佬指正。

数织定理

定义1:网格中的每一格都拥有三种状态:空白、黑、叉。称空白状态的格子为‘空白格’,表示没有被确定为黑或叉的一种状态;称黑状态的格子为‘黑格’,表示已被确定为涂成黑色;称叉状态的格子为‘叉格’,表示已被确定画X。当所有的空白格转化为黑格与叉格时,游戏结束。
定义2:一组连续n个空白格称为‘空白条’,一组连续n个黑格称为‘连续条’,一组连续n个格子,其组成为:左右为叉格或边界,内部为空白格与黑格,称其为‘待完成条’,待完成条中不应有已确定的连续条。(n>=1)
定义3:若称一格子‘已确定’,则表明该格的状态可立刻转变为黑或叉;若称一连续条‘已确认’,则表明该连续条不会再增长,且与某一数字完成了对应。
定义4:将空白条中的一些格子转化为黑状态,形成连续条,该动作命名为‘放入’。
定义5:定义函数block(n): b l o c k ( n ) = { n , if  n ≥ 0 0 , if  n < 0 block(n)= \begin{cases} n, & \text {if $n\geq0$} \\ 0, & \text{if $n<0$} \end{cases} block(n)={n,0,if n0if n<0

定理1(同质定理):将网格的行与列交换、行与行交换、列与列交换,若干变化后,对于每行每列,数织所有定理依然适用;一个空白条可被分割为若干长度各异的空白条与连续条,对于每一个分割出的子条,数织所有定理依然适用。

定理2(驻格定理):对于k个长分别为 n 1 n_1 n1 n 2 n_2 n2,… n k n_k nk的连续条,于m长的空白条中放入,对于第L个连续条,必有 b l o c k ( ∑ i = 1 k n i + n L + k − 1 + m ) block(\sum _ { i = 1 } ^ { k } n_ i+n_L+k-1+m) block(i=1kni+nL+k1+m)个格子已确定。对于整个连续条,必有 b l o c k ( ( k + 1 ) ∑ i = 1 k n i + k ( k − 1 − m ) ) block((k+1)\sum _ { i = 1 } ^ { k } n_ i+k(k-1-m)) block((k+1)i=1kni+k(k1m))个格子已确定。

[推论]k=1时,特别的有 b l o c k ( 2 n − m ) block(2n-m) block(2nm)个格子已确认.
如何确定位置,及定理可视化说明:
驻格1
驻格2
定理3(坐落定理):欲在m长的待完成条中放入一长为n的连续条,此时空白条中已有一长为 n T n_T nT的连续条,左余L个空白格,右余R个空白格。

  • L + n T < n 或 R + n T < n L+n_T<n 或 R+n_T<n L+nT<nR+nT<n成立时,必有 m a x ( n − n T − L , n − n T − R ) max(n-n_T-L,n-n_T-R) max(nnTL,nnTR)个格子已确定。
  • L + n T < n 与 R + n T < n L+n_T<n 与 R+n_T<n L+nT<nR+nT<n同时成立,可直接用定理2判明。

[推论](终端延长定理):当一待完成条的末端已有一格被确定,则有一连续条被全部确定。
如何确定位置,及定理可视化说明:
在这里插入图片描述
定理4(超体-oversize定理):

  • m长的空白条欲放入n长的连续条,m<n,则称该空白条对于该连续条超体。
  • m长的待完成条欲放入n长的连续条,且已放入 n T n_T nT长的连续条,左余L个空白格,右余R个空白格,若 L + n T > n 或 R + n T > n L+n_T>n 或 R+n_T>n L+nT>nR+nT>n成立,则必有 b l o c k ( n + n T − L ) + b l o c k ( n + n T − R ) block(n+n_T-L)+block(n+n_T-R) block(n+nTL)+block(n+nTR)个格子于该连续条超体。
  • 一个已被确认的连续条,其相邻的空白格应直接转换为叉格。
  • 一行或一列中,某空白格或某空白条对于所有连续条超体,则该空格或该空白条的所属空白格应全部转换为叉格。
  • 一行或一列中,所有连续条已被填入完成,则剩余空白格应全部转换为叉格。
    如何确定位置,及定理可视化说明:
    在这里插入图片描述

定理5(不可连接定理):一列/行中存在一条以上的连续条,取其中相邻的两个连续条,其长分别为m,n,中间相隔l个空白格。设该行/列未被确认的最大数字为max,若m+n+l>max,且m+l<max或n+l<max成立,则两个连续条不可被划分入相同的待完成条,且与该连续条相邻的空白格,不应被划分入相邻连续条存在的待完成条。
定理6(确认定理):当一连续条的长度与同行/列的某一数字相匹配时,进行如下判断,若为有一条为真,则立刻确认。

  • 最大匹配:在所有待匹配数字中,该连续条所匹配的数字是唯一最大的。
  • 唯一匹配:该连续条无法扩大(即两侧为叉格或边界),且匹配的数字是该行/列唯一的。
  • 边缘匹配:在所有待匹配数字中,该连续条所匹配的数字在最左/最右,该连续条的左方/右方没有黑格,且左方/右方的所有空白条长度小于该连续条。

数织的程序解法

想要通过程序解出数织游戏的解,一种思路就是将玩家逻辑简化,抛弃一些复杂的逻辑,把总结性的定理写成函数,循环执行,得出最后的解;另一种思路,则是依靠定理,对暴力破解手段进行大幅度剪枝。这里我们选择的是第一种思路,因为暴力手段再怎么剪枝,也不会变成线性复杂度。而我们知道,只要人类不出错,完成一局数织所需要的时间,与网格的数量大致是成线性关系的。
现在,我对其中的一些问题进行解答,并对解法程序化提出新的问题。
Q:为什么可以抛弃一些复杂的逻辑,这样还能得出最终解吗?
A:之前说过,数织有一特点是确定性,即遵守游戏规则、依靠逻辑推理得出的格子必定是对的。

Q:如何进行循环执行?
A:一开始,对于每行每列都执行定理2化成的函数,对整个网格进行初始化。此后,对于每行每列循环执行定理3、4、5,且每次执行完,都应执行定理6。当所有数字被匹配后,即可认为程序结束,但此时还应再对每行每列执行一次定理4,即可将剩下的所有空白格转化为叉格,将网格填满。

现在的问题是,仅靠我们上面推出的定理,想要直接转化为函数,然后循环得出答案,是无法成功的。因为想要完成数织的程序解法,我们还需要解决另外一些问题。这些问题在人脑看来是非常容易解决的,而想要依靠机器实现,却还需要编程者大量的思考。那就是:各个待完成条如何分割,才能得出最好的结果?对于各个条的细分,又该如何储存?在黑格、连续条对数字的匹配中,是否要加上特殊的记号以避免一些错误?确定机制太过复杂,如何简化?
此外,还涉及到许多相关的工作。对于细节部分,以及如何完成一个数织的自动解答程序,本人以后将专门再写一篇博客,提出自己的想法。

后记:这个博客的主要想法是在半年前诞生的,当时沉迷于杀时间的数织游戏,甚至为之写出了一些攻略。最近国庆假期,不经意间翻出了当初的草稿,于是花时间写出了这篇博客。事实上,本人的专业水平(不论是数学还是计算机)都算不上高,在撰写博客的途中,也发现了许多的错误。在写定理时,突然发现半年前的自己推出的定理并不完整,于是花时间完善了定理,但事实上如今的状态已经不入以往(数织退坑挺久了),因此难免有所错漏。如果有时间,自然会写出实现程序的博客,当然,下一篇的技术含量肯定比这篇高多了。

  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值