// 作品 / 詳情

OpenScore

通用運動計分與賽程引擎。Pure functional、離線優先、跨語言。

技術棧
elixirswifttypescriptkotlinphoenix
SCREENSHOT_001.png

一句話

一個任何運動都能用的計分與賽程引擎,核心是一行合約:apply(state, action, ruleset) → {state, events}

為什麼做這個

每個運動 app 都在重新發明計分邏輯,但計分的本質是純函數:給定當前狀態和一個動作,產生新狀態和事件。把這層抽出來,引擎就能跨運動、跨平台複用。

核心設計

Action-sourced,不是 event-sourced。 引擎是被動的 — 不追蹤時間,只對動作做反應。歷史是 action 序列,不是 event log。Undo = 從頭 replay 扣掉最後一個 action,不是反向操作。

State bubbling: 得分 → 遞迴向上檢查勝負條件。game_won → set_won → match_won,事件由下往上冒泡。

Ruleset 是宣告式的: 勝負條件、顯示格式、特殊行為全用 config 描述,不用程式碼。同一個引擎吃不同 ruleset 就能處理不同運動。

支援的運動

9 種運動、18+ 種規則集:

運動規則集
網球standard, grand slam, no-ad
羽球standard, doubles
籃球NBA, FIBA, 3x3
板式網球standard, golden point
匹克球rally scoring, side-out
桌球standard, short
排球standard
棒球MLB
壘球fastpitch

跨語言一致性

引擎用 4 種語言實作:Elixir(完整 9 種)、JavaScript、Swift、Kotlin。所有實作跑同一組 JSON conformance test(609 cases),確保行為一致。

三層架構

Platform Layer — Phoenix API + WebSocket + Astro 前端 + iOS (SwiftUI)
Schedule Layer — 賽程引擎(淘汰賽、雙敗、循環賽、瑞士制、分組)
Match Layer   — 單場計分引擎

賽程引擎處理 winner_of / loser_of 依賴解析:一場比賽結束 → 自動解析下游對陣 → 兩邊都到齊時 emit match_ready

即時同步

Phoenix Channels 處理即時計分同步。Match Channel 和 Session Channel 讓多人同時觀看同一場比賽的 live score。iOS app 透過 WebSocket 與 server 保持同步。