跳至主要内容

Dual-25:基因演算法策略

· 閱讀時間約 2 分鐘

Dual 25 是 Wiwi 在 睡前想到的紙牌對戰遊戲。(好玩!)

遊戲介紹​

好想贏電腦​

我(跟 LLM 互動)寫了一個固定策略,嘗試去贏過這個 MCTS 電腦。

接著再測試了一下對戰,以下是模擬結果:

  • 對戰場次:10000 場
    • ✅ 獲勝:5902 (59.0%)
    • 🤝 平手:1217 (12.2%)
    • ❌ 戰敗:2881 (28.8%)

(這樣應該還算不錯吧?)

試試看​


訓練:基因演算法(GA)​

  • 每種牌型定義 3 個權重參數:

    • 基礎參數(base):該牌型的基礎分數
    • 牌型參數(v):點數的權重,決定大牌還是小牌更優先
    • 危險參數(danger):生命值越低時的傾向調整 = (25 - 當前生命值) / 25,範圍是 0~1
  • 每張牌會根據以下公式計算分數,分數最高的牌就會被選出:

    • attack = atk_base + v × atk_v + danger × atk_danger
    • counter = ctr_base + v × ctr_v + danger × ctr_danger
    • heal = heal_base + v × heal_v + danger × heal_danger
  • 訓練參數設定:

    • 族群大小:150 個個體
    • 演化世代:1000 代
    • 評估局數:每個體每世代評估 800 局
    • 評分機制:勝利 = 1.0 分,平手 = 0.5 分,失敗 = 0 分
    • 訓練對手:採用「隨機出牌」策略,這樣跑比較快(應該也比較不會 Overfitting?),不然用 MCTS 會訓練太久

額外補強​

我最後再額外再加上「斬殺(lethal)」的概念。

  • 如果「反擊」或「攻擊」的數值高於對手的血量,則優先選擇斬殺(反擊優先)。
  • 如果有多張牌能斬殺,優先選擇點數小的牌做斬殺。

browser-sync 重複 reload 的解法

· 閱讀時間約 1 分鐘

上次在 更新網站產生器 的時候,有提到預覽功能 browser-sync 變得緩慢的問題。

今天才發現變慢的主因不是 build 產生太多檔案,而是因為偵測到太多檔案變動而不穩定。

一開始我改成偵測到檔案變動之後就去 build,然後產生一個 .reload 的檔案,裡面放 timestamp,這樣 browser-sync 只要去偵測這個檔案就可以了。

但是我也有一些檔案是不需要 build 的阿,這樣又要把路徑區分開來寫在 browser-sync,有點麻煩。

最後決定還是讓 browser-sync 偵測整個 public 資料夾,再加上兩個參數就解決了:

  • --reload-debounce 300:等檔案變動安靜下來再 reload。
  • --reload-delay 100:多等一點時間再刷新,避免被連續事件打爆。

2026/02/25 微更新​

還是限制一下範圍比較好,像是這樣:

--files "public/assets/**/*.css"
--files "public/assets/**/*.js"
--files "public/*.html"
--files "content/**/*"

筆記​

更新:部落格網站產生器

· 閱讀時間約 3 分鐘

前幾天在調整部落格時,有幾個痛點一直放著沒動,想說乾脆趁這個機會改一改。


痛點一​

在生成器出現之前,我編輯的檔案跟最後部署的檔案是同一份,所以編輯時都是用 browser-sync 套件來預覽,只要按下儲存,網頁就會自動更新。

但是在生成器出現之後,如果我想要預覽新文章,就只能選這兩種方法:

  1. 每次想要預覽就只要重新 build 整個網站。
  2. 暫時在 index.html 或 preview.html 寫新文章,等編輯到滿意之後再放到真正的文章檔案,然後再重新 build 一次。

其實使用方法二也沒有多不方便,就是有點繞路而已。

解法​

  • 新增 watch.php,監聽 content/articles 與 templates 目錄變動,自動觸發 build。
  • 接著再整合 browser-sync,只要有變動,就同步到瀏覽器上。

痛點二​

生成器在執行 build 的時候,會把所有文章都重新生成一次,久了會導致 browser-sync 變得緩慢,而且還「沒有刪除功能」,需要自己檢查有沒有殘留的頁面。

解法​

