【TIS-100 攻略】TIS-NET 第 15 關(guān):字符終端

本文首發(fā)于 B 站《TIS-100》文集(https://www.bilibili.com/read/readlist/rl626023)。原創(chuàng)不易,轉(zhuǎn)載請(qǐng)注明出處。
TIS-NET 第 15 關(guān)《字符終端》(Character Terminal)關(guān)卡展示

本關(guān)要求將讀入的數(shù)字映射成一個(gè) 2x2 的字符畫打印在屏幕上。要求讀到 0 時(shí)強(qiáng)制換行,讀到 1~4 時(shí)按照下面的規(guī)則在畫布上畫出一個(gè)字符畫:

按照上一關(guān)的思路,本關(guān)我們很容易想到一種將輸入量映射成四個(gè)顏色的值的打表法。但問(wèn)題在于,本關(guān)的每個(gè)輸入量都對(duì)應(yīng)著 4 個(gè)輸出量,簡(jiǎn)單粗暴地打表的話,需要 4x4=16 行代碼,這還沒(méi)有算上額外的無(wú)條件跳轉(zhuǎn)帶來(lái)的開銷,一張表根本寫不下。因此,本關(guān)必須要通過(guò)找規(guī)律的方法減少表的大小。我們注意到,這四個(gè)字符畫有這樣的規(guī)律:
僅當(dāng)輸入量為 3 時(shí),左上角的像素才為 0(黑色),其余時(shí)候都為 3(白色);
僅當(dāng)輸入量大于 2 時(shí),右上角的像素才為 3(白色),其余時(shí)候都為 0(黑色);
僅當(dāng)輸入量為 2 時(shí),下方的兩個(gè)像素才為 0、3(黑色、白色),其余時(shí)候都為 3、0(白色、黑色)。
我們按照這樣的規(guī)律,依次提供對(duì)應(yīng)字符畫的四個(gè)像素的顏色,同時(shí)再特殊處理一下?lián)Q行,本題就完成了。代碼如下:

1 號(hào)節(jié)點(diǎn)把收到的 in 發(fā)給 3 號(hào)節(jié)點(diǎn)(mov up down)。然后我們來(lái)觀察 3 號(hào)節(jié)點(diǎn):
3 號(hào)節(jié)點(diǎn)收到 in 后(mov up acc),檢查收到的數(shù)字是否為 0,
若不為 0 則跳到第 5 行執(zhí)行(jnz 5)。
收到的數(shù)字為 0 時(shí),要做的事情非常簡(jiǎn)單,給 4 號(hào)節(jié)點(diǎn)傳一個(gè) 9 信號(hào),強(qiáng)制讓 4 號(hào)節(jié)點(diǎn)換行就結(jié)束了(mov 9 right)
(jmp 1)
收到的數(shù)字不為 0 時(shí),首先給右邊發(fā)送一個(gè) 1 信號(hào)(mov 1 right),然后根據(jù)以上規(guī)律計(jì)算出字符畫左上角的顏色。
這里判斷 in-3(sub 3)是否為 0,
成立則按順序執(zhí)行,不成立則跳到第 10 行執(zhí)行(jnz a)。
判斷成立時(shí),按照規(guī)律,左上角的顏色是 0(黑色),我們將這個(gè) 0 傳給 5 號(hào)節(jié)點(diǎn)(mov 0 down)
(jmp b)
判斷不成立時(shí),按照規(guī)律,左上角的顏色是 3(白色),我們將這個(gè) 3 傳給 5 號(hào)節(jié)點(diǎn)(mov 3 down)。
最后,將 in-3 的值傳給下方,由下方繼續(xù)計(jì)算剩余 3 個(gè)像素的顏色(mov acc down),
并給 4 號(hào)節(jié)點(diǎn)再發(fā)送一個(gè) 1 信號(hào)(mov 1 right)。
然后看 3 號(hào)節(jié)點(diǎn)下方的 5 號(hào)節(jié)點(diǎn):
5 號(hào)節(jié)點(diǎn)收到了 3 號(hào)節(jié)點(diǎn)發(fā)來(lái)的左上角顏色后,將其傳給 6 號(hào)節(jié)點(diǎn)(mov up right)。
接下來(lái)計(jì)算剩下 3 個(gè)像素的顏色。由于剩下 3 個(gè)顏色都是和 2 這個(gè)輸入量比大小的,所以我們將傳來(lái)的 in-3 改為 in-2(mov 1 acc)
(add up)
首先是右上角的顏色:當(dāng)輸入量大于?2 時(shí),跳到第 7 行執(zhí)行(jgz 7)。
當(dāng)輸入量小于等于 2 時(shí),右上角的顏色是 0(黑色)(mov 0 right)
(jmp 8)
當(dāng)輸入量大于等于 2 時(shí),右上角的顏色是 3(白色)(mov 3 right)。
然后是下方兩個(gè)像素的顏色:當(dāng)輸入量不為 2 時(shí),跳到第 12 行執(zhí)行(jnz c)。
當(dāng)輸入量為 2 時(shí),下方的兩個(gè)像素依次為 0、3(黑色、白色)(mov 0 right)
(mov 3 right)
(jmp 1)
當(dāng)輸入量不為 2 時(shí),下方的兩個(gè)像素依次為 3、0(白色、黑色)(mov 3 right)
(mov 0 right)至此,我們就將一個(gè)字符畫里四個(gè)像素的顏色全部傳給了右邊的 6 號(hào)節(jié)點(diǎn)。
然后我們看一下 4 號(hào)節(jié)點(diǎn)。4 號(hào)節(jié)點(diǎn)是用來(lái)控制畫筆的 (x, y) 坐標(biāo)的。它的 acc 用來(lái)存儲(chǔ)實(shí)時(shí)的 x 坐標(biāo),bak 用來(lái)存儲(chǔ)實(shí)時(shí)的 y 坐標(biāo),初始值均為 0。
它首先會(huì)監(jiān)聽 3 號(hào)節(jié)點(diǎn)的信號(hào)(jro left)。
3 號(hào)節(jié)點(diǎn)發(fā)來(lái) 1 時(shí),表示正常繪制。而且因?yàn)橐粋€(gè)字符畫有兩行,所以每畫一個(gè)字符畫,3 號(hào)節(jié)點(diǎn)都會(huì)發(fā)來(lái)兩個(gè) 1。此時(shí)我們將當(dāng)前的 x(mov acc down)
和 y 坐標(biāo)(swp)
發(fā)給下方的 6 號(hào)節(jié)點(diǎn)(mov acc down),
然后從上方的 2 號(hào)節(jié)點(diǎn)接收增量 Δy 和 Δx,加到 y 和 x 中(add up)
(swp)
(add up)這里的 2 號(hào)節(jié)點(diǎn)用了上一關(guān)的思想:用增量?Δy 和 Δx 來(lái)控制畫筆坐標(biāo)的移動(dòng)。由于每個(gè)字符畫都是 2x2 大小,因此畫完第一行后,畫筆向下移動(dòng)一行,(Δx, Δy) = (0, 1);畫完第二行后,畫筆向上移動(dòng)一行,向右移動(dòng)三格,(Δx,?Δy) = (3, -1)。如下圖所示:




2 號(hào)節(jié)點(diǎn)正是提供這兩組增量的無(wú)限流。畫筆切換到當(dāng)前字符畫的第二行時(shí),Δy = 1,Δx = 0(mov 1 down, mov 0 down);畫筆切換到下一個(gè)字符畫的位置時(shí),Δy = -1,Δx = 3(mov -1 down, mov 3 down)。4 號(hào)節(jié)點(diǎn)也正是每畫完一行就從 2 號(hào)無(wú)限流中取兩個(gè)增量值加到 x, y 中,得到新的 x, y 值。
8. 4 號(hào)節(jié)點(diǎn)在改變了 x, y 值后,需要判斷是否觸發(fā)了換行。這里判斷 x 是否等于 30,即 x - 30(sub 30)是否等于 0。
9. 不等于 0 時(shí),說(shuō)明 x 還沒(méi)有到達(dá)行尾的 30 處,跳到第 14 行,加回一個(gè) 30,將 x 還原,準(zhǔn)備再次落筆(jnz e, add 30);
10. 若 x - 30 等于 0,說(shuō)明 x 到達(dá)了行尾的 30 處,此時(shí)要觸發(fā)換行:將 y 加上 3(swp)
11. (add 3)
12. (swp)
13. 將 x 清零(mov -30 acc)
14. (add 30)這里之所以使用 -30 + 30 這樣迂回的方式清零,是為了未觸發(fā)換行時(shí)能夠經(jīng)由第 9 行的 jnz e 復(fù)用這行 add 30 的代碼。
另外要注意一下,3 號(hào)節(jié)點(diǎn)在收到 0 時(shí),會(huì)給我們發(fā)一個(gè) 9,讓我們強(qiáng)制換行。4 號(hào)節(jié)點(diǎn)里的第 10~14 行代碼正是用于換行的,正好位于第一條 jro 指令下方 9 行的位置,所以 3 號(hào)節(jié)點(diǎn)發(fā)送 9 讓 4 號(hào)節(jié)點(diǎn)強(qiáng)制換行。
最后是 6 號(hào)畫圖節(jié)點(diǎn):
首先從 4 號(hào)節(jié)點(diǎn)收取 x, y 坐標(biāo)(mov up down)
(mov up down)
然后從 5 號(hào)節(jié)點(diǎn)收取當(dāng)前字符畫的當(dāng)前行的兩個(gè)像素的顏色(mov left down)
(mov left down)
將這些數(shù)字依次發(fā)給 image 后,發(fā)送一個(gè) -1 結(jié)束本次繪制(mov -1 down)。如此循環(huán),直到把畫布畫滿要求的字符畫為止。
點(diǎn)擊左下角的【RUN】,稍等片刻,便會(huì)彈出結(jié)算界面:
