最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

【深圳 IO 攻略】阿瓦隆城第 5 關(guān)《海藻收割機(jī)器人》的全新解決方案(空間換時間)

2022-09-15 15:47 作者:ココアお姉ちゃん  | 我要投稿

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

關(guān)卡展示

本關(guān)的 C2S-RF901 會不定期地發(fā)送一些長度為 2 的數(shù)據(jù)包:兩個數(shù)字分別記為 x 和 y,表示在 (x, y) 位置出現(xiàn)了新的海藻。題目保證不會在 (0, 0) 點出現(xiàn)海藻。

當(dāng)隊列中有等待收割的海藻時,命令電機(jī)按照先來后到的順序依次收割所有的海藻。這次的電機(jī)變成了在二維平面上運動,控制規(guī)則如下:

  • 【電機(jī) x】和【電機(jī) y】信號初值均為 50。

  • 給【電機(jī) x】發(fā)送 a 秒的 0 信號,表示令電機(jī)左移 a 格;給【電機(jī) x】發(fā)送 b 秒的 100 信號,表示令電機(jī)右移 b 格。

  • 給【電機(jī) y】發(fā)送 c 秒的 0 信號,表示令電機(jī)下移 a 格;給【電機(jī) y】發(fā)送 d 秒的 100 信號,表示令電機(jī)上移 d 格。

  • 可以同時給【電機(jī) x】和【電機(jī) y】兩路輸出發(fā)送信號,此時電機(jī)將按斜線方向移動:x = 100, y =?100?時,向右上方移動;x = 100, y =?0?時,向右下方移動;x = 0, y = 0 時,向左下方移動;x = 0, y = 100 時,向左上方移動。

  • 移動完畢后,需要將【電機(jī) x】和【電機(jī) y】都還原成?50。

  • 當(dāng)電機(jī)移動到有海藻的位置時,給【收割】端口發(fā)送 1 秒鐘的 100 信號,即可完成一次收割。

為了以最快速度到達(dá)海藻的目標(biāo)位置,我們需要讓電機(jī)優(yōu)先按斜線方向移動。另外,原則上,我們需要按照先來后到的順序收割海藻。但如果前往目標(biāo)點的過程中,遇到了后出現(xiàn)的海藻,也順帶收割掉。

例如,當(dāng)電機(jī)停在 (0, 0),需要前往 (2, 3) 收割海藻時,需要依次移動到?(0, 0)→(1, 1)→(2, 2)→(2, 3) 點。如果在走到 (1, 1) 點時,發(fā)現(xiàn)了后出現(xiàn)的海藻,那么即使它是先來后到里的后者,也順帶收割掉。

本關(guān)的難點在于維護(hù)一個【任務(wù)隊列】。和第 1 關(guān)不同,第 1 關(guān)從冷庫中拿食物的規(guī)則是【隨用隨取】,需要用了就拿出來,你需要隨時告訴系統(tǒng),我現(xiàn)在要拿編號多少的食物。而本關(guān)收割海藻的要求是按照先來后到的順序收割,系統(tǒng)不會告訴你,現(xiàn)在要往哪跑,收割哪里的海藻。

隊列是一種動態(tài)存儲大量數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu),就跟生活中隨處可見的“排隊”一樣,排在前面的人最先享受服務(wù)。在隊列這種數(shù)據(jù)結(jié)構(gòu)里,添加操作只能在隊列的末端進(jìn)行,讀取和刪除操作只能在隊列的首端進(jìn)行。如下圖所示。

我們收到數(shù)據(jù)包的時候,將要到達(dá)的目標(biāo)位置放在【隊尾】。當(dāng)任務(wù)隊列中還有數(shù)據(jù)時,我們需要令電機(jī)到達(dá)【隊頭】所指示的位置,完成收割后,將【隊頭】元素刪除。如此,便能實現(xiàn)“按先來后到的順序收割海藻”的要求。

本關(guān)需要 5 塊芯片。1 號芯片是數(shù)據(jù)庫管理員,和 C2S-RF901 直接連接,用于不斷向任務(wù)隊列里添加新的收割任務(wù)。2 號芯片是監(jiān)察員,不斷監(jiān)測當(dāng)前的隊頭任務(wù)是否完成。隊頭任務(wù)完成后,需要向后尋找新的任務(wù),并將隊頭指向新的任務(wù),同時給后面的 3~5 號工人芯片發(fā)送新的任務(wù)指令。3~5?號芯片是勤勞的工人,分別控制電機(jī) x、電機(jī) y 和收割信號。其中 5 號芯片也兼顧數(shù)據(jù)庫管理員這一職,每完成一次收割,就將任務(wù)隊列里的對應(yīng)任務(wù)清除,以便 2 號監(jiān)察員能夠監(jiān)視到。電路圖如下所示:

本關(guān)的接線非常復(fù)雜。我先解釋一下為什么要連這么多線。

左下角的芯片是 1 號數(shù)據(jù)庫管理員芯片,需要完成【從?C2S-RF901 中接收數(shù)據(jù)包】和【將新的數(shù)據(jù)包寫入任務(wù)隊列】2 件事情。所以它的 x0 口和 C2S-RF901 的 rx 口連接,x2、x3 口和 RAM 的 d0、a0 口連接。

正中央的芯片是 2 號監(jiān)視員芯片。這塊芯片需要完成【監(jiān)視數(shù)據(jù)庫】,以及【向右側(cè)的 4、5 號芯片告知實時的目標(biāo)位置】這兩項任務(wù)。它的 x2、x3 口和 RAM 的 d1、a1 口相接;它的 x0 口和右側(cè)控制【電機(jī) x】的?3?號芯片相接,用于傳輸目標(biāo)?x;它的 p1 口和右側(cè)控制【電機(jī) y】的 4?號芯片相接,用于傳輸目標(biāo)?y。至此,還剩下 1X1P 口未被使用。為了充分利用資源,我們不妨順手將 x1 口和 RAM 的 a0 口相接,以獲得隊尾指針。

然后是右邊的 3 塊工人芯片。輸入方面,3 號芯片用?x0 接收 2 號芯片發(fā)來的?x 坐標(biāo),4 號芯片用?p0 口接收 2 號芯片發(fā)來的?y 坐標(biāo);輸出方面,這兩塊芯片的 p1 口則連接著【電機(jī) x/y】的輸出端信號,而 x1 口是和 5 號芯片的 x3 口相連的,作用是:每到達(dá)一個新位置,就喚醒 5?號芯片,讓它檢查現(xiàn)在腳下有沒有待收割的海藻。

5 號收割芯片的 x3 口用于和上方負(fù)責(zé)移動電機(jī)的芯片通訊,p1 口連接著【收割】的輸出端信號。x0、x1 口連接著 RAM 的?d1 和 a1,以便當(dāng)自己成功收割一個海藻后,能夠抹除數(shù)據(jù)庫里的相應(yīng)任務(wù)記錄。

這一關(guān)里,芯片間的信息傳遞非常頻繁,雖說看起來導(dǎo)線連接得異常錯綜復(fù)雜,但真的沒有一根導(dǎo)線是廢線。我們先從 1 號數(shù)據(jù)庫管理員芯片開始寫起。代碼如下:

這里我要先提一下:上一版被廢棄掉的方案里,我使用的是“兩位數(shù)存儲法”,用個位存目標(biāo)點的 x 坐標(biāo),用十位存目標(biāo)點的 y 坐標(biāo)。這種壓縮存儲的方法只適合在存儲空間捉襟見肘的時候使用,實際執(zhí)行時,存儲時需要壓縮成一個兩位數(shù),使用時又要解壓,屬于【用時間換空間】,有效率上的損失。而且由于題目保證了等待收割的海藻不超過 6 個,右下角的【收割】芯片全盤掃描 RAM 時,至少有 8 格是空的,這就導(dǎo)致了全盤掃描 RAM 時,至少一半的時間在做無用功,效率進(jìn)一步降低。

當(dāng)存儲空間并沒有捉襟見肘時,正確的做法應(yīng)當(dāng)是【用空間換時間】,用兩格 RAM 來存儲一個目標(biāo)位置的 x、y 坐標(biāo),而不是把一個目標(biāo)位置壓縮成兩位數(shù)。本次的新攻略正是修正了前一次【用時間換空間】的錯誤。先提前劇透一下,本次【用空間換時間】后,三項指標(biāo)分別為 21 元成本、2.2K?電量、50 行代碼,上一版廢棄方案的 24 元成本、3.8K 電量、54 行代碼被完爆到渣都不剩。

現(xiàn)在回過頭來看代碼。1~4 行是第一秒里的初始化操作,我們需要將 RAM 填入一半的 -999。上一版方案里由于目標(biāo)點的 (x, y) 坐標(biāo)存在一個兩位數(shù)里,題目又保證了不會在原點 (0, 0) 出現(xiàn)海藻,所以上一版方案不需要初始化,直接可以把默認(rèn)的 0?視為空格。但本方案不行,因為本方案是將 (x, y) 坐標(biāo)分別存儲在兩格里的,題目可沒有保證不在除 (0, 0) 外任意的 (x, 0) 或 (0, y) 點也不出現(xiàn)海藻。如果我們還像上一版方案那樣不做初始化處理,那么后續(xù)的過程中,我們從 RAM 中讀到一格 0 時,其實是并不能確定當(dāng)前點沒有海藻的,非得連續(xù)讀到兩個 0 才能確定,這反而平白無故地增加了復(fù)雜度。與其這樣,我們還不如用一個負(fù)數(shù)代表空格呢,這樣我們讀到一個負(fù)數(shù)就知道當(dāng)前格是空格了。

第 1 行?@ teq 0 1 用于上電時激活 - 號開頭的指令。初始化部分的代碼只有第 2~4 行共 3 行代碼,全部用 - 號包裹,后續(xù)的代碼里都不可能再激活 - 號指令。初始化時,首先往 RAM 中填入一格 -999(- mov -999 x2),然后讀一格 RAM(必然讀到 0)后判斷讀到的 0 是否比地址值小,即地址值是否大于 0(- tcp x2 x3),若沒有歸零則回到第 2 行繼續(xù)填(- jmp 2),直到地址歸零,填滿為止。

RAM 地址在上電時位于 0 號位,是第偶數(shù)格。寫入一格 -999 時,地址會自增一格,這樣就跳到了第奇數(shù)格。此時再讀一格(必然讀到 0),地址同樣會自增一格,這就又回到了第偶數(shù)格。如此反復(fù),當(dāng)?shù)刂吩诘谂紨?shù)格時我們就寫 -999,當(dāng)?shù)刂吩诘谄鏀?shù)格時我們就讀。如此反復(fù),最終初始化完畢時,RAM 會變成這個樣子:

第 5~9 行就是后續(xù)的常規(guī)任務(wù)了。首先休眠 1 秒(slp 1),從第 2 秒起,每秒鐘都從 C2S-RF901 里讀取數(shù)據(jù)(mov x0 acc),檢查它是不是 -999(tcp acc -999)。若是,關(guān)閉一切 +/-?號指令,防止激活以 - 號包裹的初始化部分的代碼。若不是,說明是由兩個一位數(shù)組成的數(shù)據(jù)包,首數(shù)字代表新海藻所在的 x 坐標(biāo),第二個數(shù)字代表新海藻所在的 y 坐標(biāo)。此時我們激活 + 號指令的代碼,依次將這兩個數(shù)字存入 RAM(+ mov acc x2, + mov x0 x2)。為了確保其他芯片讀取海藻坐標(biāo)數(shù)據(jù)時不產(chǎn)生混亂,這里我們規(guī)定:第偶數(shù)格存的是 x 坐標(biāo),第奇數(shù)格存的是?y 坐標(biāo)。

接下來是位于線路板最中央的 2 號監(jiān)視員芯片:

這塊芯片的代碼使用了?【深圳 IO 攻略】番外篇:上電時設(shè)置符號以提高運行效率 里的技巧,直接解釋比較麻煩,所以下面我先貼一個優(yōu)化前的代碼:

2 號芯片的 acc 寄存器用于存儲隊頭地址,即【當(dāng)前正在執(zhí)行的任務(wù)編號】。當(dāng)任務(wù)隊列為空(即沒有新任務(wù))時,acc 表示的是【上一個已完成的任務(wù)編號】。前一個任務(wù)完成后,這個芯片不會立刻將這個已完成任務(wù)從隊列里清除,而是掃描到新任務(wù)后,再去清除。然后,這塊芯片的 dat 寄存器用于存儲目標(biāo)?x,目標(biāo)?y 則是通過 p1 口直接輸出給右邊的 4 號芯片。

第一秒里一定不會有海藻出現(xiàn),但是 2 號芯片每秒鐘都必須和后面控制電機(jī) x、y 坐標(biāo)的芯片通訊(后面會說到)。所以第一秒里仍然要將默認(rèn)的 (0, 0) 坐標(biāo)發(fā)給右邊的芯片(mov dat x0, slp 1)(dat 和 p1 的默認(rèn)值都是 0)。從第二秒開始,我們需要循環(huán)檢查隊頭處的海藻有沒有被收割掉,首先將 a1 口的地址置為緩存在 acc 里的隊頭地址(mov acc x2)。接下來的第 4~11?行構(gòu)成了一個循環(huán),用于尋找從原隊頭開始的第一個出現(xiàn)的海藻,并將對應(yīng)的位置設(shè)置為新的隊頭地址。我們每到達(dá)一個新的格子,就判斷該格是不是 -999(mov x2 acc, tgt x3 -999)。若不為 -999,那么我們就找到了離原隊頭最近的待收割海藻,我們令 RAM 指向這個新隊頭(+ mov acc x2),然后依次讀取兩格數(shù)據(jù),并依次寫入 dat 寄存器和 p1 口,作為新的目標(biāo)?x、y(+ mov x3 dat, + mov x3 p1)。執(zhí)行完畢后,停止搜索,直接跳回第 1 行,將目標(biāo) x 通過 x0 口發(fā)給右邊的 3 號芯片喚醒電機(jī)并休眠(mov dat x0, slp 1)。若不幸讀到了 -999,則說明尚未找到海藻,此時空讀一下數(shù)據(jù)口,跳過奇數(shù)格,直接到達(dá)下一個偶數(shù)格,確保讀取時不奇偶錯位(- mov x3 null)。接下來我們判斷?RAM 指針是否到達(dá)了隊尾(- teq x2 x1)。尚未達(dá)到隊尾時,跳回到第 4 行,繼續(xù)向后查找,直到到達(dá)隊尾為止(- jmp 4)。若從隊頭一直找到隊尾都沒有找到任何一株等待收割的海草,說明任務(wù)隊列清空了,電機(jī)的目標(biāo) x、y 不需要做任何調(diào)整,直接原樣發(fā)出,令電機(jī)停留在原地就好了(mov dat x0, slp 1)。此時我們只需要執(zhí)行第 1、2 行的代碼,不需要執(zhí)行第 6~8 行帶 + 號的代碼。后者是用于更新目標(biāo) x、y 的。簡而言之就是:找到了待收割的海藻,則更新目標(biāo)點并發(fā)出信號;沒找到時,僅發(fā)送信號,不更新目標(biāo)點

以上代碼,我們使用“上電設(shè)置符號法”優(yōu)化,完成

①準(zhǔn)備工作前添加 @ teq 0 0;②準(zhǔn)備工作和收尾工作全部帶上 + 號;③循環(huán)體去掉 jmp 作者:ココアお姉ちゃん https://www.bilibili.com/read/cv18047538

