【深圳 IO 攻略】第 5 關(guān):游戲積分器

本文首發(fā)于 B 站《深圳 IO》文集(https://www.bilibili.com/read/readlist/rl569860)。原創(chuàng)不易,轉(zhuǎn)載請注明出處。
關(guān)卡展示

每當(dāng)?shù)梅中盘柍霈F(xiàn)時就 +1 分,犯規(guī)信號出現(xiàn)時就 -2 分,但分數(shù)不能低于 0 分。(得分和犯規(guī)信號不會同時出現(xiàn))按照上述規(guī)則隨時更新顯示屏上的分數(shù)。
根據(jù)以上規(guī)則,我們很容易設(shè)計出如下的算法:
得分信號出現(xiàn)時,令 acc +1;
犯規(guī)信號出現(xiàn)時,令 acc -2;
當(dāng) acc < 0 時,將 acc 置為 0;
執(zhí)行完以上操作后,將 acc 的值發(fā)送給顯示屏,然后休眠一秒進入下一個時鐘周期。
代碼如下:

點擊左下角的【模擬】,稍等片刻,便會彈出結(jié)算界面:

優(yōu)化電量
我們可以看到,這個初版方案的耗電量慘不忍睹。這是因為我們的代碼中有很多冗余操作:
得分和犯規(guī)信號不會同時為 100,所以 teq p0 100 和 teq p1 100 這兩條判斷至少有一條是不生效的,效率上有浪費。完全可以改為比較 p0 和 p1 的差值。
僅當(dāng)觸發(fā)了犯規(guī)信號時,分數(shù)才有可能低于 0。沒必要每個時鐘周期都判斷分數(shù)是否低于 0。
僅當(dāng)分數(shù)產(chǎn)生變化時才有必要將 acc 的值傳給顯示器,平時分數(shù)沒有變化的時候沒必要反復(fù)傳同樣的信號。
針對以上幾點,我們重新設(shè)計一套更優(yōu)的算法:
計算犯規(guī)信號(p0)和得分信號(p1)的差值;
當(dāng)差值為 0 時,當(dāng)前時鐘周期內(nèi)不做任何操作;
當(dāng)差值為 -100 時,說明得分信號激活,令 acc +1,然后將 acc 的值發(fā)送到顯示器;
當(dāng)差值為 +100 時,說明犯規(guī)信號激活,令 acc -2。與此同時,立刻判斷 acc 是否小于 0。若是,則將 acc 更新為 0。做完以上操作后,將 acc 的值發(fā)送到顯示器;
休眠一秒,進入下一個時鐘周期,如此循環(huán)。
改進后的代碼如下:

我在第 3 關(guān)的時候說過,測試指令前也是可以帶上 + - 號的。這段代碼里就出現(xiàn)了這樣的條件嵌套。這道題里,每一秒鐘可能出現(xiàn) 4 種不同的情況,依次如下:
p0 - p1 = 0;
p0 - p1 = -100(得分);
p0 - p1 = +100, acc - 2 >= 0(犯規(guī),但是沒有扣到 0 分以下);
p0 - p1 = +100, acc - 2 < 0(犯規(guī)且扣到了 0 分以下,強制還原成 0 分)。
我們現(xiàn)在對所有的情況依次討論:
p0 - p1 = 0,此時在執(zhí)行了 tcp p0 p1 指令后,所有帶 + - 前綴的指令都會被關(guān)閉,直接跳到最后一行【slp 1】(當(dāng)前時鐘周期內(nèi)不做任何操作)。此時的代碼看起來是這樣的:
p0 - p1 = -100,此時在執(zhí)行了 tcp p0 p1 指令后,帶 - 前綴的指令會被激活,接下來會執(zhí)行第 2 行的【-?add 1】、第 7 行的【-?mov acc x1】和第 8 行的【slp 1】指令(令?acc +1,然后將?acc 的值發(fā)送到顯示器)。此時的代碼看起來是這樣的:
p0 - p1 = +100 且 acc - 2 >= 0,此時在執(zhí)行了 tcp p0 p1 指令后,帶 + 前綴的指令會被激活。然后會執(zhí)行第 3 行的【+ sub 2】和第 4 行的【+ tlt acc 0】指令。接下來,由于 acc - 2 >= 0,所以執(zhí)行完 tlt 測試指令后,帶 + 前綴的指令會變?yōu)殛P(guān)閉狀態(tài),帶 - 前綴的指令會變成激活狀態(tài)。接下來直接執(zhí)行第 7 行的【-?mov acc x1】和第 8 行的【slp 1】指令(令 acc -2,然后將?acc 的值發(fā)送到顯示器)。此時的代碼看起來是這樣的:
p0 - p1 = +100 且 acc - 2 < 0,直到第 4 行前都和上一條無異。但是此時,因為 acc - 2 < 0,所以 tlt 測試指令執(zhí)行完后的結(jié)果是 + 前綴指令保持激活,- 前綴指令保持關(guān)閉。因此接下來執(zhí)行的是第 5 行的【+ mov 0 acc】、第 6 行的【+ mov acc x1】和第 8 行的【slp 1】(acc -2 后發(fā)現(xiàn)小于 0,將 acc 強制置零,然后將?acc 的值發(fā)送到顯示器)。此時的代碼看起來是這樣的:

耗電大幅減少到 170,可喜可賀。
附:一些簡單的邏輯嵌套
下面我給出一些簡單的邏輯嵌套定式,讀者可以嘗試自行推理證明。
所以以上代碼中:
兩條測試指令間構(gòu)成了【與】關(guān)系,“僅當(dāng) p0 - p1 > 0 且 acc - 2 <?0 時,才將 acc 置 0 并發(fā)送給 x1”,“只要以上有一條不滿足,那么就執(zhí)行 - 前綴的部分,只要將 acc 發(fā)送給 x1 就可以了,不需要將 acc 置零。其中若 p0 - p1 < 0 時還要額外執(zhí)行一條 add 1 指令”。