這個老實說應該要分開做,正常來說應該是:

  1. build 是正式環境要用的,應該刪除整個資料夾,然後全部重新建構一次,這樣最安全。
  2. dev 是當下預覽用的,第一次啟動要全部 build 一遍,接下來則是根據有變動的檔案去 build 就好。

但我為求簡單,把它整合到一起了,而且 build 的時間基本上只有 0.1 秒,體感上幾乎感受不到。

  • 新增 Smart Write 機制,僅在檔案內容變動時才寫入,避免不必要的 I/O。
  • 新增 cleanStaleFiles(),自動清理多餘的分頁資料夾與文章資料夾(透過 slug 比對)。
  • 新增 summary(),建置完成後顯示更新的檔案列表。

這樣的話,每次 build 雖然會掃過所有文章,但如果只有修改內文,實際上只會生成:

  1. 首頁(或分頁)的文章內容
  2. 文章檔案(含資料夾)
  3. search.json
  4. rss.xml

痛點三​

RSS 的 lastBuildDate 欄位每次 build 都會改成最後建置時間,但其實 RSS 內容完全沒變的時候是不需要上版的。

解法​

  • RSS 的 lastBuildDate 改為最新文章發布日期,避免每次 build 的時候都變動。

新的發文流程​

  • 點兩下 new-article.bat,建立新文章模板,並且自動填入今天日期。
  • 點兩下 auto-sync.bat,自動打開 browser-sync 跟 watch.php,編輯儲存的時候會同步顯示到瀏覽器上。
  • (Optional)保險一點,自己再 build 一次。
  • 最後直接 commit + push 就行了。

檔案傳送工具

· 閱讀時間約 2 分鐘

昨天晚上臨時有個「電腦→電腦」傳大檔案的需求,大概是 80 GB 左右,因為不是在區域網路,所以平常用的 LocalSend 派不上用場(後來發現可以搭配 Tailscale 來傳),透過雲端服務的話,檔案會太大不好上傳,而且也滿沒效率的。

後來找到 Croc 跟 FilePizza 這兩種做法,我最後是選擇穩定好用的 Croc。

一開始我是用他們公用的 Relay 伺服器傳,但馬上就發現網路速度吃不滿,跑起來有點慢,看了文件之後發現可以自己架 Relay 伺服器,於是我就把我家 Router 的 NAT 轉址打開,直接用我的電腦來當 Relay 伺服器,這樣就可以把網路全部吃滿了。

這裡要吐槽自己一下,既然有 Public IP + NAT,那架 FTP Server 或用 LocalSend 指向 IP 其實就可以用了嘛~

這邊提一下,如果沒有辦法搞到 Public IP 或 NAT 轉址的話,也是有其他的做法的,像是可以把 Relay 伺服器架在 VPS 上面(但要注意流量限制),或者更好的選擇......用 Tailscale 啊!

Tailscale 也太好用了吧!(久仰大名,但這其實是我第一次用,以前都沒機會用到 XD)

總之,結論如下:

  • 如果你是要自己傳給自己,內網 LocalSend,外網搭配 Tailscale 就很夠用了。
  • 如果你臨時想要傳一些東西給別人,或者懶得裝軟體的話,就用 FilePizza。
  • 如果你想要穩定傳大檔案,又剛好有終端機可以用的話,可以選 Croc,速度不夠的話再搭配 Tailscale 架 Relay 伺服器。

使用情境​

工具網路環境裝置特點
LocalSend區域網路跨裝置圖形介面、簡單易用
FilePizza跨網路跨裝置打開網頁就能用
Croc跨網路電腦之間大檔案、斷點續傳、自架 Relay

筆記​

備份所有 GitHub Repositories

· 閱讀時間約 1 分鐘

備份檔案​

今天在做(大約)一年一度的備份,把一些資料和電腦檔案備份到行動硬碟裡。

其中也包含雲端服務的資料,像是「Google Drive」、「Google Photos」這類的,用 Google Takeout 就能直接下載成 ZIP 檔,相當方便。

GitHub​

至於 GitHub 嘛,就得自己想辦法處理了。

老實說,我原本是覺得好像沒有什麼 Repo 是一定要備份的,尤其在這個「大 AI 時代」,一些偏筆記型的 Repo 好像也沒那麼重要了,我甚至還順手砍掉了好幾個。

