欢迎光临散文网 会员登陆 & 注册

【TIS-100 攻略】TIS-NET 第 15 关:字符终端

2022-11-08 13:14 作者:ココアお姉ちゃん  | 我要投稿

本文首发于 B 站《TIS-100》文集(https://www.bilibili.com/read/readlist/rl626023)。原创不易,转载请注明出处。

TIS-NET 第 15 关《字符终端》(Character Terminal)关卡展示

本关要求将读入的数字映射成一个 2x2 的字符画打印在屏幕上。要求读到 0 时强制换行,读到 1~4 时按照下面的规则在画布上画出一个字符画:

按照上一关的思路,本关我们很容易想到一种将输入量映射成四个颜色的值的打表法。但问题在于,本关的每个输入量都对应着 4 个输出量,简单粗暴地打表的话,需要 4x4=16 行代码,这还没有算上额外的无条件跳转带来的开销,一张表根本写不下。因此,本关必须要通过找规律的方法减少表的大小。我们注意到,这四个字符画有这样的规律:

  1. 仅当输入量为 3 时,左上角的像素才为 0(黑色),其余时候都为 3(白色);

  2. 仅当输入量大于 2 时,右上角的像素才为 3(白色),其余时候都为 0(黑色);

  3. 仅当输入量为 2 时,下方的两个像素才为 0、3(黑色、白色),其余时候都为 3、0(白色、黑色)。

我们按照这样的规律,依次提供对应字符画的四个像素的颜色,同时再特殊处理一下换行,本题就完成了。代码如下:

1 号节点把收到的 in 发给 3 号节点(mov up down)。然后我们来观察 3 号节点:

  1. 3 号节点收到 in 后(mov up acc),检查收到的数字是否为 0,

  2. 若不为 0 则跳到第 5 行执行(jnz 5)。

  3. 收到的数字为 0 时,要做的事情非常简单,给 4 号节点传一个 9 信号,强制让 4 号节点换行就结束了(mov 9 right)

  4. (jmp 1)

  5. 收到的数字不为 0 时,首先给右边发送一个 1 信号(mov 1 right),然后根据以上规律计算出字符画左上角的颜色。

  6. 这里判断 in-3(sub 3)是否为 0,

  7. 成立则按顺序执行,不成立则跳到第 10 行执行(jnz a)。

  8. 判断成立时,按照规律,左上角的颜色是 0(黑色),我们将这个 0 传给 5 号节点(mov 0 down)

  9. (jmp b)

  10. 判断不成立时,按照规律,左上角的颜色是 3(白色),我们将这个 3 传给 5 号节点(mov 3 down)。

  11. 最后,将 in-3 的值传给下方,由下方继续计算剩余 3 个像素的颜色(mov acc down),

  12. 并给 4 号节点再发送一个 1 信号(mov 1 right)。

然后看 3 号节点下方的 5 号节点:

  1. 5 号节点收到了 3 号节点发来的左上角颜色后,将其传给 6 号节点(mov up right)。

  2. 接下来计算剩下 3 个像素的颜色。由于剩下 3 个颜色都是和 2 这个输入量比大小的,所以我们将传来的 in-3 改为 in-2(mov 1 acc)

  3. (add up)

  4. 首先是右上角的颜色:当输入量大于 2 时,跳到第 7 行执行(jgz 7)。

  5. 当输入量小于等于 2 时,右上角的颜色是 0(黑色)(mov 0 right)

  6. (jmp 8)

  7. 当输入量大于等于 2 时,右上角的颜色是 3(白色)(mov 3 right)。

  8. 然后是下方两个像素的颜色:当输入量不为 2 时,跳到第 12 行执行(jnz c)。

  9. 当输入量为 2 时,下方的两个像素依次为 0、3(黑色、白色)(mov 0 right)

  10. (mov 3 right)

  11. (jmp 1)

  12. 当输入量不为 2 时,下方的两个像素依次为 3、0(白色、黑色)(mov 3 right)

  13. (mov 0 right)至此,我们就将一个字符画里四个像素的颜色全部传给了右边的 6 号节点。

然后我们看一下 4 号节点。4 号节点是用来控制画笔的 (x, y) 坐标的。它的 acc 用来存储实时的 x 坐标,bak 用来存储实时的 y 坐标,初始值均为 0。

  1. 它首先会监听 3 号节点的信号(jro left)。

  2. 3 号节点发来 1 时,表示正常绘制。而且因为一个字符画有两行,所以每画一个字符画,3 号节点都会发来两个 1。此时我们将当前的 x(mov acc down)

  3. 和 y 坐标(swp)

  4. 发给下方的 6 号节点(mov acc down),

  5. 然后从上方的 2 号节点接收增量 Δy 和 Δx,加到 y 和 x 中(add up)

  6. (swp)

  7. (add up)这里的 2 号节点用了上一关的思想:用增量 Δy 和 Δx 来控制画笔坐标的移动。由于每个字符画都是 2x2 大小,因此画完第一行后,画笔向下移动一行,(Δx, Δy) = (0, 1);画完第二行后,画笔向上移动一行,向右移动三格,(Δx, Δy) = (3, -1)。如下图所示:

画第一个字符画的首行两个像素,画笔在 (0, 0) 处
画第一个字符画的末行两个像素,画笔在 (0, 1) 处,增量为 Δy = 1, Δx = 0
画第二个字符画的首行两个像素,画笔在 (3, 0) 处,增量为 Δy = -1, Δx = 3
画第二个字符画的末行两个像素,画笔在 (3, 1) 处,增量为 Δy = 1, Δx = 0

2 号节点正是提供这两组增量的无限流。画笔切换到当前字符画的第二行时,Δy = 1,Δx = 0(mov 1 down, mov 0 down);画笔切换到下一个字符画的位置时,Δy = -1,Δx = 3(mov -1 down, mov 3 down)。4 号节点也正是每画完一行就从 2 号无限流中取两个增量值加到 x, y 中,得到新的 x, y 值。

8. 4 号节点在改变了 x, y 值后,需要判断是否触发了换行。这里判断 x 是否等于 30,即 x - 30(sub 30)是否等于 0。

9. 不等于 0 时,说明 x 还没有到达行尾的 30 处,跳到第 14 行,加回一个 30,将 x 还原,准备再次落笔(jnz e, add 30);

10. 若 x - 30 等于 0,说明 x 到达了行尾的 30 处,此时要触发换行:将 y 加上 3(swp)

11. (add 3)

12. (swp)

13. 将 x 清零(mov -30 acc)

14. (add 30)这里之所以使用 -30 + 30 这样迂回的方式清零,是为了未触发换行时能够经由第 9 行的 jnz e 复用这行 add 30 的代码

另外要注意一下,3 号节点在收到 0 时,会给我们发一个 9,让我们强制换行。4 号节点里的第 10~14 行代码正是用于换行的,正好位于第一条 jro 指令下方 9 行的位置,所以 3 号节点发送 9 让 4 号节点强制换行。

最后是 6 号画图节点:

  1. 首先从 4 号节点收取 x, y 坐标(mov up down)

  2. (mov up down)

  3. 然后从 5 号节点收取当前字符画的当前行的两个像素的颜色(mov left down)

  4. (mov left down)

  5. 将这些数字依次发给 image 后,发送一个 -1 结束本次绘制(mov -1 down)。如此循环,直到把画布画满要求的字符画为止。

点击左下角的【RUN】,稍等片刻,便会弹出结算界面:


【TIS-100 攻略】TIS-NET 第 15 关:字符终端的评论 (共 条)

分享到微博请遵守国家法律