從 404 到棋局同步:TG+OpenCode 打磨 Chess Engine

西洋棋應用同時牽涉前端資源、規則、搜尋與狀態同步;任何一層出錯,都可能讓畫面看起來正常卻下出錯誤的棋。

一、需求:先做出能玩的棋盤

從一個「能下棋」的目標開始

本案例是 Agent 平台端到端驗收的內部示範,用來驗證前端、後端、第三方 binary 與容器 runtime 的整合可靠度,非對外產品。

2026 年 5 月 21 日,任務從 Telegram 裡一個新的 Chessboard 專案開始。需求表面上很簡單:使用者用瀏覽器下棋,黑方由電腦回應,還要有時間控制、標準西洋棋規則,以及可以觀察穩定性的自動對弈測試入口。

問題不只在棋盤上

在透過自然語言對喜桶提出需求後,系統很快便做出了初版,包含網頁服務、自行撰寫的棋步搜尋,以及瀏覽器棋盤元件。模型很快回報「棋盤能下、引擎能自弈」,但事件順序顯示那只是部分成立:API可以回傳合法走子,瀏覽器卻先出現棋子不見、外部圖片請求錯誤,後面還有引擎速度、升變和狀態同步問題。

這些問題不是互不相干的待辦清單。錯誤的圖片路徑會讓畫面缺棋子;舊服務或快取會讓修正看似沒有生效;前端先推進一步、後端沒有收到同一步,下一次引擎搜尋就會在另一個 FEN(描述完整棋局的文字格式)上運算。只有把事件按時間排開,才能看出這是一條跨層的除錯鏈。

Harness 驗收機制暴露出的問題

在模型交付前,工程師設計的 Harness 驗收機制暴露出一個很難靠畫面發現的問題:電腦棋手思考下一步時,有個判斷被重複執行,讓思考才剛開始就被當成「沒有結果」而提前結束。畫面和服務都沒有報錯,甚至仍可能回傳一個走法;但搜尋紀錄顯示,該次分析只比較了約四種可能局面,幾乎沒有真正展開所有的可能性之後去選擇比較好的那步。Harness 回頭檢查系統如何刪去不值得繼續分析的走法,才找到這個提前結束的原因;修正後,分析量才逐層恢復到數十、數百,再到約 2,000 種可能局面。這個例子說明,工程化的驗收流程可以在第一版交付前,先從程式行為與分析紀錄中暴露出表面畫面看不出來的問題。

模型聲稱完成後的人工驗證

相對地,在系統聲稱他做完了之後,我們需要再人工驗證一遍他做好的東西是否完整.人工驗收看到的是:外部棋子圖片被瀏覽器的安全機制擋下、有一個找不到的檔案是分頁小圖示。紀錄裡分別標成 ORB/CORB(跨來源安全阻擋)與 HTTP 404(檔案找不到),此外還有舊服務/瀏覽器快取、搜尋等待時間、非兵升變成皇后等問題。這些都是模型說「已完成」後,我們再人工重跑才暴露出來的。先有完成宣告,後有人工驗收,驗收結果再反過來要求系統修正。以下是我們人工驗證發現的問題。

第一個圖片載入問題:從畫面追到檔案

第一個問題是棋子圖片沒有正常顯示。我們從 Network(網路請求面板)發現了兩個圖片相關問題:第一是系統本來想要使用外部棋子圖片來顯示棋子,但被瀏覽器的跨來源安全機制擋下(ORB/CORB);另一個是網站小圖示(favicon)是找不到的檔案(HTTP 404)。

所以我們決定修正路線改用直接放在棋盤格子裡的西洋棋符號(♔ ♕ ♖ ♗ ♘ ♙/♚ ♛ ♜ ♝ ♞ ♟),不再依賴外部棋子圖片。中間還有一次「白棋/黑棋的標記方式」對不上,棋子明明存在頁面裡卻沒有顯示;把兩邊的標記統一後,DevTools 才看見 32 個棋子。

二、規則問題:非兵被誤當成皇后

升變問題是下一個明顯的錯誤,也最能說明「模型聲稱完成」不代表規則正確。初版把只要棋子走到第一排或第八排,都當成可以升變;結果馬或王走到邊線時,也被錯誤地變成第二個皇后。

人工回饋