而且就算 GitHub 真的哪天掛了、資料不見了,也會有很多人一起陪葬啊,就像前陣子的一些 AWS、Cloudflare 故障一樣,大家要死就一起死,好像就沒那麼痛了?(好糟糕的心態 XD)

不過,從今年開始就不一樣了。

現在的 Repo 還包括我的「部落格」跟現在這個「文檔 + 部落格」,這些是我真的花了大量心血在上面的東西,就像日記本、照片一樣珍貴,所以還是要備份一下啦!

筆記​

Cloudflare 即時聊天室

· 閱讀時間約 2 分鐘

聊天室​

前陣子想說看可不可以用 Cloudflare Worker 來做個簡單的聊天室,因為我發現 Cloudflare Worker 雖然是 serverless,但是它的冷啟動時間超短,幾乎可以忽略不計。

總之,一般聊天室做法大概有三種:

  1. Polling
  2. Long Polling
  3. WebSocket

Polling​

  • 資料儲存:使用 Cloudflare D1(基於 SQLite 的資料庫)
  • 讀取訊息:Worker 定期查詢 D1 獲取新訊息(每 4 秒查詢一次)
  • 傳送訊息:直接寫入 D1 資料庫
  • MOD 功能:可在 env 設定管理員密碼來刪除訊息
  • 安全限制:限制 API 速率、只能在指定網域使用等等

嗯...這個方法呢,稍微有點沒效率,尤其是一直去查詢資料庫的部分。

不過,以我這個小網站來說,用起來確實是沒問題的~

Long Polling​

Long Polling 的原理是:Client 發送請求後,Worker 會等待一段時間(比如 30 秒),如果在這段時間內有新資料,就馬上回傳;如果時間到了還是沒資料,就直接回傳,然後再重新連接一次。

這個方法的問題是會一直占用 CPU,這樣非常浪費 Worker 的時間額度,所以感覺不太實際,就沒有去試了。

WebSocket​

WebSocket 是一般比較正規的選擇。

  • 資料儲存:使用 Cloudflare Durable Objects 的 SQLite 方案
  • 連接管理:客戶端通過 WebSocket 建立持久連接到 Durable Object 上面
  • 傳送訊息:接收到訊息後直接 broadcast 給所有連接的人
  • 聊天歷史:通過 state.get() 和 state.put() 等 Storage API 持久化到 SQLite

備註:如果是自己處理的話,就要架一個 WebSocket 伺服器;若想省事,就是用 Pusher 這類的第三方服務。

Bonus:交給別人來做​

直接在網頁中嵌入一個 Twitch 的聊天室就好了呀~(蛤?)

心得​

因為我這次只是想試試看可行性,最後就只有做 Polling 而已,因為它足夠簡單,也已經夠用了。

再加上,我原本查 Durable Objects 好像是要付費的,不過現在好像有免費方案了!

如果未來真的有需要,可以直接用一些現成的、別人已經寫好的 Cloudflare Durable Objects 版本,這樣就能直接爽用啦~

Windows 環境測試 Safari 瀏覽器

· 閱讀時間約 1 分鐘

WebKit​

前陣子在「DVD 彈跳動畫」這篇文章中,使用到 marquee 這個已經被棄用的 HTML 標籤,結果貼文發出後,才發現蘋果的 Safari 不支援這種作法,超級尷尬。

由於我手邊沒有蘋果的裝置可以拿來測試,於是在網路上搜尋了一下,發現在 Windows 的環境底下,可以透過 Playwright 內建的 Webkit 引擎來做測試。

有了這個方法之後,以後就很方便可以測試煩人的 WebKit(Safari 的引擎)啦~

筆記​

FFmpeg 壓縮影片

· 閱讀時間約 1 分鐘

FFmpeg​

上週在部落格上放了一些「復古遊戲的影片」。

我希望能夠兼顧支援度與畫質的同時,檔案也盡量不要太大。(Cloudflare Pages 檔案上限是 25 MB)

最後將影片都壓縮到 4 MB 左右,並維持 60 FPS 的流暢度,效果還算不錯。

我的小訣竅是:把編碼速度調到最慢,然後慢慢去調 CRF,直到「檔案大小」與「畫面品質」達到可接受的範圍就好囉~

筆記​