這三步曲后,就變成了如圖所示的代碼。

接下來是 3、4?號控制電機(jī) x/y 位置的芯片:

3 號芯片的 acc 寄存器用于記錄電機(jī)的實時 x 坐標(biāo),4 號芯片的 acc 寄存器用于記錄電機(jī)的實時 y 坐標(biāo)。

由于 2 號芯片每秒鐘都會喚醒 3 號芯片,并將目標(biāo) x 坐標(biāo)通過 x0 口傳給 3 號芯片,所以 3 號芯片無需使用 slx x0 這樣的指令來等待,而是可以直接計算目標(biāo) x 和當(dāng)前 x 的差值(tcp x0 acc)。差值為 0 時,電機(jī)保持 50 的電平信號(mov 50 p1),差值為負(fù)時,目標(biāo) x 在當(dāng)前 x 的左側(cè),這一秒里電機(jī)在 x 軸方向需要左移一格,令 acc -1 并且給電機(jī)賦 0 信號(- sub 1, - mov 0 p1);差值為正時,目標(biāo) x 在當(dāng)前 x 的右側(cè),這一秒里電機(jī)在 x 軸方向需要右移一格,令 acc +1 并且給電機(jī)賦 100 信號(+ add 1, + mov 100 p1)。做完這些后,將新的實時 x 坐標(biāo)通過 x1 口發(fā)給右下角控制收割信號的 5 號芯片并休眠(mov acc x1, slp 1)。

接下來看下方的 4 號芯片。為什么 4 號芯片反而需要 slx 指令喚醒了呢?因為 4 號芯片不像 3 號芯片那樣通過 x 口接收坐標(biāo)數(shù)據(jù),而是通過 p 口接收坐標(biāo)數(shù)據(jù)。由于 x 口采用的是同步協(xié)議,所以?2 號芯片還沒有給 3 號芯片的 x0 口傳數(shù)據(jù)時,3 號芯片會一直等著,直到 2 號芯片給它發(fā)送了數(shù)據(jù)以后才繼續(xù)執(zhí)行下面的代碼??墒?p 口沒有同步協(xié)議,4 號芯片讀 p 口時,2 號芯片如果還在慢悠悠地算的話,那么 4 號芯片就會讀到上一秒里的數(shù)據(jù)。所以讀 p 口數(shù)據(jù)時,必須要手動確保時間差,讀取的一方必須要確認(rèn)寫入的一方把數(shù)據(jù)寫入進(jìn)去后才能讀。

這里的 slx x1 指令打的就是這個時間差。當(dāng) 4 號芯片被 x1 口喚醒時,說明 3 號芯片將實時的 x 坐標(biāo)往下傳給 5 號芯片了。3 號芯片必然是在 2 號芯片動完以后才動的,此時 2 號芯片肯定已經(jīng)寫好目標(biāo) y 了,此時再讀 p0 便可萬無一失。這時候我們只是為了讓 3 號芯片順便喚醒一下自己,咱們可千萬不能手賤去讀這個 x1,把這個本來要往下傳的數(shù)字給截斷了。接下來的 2~8 行,和 3 號芯片的 1~7 行幾乎完全一致,除了把 x0 改成了 p0 以外完全一致。同樣也是計算目標(biāo) y 和當(dāng)前 y 的差值(tcp p0 acc)。差值為 0 時,電機(jī)保持?50 的電平信號(mov 50 p1),差值為負(fù)時,目標(biāo) y?在當(dāng)前 y 的下方,這一秒里電機(jī)在 y?軸方向需要下移一格,令?acc -1 并且給電機(jī)賦 0 信號(- sub?1, - mov 0 p1);差值為正時,目標(biāo)?y?在當(dāng)前 y?的上方,這一秒里電機(jī)在 y?軸方向需要上移一格,令 acc +1 并且給電機(jī)賦 100 信號(+ add 1, + mov 100 p1)。做完這些后,將新的實時 y?坐標(biāo)通過 x1 口發(fā)給右下角控制收割信號的 5 號芯片并休眠(mov acc?x1, slx x1)。

由于 3、4?號芯片每秒鐘都會被喚醒,所以這里不需要寫成循環(huán)結(jié)構(gòu),每次喚醒時都做同樣的事情時,就相當(dāng)于在循環(huán)了。

最后看 5 號用于控制收割的芯片:

還是同樣地,提供一份優(yōu)化前的代碼:

首先等待上方芯片喚醒(slx x3)。喚醒后,將 RAM 的地址歸零(mov 0 x1),同時將得到的 x 和 y 分別放入 acc 和 dat 寄存器(mov x3 acc, mov x3 dat)。因為 3 號芯片將 x 坐標(biāo)送入 x1 口后,4 號芯片才醒,因此接收數(shù)據(jù)時,一定是先得到 x,后得到 y。第 5~14 行是循環(huán)。首先讀第偶數(shù)格的數(shù)據(jù),檢查是否和當(dāng)前電機(jī)的?x 一致(teq x0 acc),不一致時,該海藻不在腳下,肯定無法收割,所以直接空讀一次第奇數(shù)格,跳過這個海藻(- mov x0 null)。一致時,再讀一次第奇數(shù)格,檢查是否和當(dāng)前電機(jī)的?y 一致(+ teq x0 dat)。若當(dāng)前海藻的 x, y 與當(dāng)前電機(jī)的 x, y 都一致,那么就立刻啟動收割儀式。首先將 RAM 地址向前偏移兩格,定位到該海藻的 x 處(+ mov x1 acc, + sub 2, + mov acc x1),然后向?RAM 對應(yīng)位置處寫入 -999,將該海藻從?RAM 里抹掉(+ mov -999 x0)。改寫完 RAM 后,向【收割】端口發(fā)送 1 秒鐘的 100 信號(+ gen p1 1 0)。若當(dāng)前海藻的?x, y?有 1~2?項與當(dāng)前電機(jī)的 x, y?不一致時,就繼續(xù)向后遍歷這個 RAM。檢查 RAM 地址有沒有回到 0(- teq x1 0),尚未回到 0 時,跳回到第 5 行繼續(xù)查找有沒有海藻就在腳下(- jmp 5)。如果遍歷一圈完畢后,仍找不到任何一個在腳下的海藻,那本秒里就不需要收割,就直接回到開頭睡眠,等待下一秒電機(jī)移動后,再檢查是否需要收割。

以上代碼,我們使用“上電設(shè)置符號法”優(yōu)化,完成

①準(zhǔn)備工作前添加 @ teq 0 0;②準(zhǔn)備工作和收尾工作全部帶上 + 號;③循環(huán)體去掉 jmp 作者:ココアお姉ちゃん https://www.bilibili.com/read/cv18047538

這三步曲后,就變成了如圖所示的代碼。

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

21 元成本,2.2K 電量,50 行代碼,我們用空間換時間,完爆了上一版 24 元成本,3.8K 電量,54 行代碼的方案。

【深圳 IO 攻略】阿瓦隆城第 5 關(guān)《海藻收割機(jī)器人》的全新解決方案(空間換時間)的評論 (共 條)

分享到微博請遵守國家法律
南丰县| 维西| 定结县| 南昌县| 望江县| 北流市| 增城市| 商水县| 三明市| 托克托县| 体育| 美姑县| 株洲市| 云霄县| 平乡县| 沿河| 中方县| 常熟市| 扎赉特旗| 平山县| 和平县| 漳州市| 于田县| 临湘市| 宁晋县| 巩义市| 广安市| 吉林市| 云阳县| 右玉县| 娄底市| 信宜市| 房产| 邯郸县| 台北县| 江都市| 苍溪县| 衡水市| 苍南县| 平顶山市| 临泉县|