圖片問題處理完後,我們把規則錯誤具體回報給系統:在同一個棋局裡,馬或主教走到最後一排,不應該突然變成皇后;只有兵才可以升變,而且要走到底才會觸發。這樣的回饋不是籠統地說「規則有問題」,而是直接指出哪個棋子、走到哪裡,以及我們預期看到什麼結果。系統用同一個局面重播這些走法,對照網站服務的回應和棋規檢查工具的結果,讀程式後確認真正原因:系統把所有走到最後一排的棋子都套用了「升變成皇后」的處理。

系統修正/重新驗證

系統收到回饋後,修改升變判斷,讓玩家和電腦棋手的兩條走子路徑都先確認移動的棋子是不是兵。重新驗證時,系統自己使用同一組局面測試三種情況:白兵走到第八排、黑兵走到第一排可以正常升變;王、馬、主教走到邊線則維持原本的棋子。只有測試結果和回報的症狀一一對上,這個規則修正才算完成。

三、自製搜尋:可行不等於能交付

先把「看見的」和「實際的」分開

圖片與規則問題處理後,下一個瓶頸轉到自製的棋步搜尋。最初的搜尋看起來已經會評估局面、排除不好的選擇;實測卻是起始局分析三層約 1.03 秒、四層約 9.58 秒,中局分析四層約 28 秒,再多分析一層就可能超過可接受的等待時間。因此我們人工向系統提出這個引擎計算時間過長的問題

系統曾嘗試記住已經算過的局面、優先檢查較有希望的走法、跳過不必要的延伸,並逐層加深分析。這些調整確實讓某些中局分析六層從接近 60 秒提升至約 5.5 秒;但就算使用較淺的分析,自動對弈在較深設定仍常遇到逾時或長時間運算。這證明「有加速方法」不等於「已達到產品回應速度」。

四、為什麼最後改用 Stockfish

引擎也要有可靠的接法

切換 Stockfish 不是因為自製搜尋完全不能動,而是因為它在可接受的回應時間內只能分析有限範圍,長時間測試也暴露了多個搜尋與逾時邊界。這就是後來改用 Stockfish(現成的西洋棋引擎)的原因。網路上開源的 Stockfish 已經提供成熟的搜尋和時間控制方式,網站可以保留原本的 API 與棋規,把引擎換成一個更穩定的元件。

為了確認 Stockfish 在我們電腦上運行的效能,我們先把幾個不同局面交給 Stockfish,它能依局面回傳走法,並在約三秒思考時間內完成;確定可行後我們才讓系統把玩家落子與自動對弈功能接上。

五、前端、FEN 與引擎的同步

API 與瀏覽器雙重驗證

我們的網站應用程式同時有三份局面:瀏覽器裡的棋局、伺服器裡的棋局,以及 Stockfish 收到的局面文字(FEN)。這一段人工驗收真正要確認的是,畫面、網站服務與電腦棋手掌握的是否為同一盤棋。初版的同步問題是重新載入頁面後,瀏覽器回到初始局面,伺服器卻保留舊棋局,所以我們人工在玩的時候發現電腦走出了一些犯規的棋,回報系統後系統才發現這個問題;修正後,頁面載入先向後端要求重新開始,再用後端回傳的完整局面重建畫面。如此一來前端就不用保留任何狀態,狀態都由後端發送。

另一個同步問題出現在網路請求失敗時:畫面備援只更新瀏覽器裡的棋局,沒有同步伺服器、Stockfish 與 FEN。也就是玩家眼前看見的棋盤往前走了,但後端和電腦棋手仍停在前一個局面,下一步就可能從錯誤的棋局開始計算。

另一個具體例子是引擎觸發時機的問題:人工驗證的時候發現引擎有時候連續走了兩步,回報給系統後發現前端原本只送出「請引擎走棋」的簡單指令,後端卻沒有先確認玩家剛才的落子已經完成,所以引擎可能在不該動時被意外呼叫。後來改成先把玩家的走法交給後端,後端才可以確認局面更新並同步 Stockfish,再讓引擎回應;悔棋也改成由後端回傳完整局面,讓前端重新載入。

六、Chrome DevTools 如何找出問題

Chrome DevTools 的價值不只在截圖。他還有頁面檢查可以證明 64 格是否真的存在、32 個棋子是否真的被渲染;Network 可以區分外部圖片被瀏覽器擋下、圖示檔案找不到,以及重新開始、落子和自動對弈等 API 請求各自成功或失敗;Console 則把跨來源問題、檔案找不到(404)、伺服器處理失敗(500)問題留下來。

