iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0

汗顏,比賽近了尾聲,才發現步伐越是顢頇。

昨天發文之後,從空棋盤的狀態,先在 main.rsweb_server.rs 之間做一些調整,如這個 patch,然後有些本地端的調整還沒完善。

(繼續進行中...)

傳遞狀態碼

一邊煩惱著到底該怎麼最大限度的利用打譜程式打下的基礎,一邊先做了狀態碼的傳遞。我預期玩家在網頁模式點擊的時候,也是有可能會點錯。雖然那種反白當作可走步數的提示的功能是來不及做了,但至少點了之後是成功或失敗,應該還是可以顯示一下的。

相關功能剛剛推上去了

我最煩惱的地方在於,到底該怎麼設計一個狀態窗格?

我還是比較習慣類似 coord_server 裡面的那種狀態機設計,如同先前介紹過的。但是,ChatGPT 老師很理所當然的認為,網頁前端傳送了著點之後,從伺服器傳回來的 HTTP 回應,就應該用來顯示在這個狀態窗格裡面。所謂的狀態機是指,由諸多狀態碼包裝而成的複雜迴圈結構:

         'game: loop {
             read_exact_bytes(&mut stream, 4, &mut status)?;
             // XXX: 所有的行動都是由這裡取得的 status 來判斷的!!!
             if status == "Ix0d".as_bytes() {
                 // 接收到的對手玩家的回合做了什麼事情,然後
                 // 使用疫途遊戲引擎,更新接收到的對手玩家的回合
...
                 };
                 continue 'game;
             }
-            if status == "Ix02".as_bytes() || status == "Ix00".as_bytes() {
-                continue 'game;
-            } else if status == "Ix04".as_bytes() {
-                println!("win!");
-                break 'game;
-            } else if status == "Ix05".as_bytes() {
-                println!("lose!");
-                break 'game;
-            } else {
-                if status == "Ix01".as_bytes() || status == "Ix0b".as_bytes() {
-                    // the normal middle moves
-                } else if status == "Ix03".as_bytes() {
-                    // the main play of this round
+
+            'turn: loop {
+                if status == "Ix02".as_bytes() || status == "Ix00".as_bytes() {
+                    continue 'game;
+                } else if status == "Ix04".as_bytes() {
+                    println!("win!");
+                    break 'game;
+                } else if status == "Ix05".as_bytes() {
+                    println!("lose!");
+                    break 'game;
                 } else {
-                    // Error?
-                }
+                    if status == "Ix01".as_bytes() || status == "Ix0b".as_bytes() {
+                        // the normal middle moves
+                    } else if status == "Ix03".as_bytes() {
+                        // the main play of this round
+                    } else {
+                        // Error?
+                    }
 
-                if let Ok(c) = click_rx.recv() {
-                    println!("Received {} from web interface.", c);
-                    stream.write(&[c])?;  // XXX:這是 patch 前的內容,也就是我們可以從 click_rx 接收到網頁前端選擇的著點,但是,狀態碼卻需要到觸及迴圈底部之後,從頭開始(參照前面的 XXX 註解)!!!
+                    if let Ok((c, status_tx)) = click_rx.recv() {
                         // 所以 patch 的策略就是附上回郵信封,不是只傳回著點,而是在負責處理前端事件的非同步函數 receive_click 當中,連接一個傳送門,讓這裡可以經由網頁代理人傳送資料給遊戲伺服器(stream.write),然後由遊戲伺服器根據著點更新遊戲狀態...
+                        println!("Received {} from web interface.", c);
+                        stream.write(&[c])?;
+                        read_exact_bytes(&mut stream, 4, &mut status)?; // ...並傳送狀態碼回來,然後我們再將這個狀態碼...
+                        let s = format!("{:?}", status);
+                        status_tx.send(StatusCode { status: s }); // ... 傳回給 receive_click 的傳送門。
+                    }
+                    continue 'turn; // 為了這個調度多塞了一層迴圈,但講下去頭腦打結,就先這樣吧。

Rust 對於型別是非常嚴謹的,但依我的感覺,這個傳送門功能,實在不得不令人想起 go channel,而且也很意外沒有什麼 Copy/Clone 特徵有無實作的問題,也沒有參照可不可變的問題,很乾脆地讓我擴充了。

所以看看 receive_click 那邊的變動,

 async fn receive_click(
     data: web::Json<ClickData>,
-    click_tx: web::Data<Sender<u8>>,
+    click_tx: web::Data<Sender<(u8, Sender<StatusCode>)>>,
 ) -> impl Responder {
     let c = data.coord;
+    let (status_tx, mut status_rx): (Sender<StatusCode>, Receiver<StatusCode>) = mpsc::channel(); // 傳送門
     // Send the coordinates back to main.rs
-    if let Err(e) = click_tx.send(c) {
+    if let Err(e) = click_tx.send((c, status_tx)) { // 很輕鬆的擴充了
         eprintln!("Failed to send click data to main: {}", e);
         return HttpResponse::InternalServerError().body("Failed to process click");
     }
-    HttpResponse::Ok().body("Click received")
+
+    if let Ok(game_response) = status_rx.recv() { // 附上回郵信封之後,立刻等信來!哈。
+        return HttpResponse::Ok().json(game_response);
+    }
+
+    HttpResponse::InternalServerError().body("Error processing the move")
 }

接下來仍然要解決盤面上其他物件顯示的問題,不然就像閉著眼睛在玩。理論上昨天也是這樣。

但是為了避免意外,先上圖結束今天。

https://ithelp.ithome.com.tw/upload/images/20240927/20103524kNqSh8Epqr.png

自首:雖然我有 str_to_full_msg 函數,但是我現在有點卡在 Rust 的嚴厲型別審查。所以也不確定怎麼轉,因此只能看到窗格中只有 ASCII。熟練如實作者的我當然可以看出這是 Ex24。但當務之急果然還是,趕快把圖形顯示搞定啊。

明天的話,按照一開始的規劃,邀請了兩位朋友(友人 Y 與友人 T)首次來遊玩疫途,並且預計讓他們與訓練了好幾個世代但仍然不怎麼樣的代理人對局。兩位都是比我更身經百戰的桌遊咖。本來是很浪漫的預想要準備一些問題,端出神擋殺神的 AI 代理人殺爆他們之後,再好好的對話一下,像是 DeepMind 對李世石做的那樣,但現在,Well,就簡單聊聊天吧。


上一篇
對戰網頁實作:解決輸入
系列文
DeltaPathogen:國產雙人不對稱抽象棋「疫途」之桌遊 AI 實戰27
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言