【TIS-100 攻略】第 20 关:序列检索器

本文首发于 B 站《TIS-100》文集(https://www.bilibili.com/read/readlist/rl626023)。原创不易,转载请注明出处。
第 20 关《序列检索器》(Sequence Indexer)关卡展示

本关的 IN.V 会给你 10 个数字,最后的 0 是序列末端标志。然后你每收到一个 IN.X 里的数字,就需要向 OUT 端口输出,IN.V 里的第 X 个数是多少。这里的位置是从 0 开始的,比如,当你从 IN.V 收到 [860, 215, 230, 784, 900, 978, 945, 124, 432, 697, 0] 时,第 0 个数字是 860,第 1 个数字是 215,依此类推,直到最后的第 9 个数字为 697。
这里我先抛出一个结论:当栈里有 n 个数字,且你要取第 x 个数字(和题目一样,x 从 0 开始到 n-1 结束),那么你需要弹 n-x 次栈才能取到想要的数。如下图所示:当一个栈里有 8 个数字,你要取得第 3 个数字时,需要弹 5 次栈才能得到:






由此可得,弹栈次数跟 n 和 x 两个量相关。本关的 n 固定为 10,那么对于每个 x,弹栈的次数都为 10-x。本关的代码如下:

IN.V 下方的节点用于将 IN.V 提供的所有数字全部压入栈中,包括最后的 0(mov up right)。所有数字均压入栈中后,该节点会的任务就完成了,我们即可放任该节点被卡住。
IN.X 下方的节点首先执行了一定次数的空循环,让 acc 从 11 数到 0(mov 11 acc, sub 1, jnz 2)。这是为了等待左半边把 IN.V 里提供的所有数字都放入栈中。我试出来的最少空循环次数是 11 次。空循环完毕后,我们将最后放入栈中的哨兵 0 弹出(mov left acc)。接下来,这个节点不断从 IN.X 中读取 x 的值往下发(mov up down)。注意发完 x 后要跳回第 5 行,不能放任代码跳回到第 1 行的空循环处(jmp 5)。
中央靠右的节点两个任务:一是将收到的 X 传给正中央的节点(mov up left),另一个是把从正中央节点得到的对应值发给下面的节点(mov left down)。
正中央的节点首先计算 10-x 的值,这便是我们需要弹栈的次数(mov 10 acc, sub right)。我们需要先弹出 10-x 个数字,待取到对应值后,我们还需要将这 10-x 个值放回去。因此我们要执行两遍 10-x 次的循环,我们需要将这个循环次数额外复制一份到 bak 里(sav)。接下来我们就开始弹栈了:每从上方的栈弹出一个数字,都将弹出的数字放到下方的栈里暂存(mov up down),同时令弹栈的剩余次数减 1(sub 1)。剩余次数尚未减到 0 时,跳回到第 4 行继续弹(jnz 4),直到剩余次数为 0 时,我们就成功地将 10-x 个数放到了下方的栈里。
此时,下方栈里的第一个数就是我们需要发送给 OUT 的数,将它取出(mov down acc),发给右边后(mov acc right)再放回原处(mov acc down)。得到本次的答案后,我们要将下方栈里的 n-x 个数原封不动地放回去。我们将之前备份到 bak 里的 n-x 重新拿出来(swp),然后开始将下方的 n-x 个数归位。每从下方弹一个数回去(mov down up),就令剩余次数减 1(sub 1),并判断剩余次数是否到达了 0。尚未到达 0 时,跳回第 11 行继续弹(jnz b),直到剩余次数归零为止。
OUT 上方的节点没啥说的,它会从中央靠右的节点收到所有的输出值,只要来者不拒,通通送往 OUT 口就完事了(mov up down)。
点击左下角的【RUN】,稍等片刻,便会弹出结算界面:
