【深圳 IO 攻略】第 31 關(guān):安全網(wǎng)追蹤徽章

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

這一關(guān)我們需要結(jié)合數(shù)據(jù)手冊(cè)來(lái)分析

我們首先看【同步雷達(dá)】信號(hào),【同步雷達(dá)】的值是用于指示雷達(dá)當(dāng)前所在方位的。我們視正北方向?yàn)?0°,則【同步雷達(dá)】的值和方位角度呈如下對(duì)應(yīng)關(guān)系:0 = -30°~30°,20 = 30°~90°,40 = 90° ~ 150°,60 = 150° ~ -150°, 80 = -150° ~ -90°,100 = -90° ~ -30°。時(shí)序圖里,【同步雷達(dá)】信號(hào)永遠(yuǎn)按照 0→20→40→60→80→100→0…… 的方式循環(huán),說(shuō)明雷達(dá)在不斷按逆時(shí)針?lè)较蛐D(zhuǎn)。
再看【脈沖雷達(dá)】信號(hào)。當(dāng)雷達(dá)未朝向用戶(hù)時(shí),該信號(hào)會(huì)保持為 0;而當(dāng)雷達(dá)正面朝向用戶(hù)時(shí),該信號(hào)的值會(huì)在瞬間變?yōu)椤熬嚯x用戶(hù)的直線(xiàn)距離”。
最后,無(wú)線(xiàn) rx 會(huì)不定期地發(fā)送一些數(shù)據(jù)包。當(dāng)你收到了 1 開(kāi)頭的數(shù)據(jù)包時(shí),說(shuō)明這是一個(gè)聲音波形的數(shù)據(jù)包。這個(gè)數(shù)據(jù)包的第二個(gè)數(shù)字表示波形長(zhǎng)度,后續(xù)的數(shù)字?jǐn)?shù)量等同于該長(zhǎng)度。你需要將這些波形原封不動(dòng)地輸出到【揚(yáng)聲器】中。當(dāng)你收到了 2 開(kāi)頭的數(shù)據(jù)包,且第二個(gè)數(shù)字和鎖中的數(shù)字一致時(shí),我們需要根據(jù)雷達(dá)的方向、雷達(dá)和用戶(hù)的直線(xiàn)距離這兩個(gè)數(shù)據(jù),計(jì)算用戶(hù)當(dāng)前所在的區(qū)域,并將對(duì)應(yīng)的區(qū)域代碼發(fā)送給【無(wú)線(xiàn) tx】。例如,方位為 60,距離為 50 時(shí),說(shuō)明用戶(hù)在工業(yè)區(qū),需要發(fā)送 700。
這一關(guān)仍然是分工合作,一塊芯片用于隨時(shí)確定用戶(hù)的位置,另一塊芯片用于接收并處理無(wú)線(xiàn) rx 發(fā)來(lái)的數(shù)據(jù)包。



兩塊寫(xiě)得滿(mǎn)滿(mǎn)的芯片。我們先來(lái)分析左邊的芯片,左邊的芯片要完成的任務(wù)是:當(dāng)右邊的芯片發(fā)來(lái)請(qǐng)求信號(hào)時(shí),左邊的芯片通過(guò)雷達(dá)的方向,以及和用戶(hù)的直線(xiàn)距離這兩個(gè)數(shù)據(jù),計(jì)算用戶(hù)所在區(qū)域,并將區(qū)域代碼回傳給右邊的芯片。
首先我們等待右邊的芯片發(fā)來(lái)信號(hào)(slx x3)。右邊芯片發(fā)來(lái)的一定是非 0 的【脈沖雷達(dá)】信號(hào)的值(也就是和實(shí)時(shí)的 p0 值相等),第一句 tcp x3 79 和 tcp p0 79 完全等價(jià),這里使用 x3 而不使用 p0 的原因是為了釋放掉從右邊芯片發(fā)來(lái)的數(shù)據(jù),防止阻塞。
首先我們檢查距離值是否 ≥80(tcp x3 79)。根據(jù)地圖上的指示,一旦距離 ≥80 時(shí),用戶(hù)不論處于什么方位,都一定位于政府大樓。所以滿(mǎn)足以上條件時(shí),直接回傳政府大樓的區(qū)域編號(hào)?100(+ mov 100 x3),同時(shí)跳回第一行,不再執(zhí)行下方的一切代碼(+ jmp 1)。
然后再檢查方位值是否在 -30°~30° 范圍內(nèi)(teq p1 0)。根據(jù)地圖指示,方位為 0 且距離小于 80 時(shí),用戶(hù)一定位于港口。而執(zhí)行到此處時(shí),距離一定是小于 80 的。所以執(zhí)行到此處時(shí),只需要方位為 0 就可判定用戶(hù)位于港口,直接回傳港口的區(qū)域編號(hào) 600 即可(+ mov 600 x3),同時(shí)跳回第一行,不再執(zhí)行下方的一切代碼(+ jmp 1)。
接下來(lái),繼續(xù)判斷距離值是否 ≤50(tlt p0 51)。根據(jù)地圖指示,距離在 0~50 范圍內(nèi),且方位不在 -30°~30° 范圍內(nèi)時(shí),用戶(hù)一定位于工業(yè)區(qū)。執(zhí)行到此處時(shí),第二個(gè)條件一定是成立的,所以只要距離 ≤50,就直接回傳工業(yè)區(qū)的區(qū)域編號(hào) 700 即可(+ mov 700 x3)。而當(dāng)距離在 50~80 范圍內(nèi)時(shí),回傳的區(qū)域編號(hào)和方位值有關(guān):方位值為 20 時(shí),區(qū)域編號(hào)為 200;方位值為 40?時(shí),區(qū)域編號(hào)為 201……方位值為 100 時(shí),區(qū)域編號(hào)為 204。我們發(fā)現(xiàn)距離 50~80 且方位值不為 0 時(shí),區(qū)域編號(hào)和方位值滿(mǎn)足如下函數(shù)關(guān)系:

除以 20 相當(dāng)于乘以 5 再除以 100,相當(dāng)于乘以 5 后取百位。因此,當(dāng)距離 50~80 且方位值不為 0 時(shí),我們將方位值放入 acc(-?mov p1 acc),將它乘以 5(- mul 5),取得百位數(shù)字(- dgt 2),再加上 199(-?add 199)即得到了對(duì)應(yīng)的區(qū)域編號(hào),將該編號(hào)回傳給右邊的芯片,即完成任務(wù)(-?mov acc x3)。
然后我們看右邊的芯片。右邊芯片有兩個(gè)任務(wù):一個(gè)是在收到【脈沖雷達(dá)】信號(hào)時(shí),委托左邊的芯片計(jì)算用戶(hù)所在的區(qū)域;另一個(gè)是收到無(wú)線(xiàn) rx 的數(shù)據(jù)包時(shí)做出相應(yīng)處理。
首先我們檢查是否出現(xiàn)了【脈沖雷達(dá)】信號(hào)(tcp p0 0),一旦出現(xiàn),將當(dāng)前的信號(hào)值發(fā)給左邊,喚醒左邊的芯片(+ mov p0 x0),等待它將用戶(hù)所在的區(qū)域編號(hào)計(jì)算好后,將該編號(hào)存入 dat 寄存器(+ mov x0 dat)。
第 4~7 行用于檢查是否仍有尚未播放完的音頻數(shù)據(jù),我們暫時(shí)跳過(guò)去,先從第 8 行開(kāi)始看。當(dāng)沒(méi)有音頻數(shù)據(jù)時(shí),我們需要將揚(yáng)聲器置為靜音(mov 50 p1)。然后我們讀取數(shù)據(jù)包。當(dāng)數(shù)據(jù)包的首數(shù)字是 1 時(shí)(teq x2 1),我們將第二個(gè)表示長(zhǎng)度的數(shù)字讀入 acc(+ mov x2 acc),然后跳回到第 5 行開(kāi)始播放音效(+ jmp 5)。現(xiàn)在跳回第 5 行,我們播放一格聲音波形(+ mov x2 p1),令音頻剩余長(zhǎng)度 -1(+ sub 1),然后跳到最后休眠一秒(+ jmp e, slp 1)。后面的時(shí)間里,只要聲音沒(méi)有播放完,我們的程序就一邊檢測(cè)用戶(hù)位置有沒(méi)有改變(1~3 行),一邊播放剩下的聲音(4~7 行及 14 行),直到聲音播放完畢后,將揚(yáng)聲器靜音(第 8 行)。
現(xiàn)在看第 12 行。無(wú)論是無(wú)線(xiàn) rx 隊(duì)列里沒(méi)有數(shù)字,讀到了 -999,還是數(shù)據(jù)包的首數(shù)字不是 1,我們都嘗試去讀入第二個(gè)數(shù)字,檢查它和鎖中的數(shù)字是否相等(teq x2 x1)。如果數(shù)據(jù)包中沒(méi)有數(shù)字時(shí),讀到的一定還是 -999,和鎖中的數(shù)字一定不一致(這是游戲里的設(shè)定),直接什么都不做,跳到最后休眠(slp 1)。僅當(dāng)?shù)诙€(gè)數(shù)字和鎖中的數(shù)字一致時(shí),我們先將用戶(hù)當(dāng)前所在的區(qū)域編號(hào)發(fā)送給 tx(+ mov dat x3),然后再休眠(slp 1)。
點(diǎn)擊左下角的【模擬】,稍等片刻,便會(huì)彈出結(jié)算界面:

這一關(guān)需要特別注意這樣一個(gè)小細(xì)節(jié):播放音頻的過(guò)程中仍然需要不斷探測(cè)用戶(hù)的位置改變了沒(méi)有。我們上面的代碼里的 4~7 行將播放音頻的過(guò)程從循環(huán)中分離了出來(lái),變成了每個(gè)時(shí)鐘周期的例行公事。每個(gè)時(shí)鐘周期里,右邊的芯片要做兩件例行公事:①檢查是否出現(xiàn)了【脈沖雷達(dá)】信號(hào),一旦出現(xiàn)就要通知左邊的芯片計(jì)算用戶(hù)所在區(qū)域。②檢查是否仍有沒(méi)播放完的音頻,若仍有,則從無(wú)線(xiàn) rx 中讀取波形信號(hào)發(fā)送到揚(yáng)聲器。
如果我們還按常規(guī)思路,將播放音頻的過(guò)程寫(xiě)在循環(huán)里,每播放一格就睡一秒,令 acc -1,直到 acc 減為 0 之前一直循環(huán)讀取,把代碼寫(xiě)成類(lèi)似下面這個(gè)樣子的話(huà):

那么就會(huì)造成一個(gè)問(wèn)題:播放聲音的過(guò)程中,芯片不再檢測(cè)外部是否產(chǎn)生了【脈沖雷達(dá)】信號(hào),不再更新用戶(hù)所在的區(qū)域位置。最終運(yùn)行時(shí)會(huì)卡在這么一個(gè)樣例上:

這個(gè)錯(cuò)誤就是典型的“播放聲音的過(guò)程中,用戶(hù)的位置都變了,你卻只顧著播放聲音了,忘了去更新用戶(hù)的位置”。
至此,恭喜你完成【深圳龍騰有限公司】系列的所有關(guān)卡。請(qǐng)準(zhǔn)備前往【阿瓦隆城】,開(kāi)啟 hard 模式,接受更大的挑戰(zhàn)!