瀏覽器的即時檢查還能把畫面操作和狀態資料並排:讀取棋局的 FEN、走子紀錄和合法走法,再和 API 回傳與 game log 對照。這也能排除假線索,例如某次被懷疑的吃子其實是當時馬的位置本來就不能走到那一格。瀏覽器驗收的重點不是「看起來像棋盤」,而是讓畫面、網路請求和 Console 的事實彼此吻合。

西洋棋 Web App 的棋盤畫面
西洋棋案例的第一層驗收是畫面:棋盤格、棋子、資源載入與瀏覽器 Console 必須先對齊,後面才談得上引擎與棋局同步。

七、換到 docker 裡面後:引擎檔案能取得不等於能執行

後續我們將這個系統放到環境隔離的 docker 容器內。把網站放進去後,Stockfish 又遇到一次環境問題:下載的引擎檔案是另一種處理器架構,和實際執行它的 ARM 機器不相容;暫存資料夾也禁止直接執行程式,導致即使編譯成功仍無法啟動。這解釋了為什麼頁面可以開,按下自動對弈卻只得到錯誤。

最後改用符合伺服器所使用處理器架構(ARM)的 Stockfish,在原本的環境重新編譯後,將檔案放到可以執行且不會隨容器重建消失的位置,並準備好網頁服務、棋規檢查工具與引擎連接元件。服務對外提供後,瀏覽器才可以存取;引擎直接啟動能回應 Stockfish 18,自動對弈介面也曾從錯誤變成成功完成一小段棋局,這只是一個環境修復後的短局證據。

若未來涉及對外散布 Stockfish 執行檔或原始碼,需另行確認並遵守其開源授權義務;本案例重點是內部驗收第三方 binary 與容器 runtime 的接合方式。

收穫:從純前端遊戲,進到系統整合

有證據支持的完成狀態

這個案例比踩地雷更進階的地方,不只是規則更複雜,而是系統形態完全不同。踩地雷主要是一個純前端遊戲:使用者操作、畫面狀態和遊戲規則大多在同一個瀏覽器環境裡完成。西洋棋則變成前端、後端、第三方 binary 的整合題:瀏覽器負責操作與顯示,後端負責保存棋局和處理 API,Stockfish 則作為獨立的電腦棋手在系統外部執行。

一旦系統跨到這三層,真正困難的地方就不只是「棋盤能不能下」,而是每一層是否掌握同一個局面。棋盤畫面、後端棋局、FEN、Stockfish position、走子紀錄和容器裡的執行環境,都可能各自看起來正常;但只要其中一層慢了一步,使用者就會看到能落子的畫面,系統卻在另一盤棋上思考。

因此,FEN 在這裡不是單純的資料格式,而是跨層狀態合約。API 證明的是:重新開始、玩家落子、悔棋、查詢局面和自動對弈等功能是否回應、狀態碼是否合理、回應是否含 FEN/走法/遊戲狀態;API 不單獨證明畫面已渲染,也不證明瀏覽器沒有快取舊資源。FEN 證明的是某一瞬間的完整局面與回合,可用來比對瀏覽器、伺服器和 Stockfish 收到的局面;FEN 本身不是歷史紀錄。

game log(伺服器事件紀錄,也就是伺服器操作紀錄)補上的,是狀態變化的順序:什麼時候收到玩家走棋、什麼時候更新局面、什麼時候啟動引擎、Stockfish 回了哪一步。瀏覽器證明的是另一件事:棋盤格和棋子是否真的出現、落子後畫面是否更新、網路請求是否成功,以及 Console 是否仍有錯誤。這些證據不是互相替代,而是用來回答同一個問題:畫面、網站服務與電腦棋手掌握的是否為同一盤棋。

結果:把 AI 產出接進真實系統

所以這篇的 takeaway 更接近系統整合:AI 可以很快做出一個會動的棋盤,也可以快速嘗試自製搜尋、接上 Stockfish、補 UI 和 API;但只要牽涉前端、後端、第三方 binary、瀏覽器快取、容器環境和多層狀態,交付標準就不能停在「看起來可以下」。真正可交付的是一套邊界清楚、資料流清楚、每一步都有可追溯證據的工作流。

這個案例真正完成的是經過多輪錯誤暴露後,整理出可以重用的整合原則:外部資源問題要由 DevTools 拆解;引擎效能要用實測決定是否換用 Stockfish;升變與同步要有針對性測試和 FEN 對照;容器化後還要確認 binary 架構、執行位置與短局驗收。

進階的重點不是讓 AI 寫出更多前端互動,而是讓它完成前端、後端與第三方 binary 之間的可靠整合。
返回文章列表 回到首頁