【TIS-100 攻略】TIS-NET 第 8~9 關(guān):第二大數(shù)字選擇器、十進(jìn)制數(shù)位分解器

本文首發(fā)于 B 站《TIS-100》文集(https://www.bilibili.com/read/readlist/rl626023)。原創(chuàng)不易,轉(zhuǎn)載請注明出處。
TIS-NET 第 8 關(guān)《第二大數(shù)字選擇器》(Submaximum Selector)關(guān)卡展示

TIS-NET 系列的前 7 關(guān)還是比較容易的,這一關(guān)遇到的是第一個比較難的挑戰(zhàn)。本關(guān)你需要將 IN.A ~ IN.D 這四路輸入里第二大的數(shù)字給找出來并輸出。
我們設(shè)找到的第一大數(shù)字為 X,第二大數(shù)字為 Y。那么我們需要做的事情就是計算出 Y 是多少,并輸出。C 語言代碼如下:
將以上 C 語言代碼轉(zhuǎn)換成 TIS-100 代碼,如下:

1 號節(jié)點將 a 向右發(fā)送兩遍,以便 b 和 a 能夠比較大?。╩ov up acc, mov acc right, mov acc right)。2 號節(jié)點用來比較 a 和 b 的大小,然后令 x = 較大者,y = 較小者。邏輯如下:
2 號節(jié)點拿到 b 后(mov up acc),
首先將其復(fù)制一份到 bak 中(sav),
然后計算 b-a 的值(sub left)。
b-a <= 0 時按順序執(zhí)行,b-a > 0 時跳到第 8 行執(zhí)行(jgz 8)。
這里我們選擇在 bak?里放入 x,在 acc 里放入 y。當(dāng) b-a <= 0 時,x=a,y=b,此時從左邊讀入 a(mov left acc)
然后發(fā)現(xiàn),acc = a, bak = b。這時候我們要將原先放在 bak 里的 b 換到 acc 里(swp)
(jmp 9)
當(dāng) b-a > 0 時,x=b,y=a,此時正常將 a 放入 acc 中,bak 里的數(shù)不變(mov left acc)。
完成后,我們先將 acc 里的 y 向右發(fā)送兩遍(mov acc right)
(mov acc right)
再將 bak 里的 x 向右發(fā)送一遍(swp)
(mov acc right)。
接下來是 3 號節(jié)點:
3 號節(jié)點拿到 c 后(mov up acc),
首先計算 c-y 的值(sub left)。
c-y <= 0 時按順序執(zhí)行,c-y > 0 時跳到第 6 行執(zhí)行(jgz 6)。
c-y <= 0 時,說明?c <= y,c 沒有對前兩名構(gòu)成威脅,此時直接將 acc 覆蓋為左邊二次提供的原始 y(mov left acc)
(jmp 7)
c-y > 0 時,說明 c > y,c 至少是前兩名之一,原始 y 出局,此時令 acc 加回一個原始 y,將 c-y 變回成 c(add left)。
3 號節(jié)點里至此已經(jīng)寫不下 c?繼續(xù)跟 x 比大小的邏輯了,但我們知道 x 一定是前兩名之一,所以我們將原始的 x 向右發(fā)一遍(mov left right),
將?y 的值向右發(fā)兩遍(mov acc right)
(mov acc right)讓右邊的 4 號節(jié)點來完成這個【給前兩名排序】的工作。
4 號節(jié)點首先將收到的 d 值向下傳,接下來的 2~9 行代碼跟 2 號節(jié)點大同小異,就是比較 3 號節(jié)點發(fā)來的兩個數(shù)的大小,確定最新的 x 和 y 的值,其中第二個數(shù)會發(fā)兩遍(mov left acc, sav, sub left, jgz 9, mov left acc, swp, jmp a, mov left acc)。我就不再解釋了。第 10~14 行,我們將得到的最新的 y 值和 x 值各向下發(fā)兩遍(mov acc down, mov acc down, swp, mov acc down, mov acc down)。
5 號和 6 號節(jié)點純粹給 7 號節(jié)點傳話(mov up left, mov right down)。
7 號節(jié)點會依次收到一次 d、兩次 y 和兩次 x。這個節(jié)點要在 x、y 和 d 中找到第二大的數(shù),邏輯如下:
首先計算 d-y 的值(mov up acc)
(sub up)
然后判定:d-y <= 0 時按順序執(zhí)行,d-y > 0 時跳到第 6 行執(zhí)行(jgz 6)。
d-y <= 0 時,說明 d <= y,沒有對前兩名構(gòu)成威脅,輸出的值是原始 y(mov up acc)。
(jmp 7)這里要注意:由于上方會固定發(fā)送一次 d、兩次 y、兩次 x,所以即使我們已經(jīng)提前得出結(jié)論了,也仍然要和接下來的 x 白白比較一次,以便把最后兩次 x 消耗掉。這就是為什么這里寫的是 mov up acc, jmp 7 而不是 mov up down, jmp 1 的原因。
d-y > 0 時,說明 d > y,d 至少是前兩名之一,原始 y 出局,此時令 acc 加回一個原始 y,將 d-y 變回成 d,作為新的 y(add up)。
這時候這個新的 y(或者原始的 y)還要再跟 x 比一次,計算 y-x 的值(sub up)。
若 y-x > 0,跳到第 12 行執(zhí)行(jgz c)
若 y-x <= 0,說明這個新的 y 是第二名,我們令 acc 加回一個 x(add up),
將 y-x 變回成 y 后輸出(mov acc down)。
(jmp 1)
若 y-x > 0,說明這個新的?y?是第一名,而原始的 x 是第二名,我們要輸出 x 的值(mov up down)。
點擊左下角的【RUN】,稍等片刻,便會彈出結(jié)算界面:


TIS-NET 第 9 關(guān)《十進(jìn)制數(shù)位分解器》(Decimal Decomposer)關(guān)卡展示

本關(guān)要求分解 IN 中的數(shù)字,將讀入數(shù)字的百位、十位、個位分別寫入 OUT.X、OUT.Y 和 OUT.Z。當(dāng)讀入的數(shù)字不滿三位數(shù)時,沒有數(shù)字的高位視為 0。
本關(guān)的本質(zhì)是除法計算:首先將得到的數(shù)字除以 100,得到的商就是百位數(shù),然后將剩下的余數(shù)再除以 10,第二次得到的商就是十位數(shù),余數(shù)就是個位數(shù)。
本關(guān)的除法最多做 10 次運(yùn)算就能得出結(jié)論,線性查找和二分查找的效率相當(dāng),因此我就不提供二分查找的做法了。代碼如下:

上方兩個節(jié)點純傳話(mov up down, mov up down)。
IN 輸入在第二縱列,正對著 OUT.Y 這個出口。所以 OUT.Y 上方的節(jié)點收到 IN 后,先將這個數(shù)字傳給左邊的節(jié)點,讓它計算出百位數(shù)后,將剩下的余數(shù)再發(fā)給自己。現(xiàn)在我們將目光暫時移動到左邊:
左邊的節(jié)點收到 IN 后(mov right acc),開始做除以 100 的運(yùn)算。
每當(dāng)?acc 減去一個 100(sub 100),就判斷是否減到了負(fù)數(shù)。
減到了負(fù)數(shù)時,跳到第 8 行執(zhí)行(jlz 8);
尚未減到負(fù)數(shù)時,令 bak 加上 1(swp)
(add 1)
(swp)
然后跳回第 2 行繼續(xù)減(jmp 2),
直到減到了負(fù)數(shù)為止,將這個【多減了 100 的余數(shù)】傳給右邊(mov acc right)
并清除(sub acc)。
然后我們自己將 bak 里的商,即百位(swp)
發(fā)給下方的 OUT.X(mov acc down)。
然后回頭來看中間的節(jié)點:
中間的節(jié)點在把 IN 傳給左邊后(mov up?left),就一直在等待左邊傳來【多減了 100 的余數(shù)】。
收到左邊傳的【多減了 100 的余數(shù)】后,需要加上 100 才能開始做除以 10 的運(yùn)算。由于第一次運(yùn)算一定要減去 10,所以我們改為加上 90(mov 90 acc)
(add left)
這時候開始判斷 acc 是否減到了負(fù)數(shù)。減到了負(fù)數(shù)時,跳到第 10 行執(zhí)行(jlz a);
尚未減到負(fù)數(shù)時,令 bak 加上 1(swp)
(add 1)
(swp)
令 acc 繼續(xù)減 10(sub 10),
然后跳回第 4 行繼續(xù)判斷(jmp 4),
直到減到了負(fù)數(shù)為止,我們將【多減了 10 的余數(shù)】加回一個 10(add 10),
傳給右邊并清除(mov acc right)
并清除(sub acc),
然后我們自己將 bak 里的商,即十位(swp)
發(fā)給下方的 OUT.Y(mov acc down)。
右邊的節(jié)點收到左邊傳的余數(shù)后,直接將這個余數(shù)(個位)發(fā)給下方的 OUT.Z(mov left down)。
點擊左下角的【RUN】,稍等片刻,便會彈出結(jié)算界面:
