「こんなきれいな星も、やっぱりここまで来てから、見れたのだと思うから。だから・・もっと遠くへ・・」

一个很有趣的编程游戏 Manufactoria

地址 http://www.matrix67.com/data/manufactoria.swf

忽略细节的话,游戏就是一个图灵机的变种,游戏目标是识别出某类字符串(比如以蓝色开头的字符串)或者对某个字符串进行某种操作(比如将字符串反色)。
输入串只有红蓝两种字符(某些题目有绿色),我们可以用红蓝黄绿四种字符,机器有一个读取头和一个写入头,不能移动,读取头只能读取串的第一个字符,读完后字符自动被吃掉(没有办法只读取不吃掉字符),写入头只能在串最后写入字符(颜色任意)。
整个机器必须被排在限制大小的方格棋盘上,控制部件之间用传送带连接(各种控制部件和传送带都占一格)。为了避免歧义,部件不能重叠,惟一的例外是传送带可以垂直交叉(交叉时优先走平行方向传送带),但反向交叉也是不可以的。

首先发现给定的这个机器是图灵完备的,因为我们可以用黄色字符当“标记”,先在串末尾加一个黄色字符,这样整个串就有序了。然后图灵机的基本操作我们都能用给定的部件完成(比如左移读写头可以通过当前位置插入一个绿标记,然后把整个序列循环一遍,一边读一边写,同时把前一个字符是啥用状态记住,读到绿字符把前一个字符视为当前字符就行了)。

这样的话游戏的意义就是怎么在限制大小的棋盘上把机器实现出来了…… 也就是怎么比较简单地实现出来而不是直接实现一台电脑……

前面若干关都是正则语言,显然给的机器可以直接实现一个DFA…… 就略过了。

一些好玩的关卡:
Androids: 第一个有难度的关卡,识别形如R^nB^n的串。思路很简单,加一个黄色字符作为标记,这样整个串就有序了。每次读取第一个和最后一个字符,看看是不是R和B,然后把它们删掉。

Robotanks: 蓝色视为1,红色视为0。识别所有作为二进制数时数值大于15的串(可以有前导零)。先去掉前导零,然后就是看是否有至少5位了。

Robo-children: 识别蓝色和红色字符数目相同的串。每次去掉一个红色一个蓝色即可。Politicians那题是识别蓝色是红色两倍的串,做法相同。

Officers: 蓝色视为1,红色视为0。给这个二进制数+1,输出结果。这题比较坑…… 首先注意到答案是允许前导零的…… 为了减少特判,我们直接在前面加两个零…… 然后同样用黄色标记末尾,然后再用一个绿字符标记当前正在处理的位(一开始在最后一位)。这样的话每次扫串,记录前一个字符,读到绿色字符时候,如果前一个字符是0,把它改成1结束,如果是1,把它改成0,然后把绿色字符和它交换,这样反复做就行了。

Generals: +1变成-1,其他和上题一样。做法也和上题一样。

Police: 保证给定串长度为偶数,在串的正中间加一个黄色字符。这题我做法复杂了。我的做法是先把每两个字符之间都加上黄色(显然有奇数个黄色),然后每次删掉第一个和最后一个黄色,直到剩一个为止。最后一个黄色可以通过它后面的第二个字符是否是黄色判定,所以只要记一个字符就行。但细节一大堆,比如空串或者只有两个字符的情况,终止条件也略麻烦,各种挤压才在棋盘里撑下。更简单的方法见下。

Teachers: 识别形如B^nR^nB^n的串。先检查形状(是否是B^aR^bB^c的形式),在确定形状没问题后,加一个黄色字符确定次序,然后每次删掉第一个碰到的蓝色、第一个碰到的红色、第一个碰到的蓝色即可。任何一步找不到就是挂了。

Rocket planes: 对给定串排序,把所有蓝色字符放到最前面。还是加一个字符定序,然后反复扫,每次额外打一个蓝色,然后下次遇到的第一个蓝色就可以吃掉了。如果没有蓝色就吃掉第一个蓝色(这是刚才额外打的)然后结束。

Judiciary: 保证给定串长度是偶数,识别所有形如xx的串,其中x是任意字符串。识思路是首先想办法在正中间加一个绿色字符,然后反复扫,每次读第一个字符和绿色后第一个字符,比较,不一样就挂,一样就删掉。在正中间加一个字符的一个更简单的方法是,先在首尾各加一个绿字符,然后每次开头的绿字符向后移一个位置,最后的绿字符向前移一个位置,相遇为止。这个比之前那个方法好写多了。

Academics: 将给定串反序输出。做法是先黄字符定序,然后用一个绿字符标记已经翻转的部分。每次找到绿字前的最后一个字符,把它和绿字交换。

Engineers: 识别所有回文串。每次取出第一个和最后一个字符,比较,不一样就挂,一样就删掉。

Ophanim(隐藏关): 给定两个二进制数(依然蓝1红0,用一个绿字符分隔),如果第一个数比第二个数大,就接受。 这个题比较猎奇,虽然有很多描述起来很简单的做法,但实际用给定的那个机器实现的话很容易就发现棋盘不够大了…… 我用的方法是先去掉前导零,然后直接从前向后进行比较,在发现第一个不同后再比长度。

Metatron(隐藏关): 给定两个二进制数(表示方法与上题相同),输出它们的和。不会。试了很多方法,都没法压缩到放得进给定的13x13棋盘内……