d1 x d1 blackjack battle recap
Browse files- README.md +16 -4
- battle.json +3415 -0
- index.html +672 -17
- source/blackjack.py +535 -0
- source/build_site.py +753 -0
- source/run_demo.sh +42 -0
README.md
CHANGED
|
@@ -1,10 +1,22 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
colorFrom: green
|
| 5 |
-
colorTo:
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: d1demo
|
| 3 |
+
emoji: 🃏
|
| 4 |
colorFrom: green
|
| 5 |
+
colorTo: yellow
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
+
license: mit
|
| 9 |
---
|
| 10 |
|
| 11 |
+
# d1 × d1 21 點對戰實錄
|
| 12 |
+
|
| 13 |
+
兩個 **LiquidAI d1-3B** 決策模型,各自跑在一個 llama.cpp server 上,在同一張 21 點牌桌上對打。每一次下注、補牌、停牌、加倍,都是模型輸出的機率分佈;這頁把每一步整理成可回放的視覺化紀錄。
|
| 14 |
+
|
| 15 |
+
- 模型:`LiquidAI/d1-3B-GGUF` (Q8_0),llama.cpp b11786
|
| 16 |
+
- API:`POST /v1/systemone`(d1 是 decision model,不是文字生成模型)
|
| 17 |
+
- 莊家:規則操作,S17,6 副牌,BJ 3:2
|
| 18 |
+
- 內容:對戰時間軸、決策機率、風險評分、信心度、延遲、資金曲線、動作統計
|
| 19 |
+
- 原始碼:`source/blackjack.py`(對戰 + 記錄)、`source/build_site.py`(靜態網頁編譯)
|
| 20 |
+
- 原始資料:`battle.json`
|
| 21 |
+
|
| 22 |
+
頁面是靜態的,所有資料都在 `battle.json`;`build_site.py` 可重新編譯整站。
|
battle.json
ADDED
|
@@ -0,0 +1,3415 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"meta": {
|
| 3 |
+
"title": "d1 × d1 21 點對戰實錄",
|
| 4 |
+
"subtitle": "兩個 LiquidAI d1-3B 決策模型,透過 llama.cpp 在同一個牌桌上對打",
|
| 5 |
+
"created_at": "2026-10-21T11:09:42+02:00",
|
| 6 |
+
"seed": 20261021,
|
| 7 |
+
"model": "LiquidAI/d1-3B-GGUF",
|
| 8 |
+
"quant": "Q8_0",
|
| 9 |
+
"llama_build": "b11786 (0.7.0-dev)",
|
| 10 |
+
"rules": {
|
| 11 |
+
"decks": 6,
|
| 12 |
+
"dealer": "莊家軟 17 停牌 (S17)",
|
| 13 |
+
"blackjack_pays": "3:2",
|
| 14 |
+
"double": "僅限硬 9–11 或軟 13–18 時加倍,加倍後只補一張",
|
| 15 |
+
"split": "本局不開放分牌",
|
| 16 |
+
"insurance": "不開放保險",
|
| 17 |
+
"reshuffle": "剩餘牌數低於 25% 時重洗",
|
| 18 |
+
"start_bankroll": 1000
|
| 19 |
+
},
|
| 20 |
+
"start_bankroll": 1000,
|
| 21 |
+
"agents": [
|
| 22 |
+
{
|
| 23 |
+
"id": "a",
|
| 24 |
+
"name": "阿保",
|
| 25 |
+
"style": "保守算牌派",
|
| 26 |
+
"description": "以保住本金為第一優先,只有在牌面明顯有利時才加注,遇到風險寧可停牌。",
|
| 27 |
+
"url": "http://127.0.0.1:8081",
|
| 28 |
+
"final_bankroll": 940
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
"id": "b",
|
| 32 |
+
"name": "阿衝",
|
| 33 |
+
"style": "激進壓迫派",
|
| 34 |
+
"description": "相信進攻是最好的防守,喜歡加倍與大注,願意為了期望值承擔波動。",
|
| 35 |
+
"url": "http://127.0.0.1:8082",
|
| 36 |
+
"final_bankroll": 900
|
| 37 |
+
}
|
| 38 |
+
],
|
| 39 |
+
"rounds_played": 12,
|
| 40 |
+
"duration_sec": 161.39,
|
| 41 |
+
"inference": {
|
| 42 |
+
"a": {
|
| 43 |
+
"calls": 27,
|
| 44 |
+
"avg_latency_ms": 2589
|
| 45 |
+
},
|
| 46 |
+
"b": {
|
| 47 |
+
"calls": 27,
|
| 48 |
+
"avg_latency_ms": 3387
|
| 49 |
+
}
|
| 50 |
+
}
|
| 51 |
+
},
|
| 52 |
+
"rounds": [
|
| 53 |
+
{
|
| 54 |
+
"n": 1,
|
| 55 |
+
"shoe_before": {
|
| 56 |
+
"decks": 6,
|
| 57 |
+
"remaining_cards": 306,
|
| 58 |
+
"penetration_pct": 2,
|
| 59 |
+
"cards_left_by_rank": {
|
| 60 |
+
"A": 23,
|
| 61 |
+
"2": 24,
|
| 62 |
+
"3": 23,
|
| 63 |
+
"4": 24,
|
| 64 |
+
"5": 24,
|
| 65 |
+
"6": 23,
|
| 66 |
+
"7": 23,
|
| 67 |
+
"8": 24,
|
| 68 |
+
"9": 24,
|
| 69 |
+
"10": 24,
|
| 70 |
+
"J": 24,
|
| 71 |
+
"Q": 23,
|
| 72 |
+
"K": 23
|
| 73 |
+
},
|
| 74 |
+
"hi_lo_running_count": -1,
|
| 75 |
+
"true_count": -0.17
|
| 76 |
+
},
|
| 77 |
+
"dealer_up": "A♣",
|
| 78 |
+
"dealer_hole": "K♣",
|
| 79 |
+
"dealer_revealed": true,
|
| 80 |
+
"dealer": {
|
| 81 |
+
"cards": [
|
| 82 |
+
"A♣",
|
| 83 |
+
"K♣"
|
| 84 |
+
],
|
| 85 |
+
"total": 21,
|
| 86 |
+
"soft": true,
|
| 87 |
+
"blackjack": true,
|
| 88 |
+
"bust": false
|
| 89 |
+
},
|
| 90 |
+
"events": [
|
| 91 |
+
"莊家明牌為 A 或 10 點,進行暗牌確認",
|
| 92 |
+
"莊家直接拿到 Blackjack,本局立即結束"
|
| 93 |
+
],
|
| 94 |
+
"players": [
|
| 95 |
+
{
|
| 96 |
+
"cards": [
|
| 97 |
+
"7♥",
|
| 98 |
+
"3♣"
|
| 99 |
+
],
|
| 100 |
+
"bet": 10,
|
| 101 |
+
"doubled": false,
|
| 102 |
+
"blackjack": false,
|
| 103 |
+
"bust": false,
|
| 104 |
+
"total": 10,
|
| 105 |
+
"soft": false,
|
| 106 |
+
"result": "lose",
|
| 107 |
+
"net": -10,
|
| 108 |
+
"bet_decision": {
|
| 109 |
+
"decision": {
|
| 110 |
+
"type": "choice",
|
| 111 |
+
"choice": "min",
|
| 112 |
+
"probabilities": {
|
| 113 |
+
"min": 0.8015038103456409,
|
| 114 |
+
"standard": 0.13503222188459674,
|
| 115 |
+
"max": 0.06346396776976232
|
| 116 |
+
},
|
| 117 |
+
"confidence": 0.7022557155184613
|
| 118 |
+
},
|
| 119 |
+
"bet": 10,
|
| 120 |
+
"confidence": {
|
| 121 |
+
"type": "score",
|
| 122 |
+
"score": 1.2743893137525868,
|
| 123 |
+
"legend": {
|
| 124 |
+
"0": "沒信心",
|
| 125 |
+
"1": "普通",
|
| 126 |
+
"2": "有信心",
|
| 127 |
+
"3": "非常有信心"
|
| 128 |
+
},
|
| 129 |
+
"probabilities": {
|
| 130 |
+
"0": 0.177727990768028,
|
| 131 |
+
"1": 0.48287248232676894,
|
| 132 |
+
"2": 0.22668174928979082,
|
| 133 |
+
"3": 0.11271777761541213
|
| 134 |
+
},
|
| 135 |
+
"confidence": 0.3701547047113569
|
| 136 |
+
},
|
| 137 |
+
"latency_ms": 2619
|
| 138 |
+
},
|
| 139 |
+
"bankroll_after": 990,
|
| 140 |
+
"id": "a",
|
| 141 |
+
"name": "阿保",
|
| 142 |
+
"style": "保守算牌派"
|
| 143 |
+
},
|
| 144 |
+
{
|
| 145 |
+
"cards": [
|
| 146 |
+
"Q♦",
|
| 147 |
+
"6♠"
|
| 148 |
+
],
|
| 149 |
+
"bet": 50,
|
| 150 |
+
"doubled": false,
|
| 151 |
+
"blackjack": false,
|
| 152 |
+
"bust": false,
|
| 153 |
+
"total": 16,
|
| 154 |
+
"soft": false,
|
| 155 |
+
"result": "lose",
|
| 156 |
+
"net": -50,
|
| 157 |
+
"bet_decision": {
|
| 158 |
+
"decision": {
|
| 159 |
+
"type": "choice",
|
| 160 |
+
"choice": "max",
|
| 161 |
+
"probabilities": {
|
| 162 |
+
"min": 0.22510969265696443,
|
| 163 |
+
"standard": 0.3522666589432454,
|
| 164 |
+
"max": 0.42262364839979016
|
| 165 |
+
},
|
| 166 |
+
"confidence": 0.13393547259968525
|
| 167 |
+
},
|
| 168 |
+
"bet": 50,
|
| 169 |
+
"confidence": {
|
| 170 |
+
"type": "score",
|
| 171 |
+
"score": 1.4739367761682,
|
| 172 |
+
"legend": {
|
| 173 |
+
"0": "沒信心",
|
| 174 |
+
"1": "普通",
|
| 175 |
+
"2": "有信心",
|
| 176 |
+
"3": "非常有信心"
|
| 177 |
+
},
|
| 178 |
+
"probabilities": {
|
| 179 |
+
"0": 0.16212488082911708,
|
| 180 |
+
"1": 0.364637000115999,
|
| 181 |
+
"2": 0.3104145811124513,
|
| 182 |
+
"3": 0.16282353794243282
|
| 183 |
+
},
|
| 184 |
+
"confidence": 0.201813462173566
|
| 185 |
+
},
|
| 186 |
+
"latency_ms": 2899
|
| 187 |
+
},
|
| 188 |
+
"bankroll_after": 950,
|
| 189 |
+
"id": "b",
|
| 190 |
+
"name": "阿衝",
|
| 191 |
+
"style": "激進壓迫派"
|
| 192 |
+
}
|
| 193 |
+
],
|
| 194 |
+
"shoe_after": {
|
| 195 |
+
"decks": 6,
|
| 196 |
+
"remaining_cards": 306,
|
| 197 |
+
"penetration_pct": 2,
|
| 198 |
+
"cards_left_by_rank": {
|
| 199 |
+
"A": 23,
|
| 200 |
+
"2": 24,
|
| 201 |
+
"3": 23,
|
| 202 |
+
"4": 24,
|
| 203 |
+
"5": 24,
|
| 204 |
+
"6": 23,
|
| 205 |
+
"7": 23,
|
| 206 |
+
"8": 24,
|
| 207 |
+
"9": 24,
|
| 208 |
+
"10": 24,
|
| 209 |
+
"J": 24,
|
| 210 |
+
"Q": 23,
|
| 211 |
+
"K": 23
|
| 212 |
+
},
|
| 213 |
+
"hi_lo_running_count": -1,
|
| 214 |
+
"true_count": -0.17
|
| 215 |
+
}
|
| 216 |
+
},
|
| 217 |
+
{
|
| 218 |
+
"n": 2,
|
| 219 |
+
"shoe_before": {
|
| 220 |
+
"decks": 6,
|
| 221 |
+
"remaining_cards": 300,
|
| 222 |
+
"penetration_pct": 4,
|
| 223 |
+
"cards_left_by_rank": {
|
| 224 |
+
"A": 22,
|
| 225 |
+
"2": 24,
|
| 226 |
+
"3": 22,
|
| 227 |
+
"4": 24,
|
| 228 |
+
"5": 23,
|
| 229 |
+
"6": 23,
|
| 230 |
+
"7": 23,
|
| 231 |
+
"8": 23,
|
| 232 |
+
"9": 24,
|
| 233 |
+
"10": 23,
|
| 234 |
+
"J": 24,
|
| 235 |
+
"Q": 23,
|
| 236 |
+
"K": 22
|
| 237 |
+
},
|
| 238 |
+
"hi_lo_running_count": -2,
|
| 239 |
+
"true_count": -0.35
|
| 240 |
+
},
|
| 241 |
+
"dealer_up": "8♣",
|
| 242 |
+
"dealer_hole": "A♠",
|
| 243 |
+
"dealer_revealed": true,
|
| 244 |
+
"dealer": {
|
| 245 |
+
"cards": [
|
| 246 |
+
"8♣",
|
| 247 |
+
"A♠"
|
| 248 |
+
],
|
| 249 |
+
"total": 19,
|
| 250 |
+
"soft": true,
|
| 251 |
+
"blackjack": false,
|
| 252 |
+
"bust": false
|
| 253 |
+
},
|
| 254 |
+
"events": [
|
| 255 |
+
"莊家最終 19 點"
|
| 256 |
+
],
|
| 257 |
+
"players": [
|
| 258 |
+
{
|
| 259 |
+
"cards": [
|
| 260 |
+
"5♠",
|
| 261 |
+
"3♥",
|
| 262 |
+
"9♠"
|
| 263 |
+
],
|
| 264 |
+
"bet": 10,
|
| 265 |
+
"doubled": false,
|
| 266 |
+
"total": 17,
|
| 267 |
+
"soft": false,
|
| 268 |
+
"bust": false,
|
| 269 |
+
"blackjack": false,
|
| 270 |
+
"steps": [
|
| 271 |
+
{
|
| 272 |
+
"type": "action",
|
| 273 |
+
"cards": [
|
| 274 |
+
"5♠",
|
| 275 |
+
"3♥"
|
| 276 |
+
],
|
| 277 |
+
"total": 8,
|
| 278 |
+
"soft": false,
|
| 279 |
+
"dealer_up": "8♣",
|
| 280 |
+
"allowed": [
|
| 281 |
+
"hit",
|
| 282 |
+
"stand"
|
| 283 |
+
],
|
| 284 |
+
"decision": {
|
| 285 |
+
"type": "choice",
|
| 286 |
+
"choice": "hit",
|
| 287 |
+
"probabilities": {
|
| 288 |
+
"hit": 0.7903426468089709,
|
| 289 |
+
"stand": 0.2096573531910292
|
| 290 |
+
},
|
| 291 |
+
"confidence": 0.5806852936179419
|
| 292 |
+
},
|
| 293 |
+
"risk": {
|
| 294 |
+
"type": "score",
|
| 295 |
+
"score": 2.1705379533840112,
|
| 296 |
+
"legend": {
|
| 297 |
+
"0": "非常有利",
|
| 298 |
+
"1": "稍微有利",
|
| 299 |
+
"2": "有點不利",
|
| 300 |
+
"3": "非常不利"
|
| 301 |
+
},
|
| 302 |
+
"probabilities": {
|
| 303 |
+
"0": 0.04566941566120645,
|
| 304 |
+
"1": 0.12003841453217244,
|
| 305 |
+
"2": 0.4523769705680244,
|
| 306 |
+
"3": 0.38191519923859674
|
| 307 |
+
},
|
| 308 |
+
"confidence": 0.406707554906818
|
| 309 |
+
},
|
| 310 |
+
"latency_ms": 3068,
|
| 311 |
+
"drawn": "9♠",
|
| 312 |
+
"total_after": 17
|
| 313 |
+
},
|
| 314 |
+
{
|
| 315 |
+
"type": "action",
|
| 316 |
+
"cards": [
|
| 317 |
+
"5♠",
|
| 318 |
+
"3♥",
|
| 319 |
+
"9♠"
|
| 320 |
+
],
|
| 321 |
+
"total": 17,
|
| 322 |
+
"soft": false,
|
| 323 |
+
"dealer_up": "8♣",
|
| 324 |
+
"allowed": [
|
| 325 |
+
"hit",
|
| 326 |
+
"stand"
|
| 327 |
+
],
|
| 328 |
+
"decision": {
|
| 329 |
+
"type": "choice",
|
| 330 |
+
"choice": "stand",
|
| 331 |
+
"probabilities": {
|
| 332 |
+
"hit": 0.34748348200597706,
|
| 333 |
+
"stand": 0.6525165179940229
|
| 334 |
+
},
|
| 335 |
+
"confidence": 0.30503303598804576
|
| 336 |
+
},
|
| 337 |
+
"risk": {
|
| 338 |
+
"type": "score",
|
| 339 |
+
"score": 1.3057660239341171,
|
| 340 |
+
"legend": {
|
| 341 |
+
"0": "非常有利",
|
| 342 |
+
"1": "稍微有利",
|
| 343 |
+
"2": "有點不利",
|
| 344 |
+
"3": "非常不利"
|
| 345 |
+
},
|
| 346 |
+
"probabilities": {
|
| 347 |
+
"0": 0.1680177506175406,
|
| 348 |
+
"1": 0.4059181822464935,
|
| 349 |
+
"2": 0.3783443597202743,
|
| 350 |
+
"3": 0.04771970741569162
|
| 351 |
+
},
|
| 352 |
+
"confidence": 0.3581984748308018
|
| 353 |
+
},
|
| 354 |
+
"latency_ms": 2844,
|
| 355 |
+
"drawn": null,
|
| 356 |
+
"total_after": 17
|
| 357 |
+
}
|
| 358 |
+
],
|
| 359 |
+
"player_id": "a",
|
| 360 |
+
"result": "lose",
|
| 361 |
+
"net": -10,
|
| 362 |
+
"bet_decision": {
|
| 363 |
+
"decision": {
|
| 364 |
+
"type": "choice",
|
| 365 |
+
"choice": "min",
|
| 366 |
+
"probabilities": {
|
| 367 |
+
"min": 0.7725987262844797,
|
| 368 |
+
"standard": 0.1413424593652034,
|
| 369 |
+
"max": 0.08605881435031675
|
| 370 |
+
},
|
| 371 |
+
"confidence": 0.6588980894267195
|
| 372 |
+
},
|
| 373 |
+
"bet": 10,
|
| 374 |
+
"confidence": {
|
| 375 |
+
"type": "score",
|
| 376 |
+
"score": 1.3006956637914562,
|
| 377 |
+
"legend": {
|
| 378 |
+
"0": "沒信心",
|
| 379 |
+
"1": "普通",
|
| 380 |
+
"2": "有信心",
|
| 381 |
+
"3": "非常有信心"
|
| 382 |
+
},
|
| 383 |
+
"probabilities": {
|
| 384 |
+
"0": 0.16014576713262269,
|
| 385 |
+
"1": 0.4790571948548963,
|
| 386 |
+
"2": 0.2607526451008829,
|
| 387 |
+
"3": 0.100044392911598
|
| 388 |
+
},
|
| 389 |
+
"confidence": 0.37901280194329834
|
| 390 |
+
},
|
| 391 |
+
"latency_ms": 2449
|
| 392 |
+
},
|
| 393 |
+
"bankroll_after": 980,
|
| 394 |
+
"id": "a",
|
| 395 |
+
"name": "阿保",
|
| 396 |
+
"style": "保守算牌派"
|
| 397 |
+
},
|
| 398 |
+
{
|
| 399 |
+
"cards": [
|
| 400 |
+
"K♦",
|
| 401 |
+
"10♣"
|
| 402 |
+
],
|
| 403 |
+
"bet": 50,
|
| 404 |
+
"doubled": false,
|
| 405 |
+
"total": 20,
|
| 406 |
+
"soft": false,
|
| 407 |
+
"bust": false,
|
| 408 |
+
"blackjack": false,
|
| 409 |
+
"steps": [
|
| 410 |
+
{
|
| 411 |
+
"type": "action",
|
| 412 |
+
"cards": [
|
| 413 |
+
"K♦",
|
| 414 |
+
"10♣"
|
| 415 |
+
],
|
| 416 |
+
"total": 20,
|
| 417 |
+
"soft": false,
|
| 418 |
+
"dealer_up": "8♣",
|
| 419 |
+
"allowed": [
|
| 420 |
+
"hit",
|
| 421 |
+
"stand"
|
| 422 |
+
],
|
| 423 |
+
"decision": {
|
| 424 |
+
"type": "choice",
|
| 425 |
+
"choice": "stand",
|
| 426 |
+
"probabilities": {
|
| 427 |
+
"hit": 0.29523031516652504,
|
| 428 |
+
"stand": 0.704769684833475
|
| 429 |
+
},
|
| 430 |
+
"confidence": 0.40953936966695004
|
| 431 |
+
},
|
| 432 |
+
"risk": {
|
| 433 |
+
"type": "score",
|
| 434 |
+
"score": 1.1019380316438792,
|
| 435 |
+
"legend": {
|
| 436 |
+
"0": "非常有利",
|
| 437 |
+
"1": "稍微有利",
|
| 438 |
+
"2": "有點不利",
|
| 439 |
+
"3": "非常不利"
|
| 440 |
+
},
|
| 441 |
+
"probabilities": {
|
| 442 |
+
"0": 0.270273972567917,
|
| 443 |
+
"1": 0.3948283551800062,
|
| 444 |
+
"2": 0.2975833402923578,
|
| 445 |
+
"3": 0.03731433195971913
|
| 446 |
+
},
|
| 447 |
+
"confidence": 0.3575140232202869
|
| 448 |
+
},
|
| 449 |
+
"latency_ms": 4289,
|
| 450 |
+
"drawn": null,
|
| 451 |
+
"total_after": 20
|
| 452 |
+
}
|
| 453 |
+
],
|
| 454 |
+
"player_id": "b",
|
| 455 |
+
"result": "win",
|
| 456 |
+
"net": 50,
|
| 457 |
+
"bet_decision": {
|
| 458 |
+
"decision": {
|
| 459 |
+
"type": "choice",
|
| 460 |
+
"choice": "max",
|
| 461 |
+
"probabilities": {
|
| 462 |
+
"min": 0.19967317203877727,
|
| 463 |
+
"standard": 0.3811921558998119,
|
| 464 |
+
"max": 0.4191346720614108
|
| 465 |
+
},
|
| 466 |
+
"confidence": 0.1287020080921162
|
| 467 |
+
},
|
| 468 |
+
"bet": 50,
|
| 469 |
+
"confidence": {
|
| 470 |
+
"type": "score",
|
| 471 |
+
"score": 1.5668315252239973,
|
| 472 |
+
"legend": {
|
| 473 |
+
"0": "沒信心",
|
| 474 |
+
"1": "普通",
|
| 475 |
+
"2": "有信心",
|
| 476 |
+
"3": "非常有信心"
|
| 477 |
+
},
|
| 478 |
+
"probabilities": {
|
| 479 |
+
"0": 0.14124839285420554,
|
| 480 |
+
"1": 0.3269367548074964,
|
| 481 |
+
"2": 0.3555497865983932,
|
| 482 |
+
"3": 0.17626506573990477
|
| 483 |
+
},
|
| 484 |
+
"confidence": 0.21430139374418777
|
| 485 |
+
},
|
| 486 |
+
"latency_ms": 2759
|
| 487 |
+
},
|
| 488 |
+
"bankroll_after": 1000,
|
| 489 |
+
"id": "b",
|
| 490 |
+
"name": "阿衝",
|
| 491 |
+
"style": "激進壓迫派"
|
| 492 |
+
}
|
| 493 |
+
],
|
| 494 |
+
"shoe_after": {
|
| 495 |
+
"decks": 6,
|
| 496 |
+
"remaining_cards": 299,
|
| 497 |
+
"penetration_pct": 4,
|
| 498 |
+
"cards_left_by_rank": {
|
| 499 |
+
"A": 22,
|
| 500 |
+
"2": 24,
|
| 501 |
+
"3": 22,
|
| 502 |
+
"4": 24,
|
| 503 |
+
"5": 23,
|
| 504 |
+
"6": 23,
|
| 505 |
+
"7": 23,
|
| 506 |
+
"8": 23,
|
| 507 |
+
"9": 23,
|
| 508 |
+
"10": 23,
|
| 509 |
+
"J": 24,
|
| 510 |
+
"Q": 23,
|
| 511 |
+
"K": 22
|
| 512 |
+
},
|
| 513 |
+
"hi_lo_running_count": -2,
|
| 514 |
+
"true_count": -0.35
|
| 515 |
+
}
|
| 516 |
+
},
|
| 517 |
+
{
|
| 518 |
+
"n": 3,
|
| 519 |
+
"shoe_before": {
|
| 520 |
+
"decks": 6,
|
| 521 |
+
"remaining_cards": 293,
|
| 522 |
+
"penetration_pct": 6,
|
| 523 |
+
"cards_left_by_rank": {
|
| 524 |
+
"A": 22,
|
| 525 |
+
"2": 22,
|
| 526 |
+
"3": 21,
|
| 527 |
+
"4": 24,
|
| 528 |
+
"5": 23,
|
| 529 |
+
"6": 22,
|
| 530 |
+
"7": 23,
|
| 531 |
+
"8": 23,
|
| 532 |
+
"9": 23,
|
| 533 |
+
"10": 21,
|
| 534 |
+
"J": 24,
|
| 535 |
+
"Q": 23,
|
| 536 |
+
"K": 22
|
| 537 |
+
},
|
| 538 |
+
"hi_lo_running_count": 0,
|
| 539 |
+
"true_count": 0.0
|
| 540 |
+
},
|
| 541 |
+
"dealer_up": "3♦",
|
| 542 |
+
"dealer_hole": "2♠",
|
| 543 |
+
"dealer_revealed": true,
|
| 544 |
+
"dealer": {
|
| 545 |
+
"cards": [
|
| 546 |
+
"3♦",
|
| 547 |
+
"2♠",
|
| 548 |
+
"2♠",
|
| 549 |
+
"3♦",
|
| 550 |
+
"A♣"
|
| 551 |
+
],
|
| 552 |
+
"total": 21,
|
| 553 |
+
"soft": true,
|
| 554 |
+
"blackjack": false,
|
| 555 |
+
"bust": false
|
| 556 |
+
},
|
| 557 |
+
"events": [
|
| 558 |
+
"莊家補牌 2♠ → 7 點",
|
| 559 |
+
"莊家補牌 3♦ → 10 點",
|
| 560 |
+
"莊家補牌 A♣ → 21 點",
|
| 561 |
+
"莊家最終 21 點"
|
| 562 |
+
],
|
| 563 |
+
"players": [
|
| 564 |
+
{
|
| 565 |
+
"cards": [
|
| 566 |
+
"10♠",
|
| 567 |
+
"2♦",
|
| 568 |
+
"8♣"
|
| 569 |
+
],
|
| 570 |
+
"bet": 10,
|
| 571 |
+
"doubled": false,
|
| 572 |
+
"total": 20,
|
| 573 |
+
"soft": false,
|
| 574 |
+
"bust": false,
|
| 575 |
+
"blackjack": false,
|
| 576 |
+
"steps": [
|
| 577 |
+
{
|
| 578 |
+
"type": "action",
|
| 579 |
+
"cards": [
|
| 580 |
+
"10♠",
|
| 581 |
+
"2♦"
|
| 582 |
+
],
|
| 583 |
+
"total": 12,
|
| 584 |
+
"soft": false,
|
| 585 |
+
"dealer_up": "3♦",
|
| 586 |
+
"allowed": [
|
| 587 |
+
"hit",
|
| 588 |
+
"stand"
|
| 589 |
+
],
|
| 590 |
+
"decision": {
|
| 591 |
+
"type": "choice",
|
| 592 |
+
"choice": "hit",
|
| 593 |
+
"probabilities": {
|
| 594 |
+
"hit": 0.564536710896258,
|
| 595 |
+
"stand": 0.43546328910374205
|
| 596 |
+
},
|
| 597 |
+
"confidence": 0.12907342179251602
|
| 598 |
+
},
|
| 599 |
+
"risk": {
|
| 600 |
+
"type": "score",
|
| 601 |
+
"score": 1.5462336728175854,
|
| 602 |
+
"legend": {
|
| 603 |
+
"0": "非常有利",
|
| 604 |
+
"1": "稍微有利",
|
| 605 |
+
"2": "有點不利",
|
| 606 |
+
"3": "非常不利"
|
| 607 |
+
},
|
| 608 |
+
"probabilities": {
|
| 609 |
+
"0": 0.11070758630941745,
|
| 610 |
+
"1": 0.3353281508308657,
|
| 611 |
+
"2": 0.45098726659243077,
|
| 612 |
+
"3": 0.10297699626728603
|
| 613 |
+
},
|
| 614 |
+
"confidence": 0.34027968028301336
|
| 615 |
+
},
|
| 616 |
+
"latency_ms": 2619,
|
| 617 |
+
"drawn": "8♣",
|
| 618 |
+
"total_after": 20
|
| 619 |
+
},
|
| 620 |
+
{
|
| 621 |
+
"type": "action",
|
| 622 |
+
"cards": [
|
| 623 |
+
"10♠",
|
| 624 |
+
"2♦",
|
| 625 |
+
"8♣"
|
| 626 |
+
],
|
| 627 |
+
"total": 20,
|
| 628 |
+
"soft": false,
|
| 629 |
+
"dealer_up": "3♦",
|
| 630 |
+
"allowed": [
|
| 631 |
+
"hit",
|
| 632 |
+
"stand"
|
| 633 |
+
],
|
| 634 |
+
"decision": {
|
| 635 |
+
"type": "choice",
|
| 636 |
+
"choice": "stand",
|
| 637 |
+
"probabilities": {
|
| 638 |
+
"hit": 0.23511610687855905,
|
| 639 |
+
"stand": 0.764883893121441
|
| 640 |
+
},
|
| 641 |
+
"confidence": 0.529767786242882
|
| 642 |
+
},
|
| 643 |
+
"risk": {
|
| 644 |
+
"type": "score",
|
| 645 |
+
"score": 1.1724233566485125,
|
| 646 |
+
"legend": {
|
| 647 |
+
"0": "非常有利",
|
| 648 |
+
"1": "稍微有利",
|
| 649 |
+
"2": "有點不利",
|
| 650 |
+
"3": "非常不利"
|
| 651 |
+
},
|
| 652 |
+
"probabilities": {
|
| 653 |
+
"0": 0.2259679727325013,
|
| 654 |
+
"1": 0.41366566628608514,
|
| 655 |
+
"2": 0.32234139258181355,
|
| 656 |
+
"3": 0.038024968399600075
|
| 657 |
+
},
|
| 658 |
+
"confidence": 0.375640697886485
|
| 659 |
+
},
|
| 660 |
+
"latency_ms": 3056,
|
| 661 |
+
"drawn": null,
|
| 662 |
+
"total_after": 20
|
| 663 |
+
}
|
| 664 |
+
],
|
| 665 |
+
"player_id": "a",
|
| 666 |
+
"result": "lose",
|
| 667 |
+
"net": -10,
|
| 668 |
+
"bet_decision": {
|
| 669 |
+
"decision": {
|
| 670 |
+
"type": "choice",
|
| 671 |
+
"choice": "min",
|
| 672 |
+
"probabilities": {
|
| 673 |
+
"min": 0.7433957153910495,
|
| 674 |
+
"standard": 0.16651591247876724,
|
| 675 |
+
"max": 0.09008837213018317
|
| 676 |
+
},
|
| 677 |
+
"confidence": 0.6150935730865743
|
| 678 |
+
},
|
| 679 |
+
"bet": 10,
|
| 680 |
+
"confidence": {
|
| 681 |
+
"type": "score",
|
| 682 |
+
"score": 1.2496433080778582,
|
| 683 |
+
"legend": {
|
| 684 |
+
"0": "沒信心",
|
| 685 |
+
"1": "普通",
|
| 686 |
+
"2": "有信心",
|
| 687 |
+
"3": "非常有信心"
|
| 688 |
+
},
|
| 689 |
+
"probabilities": {
|
| 690 |
+
"0": 0.17187017754479506,
|
| 691 |
+
"1": 0.48519722500579504,
|
| 692 |
+
"2": 0.26435170927616675,
|
| 693 |
+
"3": 0.07858088817324316
|
| 694 |
+
},
|
| 695 |
+
"confidence": 0.40661633683255194
|
| 696 |
+
},
|
| 697 |
+
"latency_ms": 2226
|
| 698 |
+
},
|
| 699 |
+
"bankroll_after": 970,
|
| 700 |
+
"id": "a",
|
| 701 |
+
"name": "阿保",
|
| 702 |
+
"style": "保守算牌派"
|
| 703 |
+
},
|
| 704 |
+
{
|
| 705 |
+
"cards": [
|
| 706 |
+
"6♦",
|
| 707 |
+
"10♠",
|
| 708 |
+
"4♠"
|
| 709 |
+
],
|
| 710 |
+
"bet": 50,
|
| 711 |
+
"doubled": false,
|
| 712 |
+
"total": 20,
|
| 713 |
+
"soft": false,
|
| 714 |
+
"bust": false,
|
| 715 |
+
"blackjack": false,
|
| 716 |
+
"steps": [
|
| 717 |
+
{
|
| 718 |
+
"type": "action",
|
| 719 |
+
"cards": [
|
| 720 |
+
"6♦",
|
| 721 |
+
"10♠"
|
| 722 |
+
],
|
| 723 |
+
"total": 16,
|
| 724 |
+
"soft": false,
|
| 725 |
+
"dealer_up": "3♦",
|
| 726 |
+
"allowed": [
|
| 727 |
+
"hit",
|
| 728 |
+
"stand"
|
| 729 |
+
],
|
| 730 |
+
"decision": {
|
| 731 |
+
"type": "choice",
|
| 732 |
+
"choice": "hit",
|
| 733 |
+
"probabilities": {
|
| 734 |
+
"hit": 0.6418322181399073,
|
| 735 |
+
"stand": 0.3581677818600927
|
| 736 |
+
},
|
| 737 |
+
"confidence": 0.28366443627981464
|
| 738 |
+
},
|
| 739 |
+
"risk": {
|
| 740 |
+
"type": "score",
|
| 741 |
+
"score": 1.4828880574132604,
|
| 742 |
+
"legend": {
|
| 743 |
+
"0": "非常有利",
|
| 744 |
+
"1": "稍微有利",
|
| 745 |
+
"2": "有點不利",
|
| 746 |
+
"3": "非常不利"
|
| 747 |
+
},
|
| 748 |
+
"probabilities": {
|
| 749 |
+
"0": 0.12154439867797127,
|
| 750 |
+
"1": 0.3522754018654166,
|
| 751 |
+
"2": 0.44792794282199283,
|
| 752 |
+
"3": 0.07825225663461932
|
| 753 |
+
},
|
| 754 |
+
"confidence": 0.3263835441440215
|
| 755 |
+
},
|
| 756 |
+
"latency_ms": 3581,
|
| 757 |
+
"drawn": "4♠",
|
| 758 |
+
"total_after": 20
|
| 759 |
+
},
|
| 760 |
+
{
|
| 761 |
+
"type": "action",
|
| 762 |
+
"cards": [
|
| 763 |
+
"6♦",
|
| 764 |
+
"10♠",
|
| 765 |
+
"4♠"
|
| 766 |
+
],
|
| 767 |
+
"total": 20,
|
| 768 |
+
"soft": false,
|
| 769 |
+
"dealer_up": "3♦",
|
| 770 |
+
"allowed": [
|
| 771 |
+
"hit",
|
| 772 |
+
"stand"
|
| 773 |
+
],
|
| 774 |
+
"decision": {
|
| 775 |
+
"type": "choice",
|
| 776 |
+
"choice": "stand",
|
| 777 |
+
"probabilities": {
|
| 778 |
+
"hit": 0.3440449520657691,
|
| 779 |
+
"stand": 0.6559550479342309
|
| 780 |
+
},
|
| 781 |
+
"confidence": 0.31191009586846175
|
| 782 |
+
},
|
| 783 |
+
"risk": {
|
| 784 |
+
"type": "score",
|
| 785 |
+
"score": 1.2733321008675031,
|
| 786 |
+
"legend": {
|
| 787 |
+
"0": "非常有利",
|
| 788 |
+
"1": "稍微有利",
|
| 789 |
+
"2": "有點不利",
|
| 790 |
+
"3": "非常不利"
|
| 791 |
+
},
|
| 792 |
+
"probabilities": {
|
| 793 |
+
"0": 0.19239615322374934,
|
| 794 |
+
"1": 0.3916707506879337,
|
| 795 |
+
"2": 0.36613793808538125,
|
| 796 |
+
"3": 0.049795158002935644
|
| 797 |
+
},
|
| 798 |
+
"confidence": 0.3418755926849981
|
| 799 |
+
},
|
| 800 |
+
"latency_ms": 3581,
|
| 801 |
+
"drawn": null,
|
| 802 |
+
"total_after": 20
|
| 803 |
+
}
|
| 804 |
+
],
|
| 805 |
+
"player_id": "b",
|
| 806 |
+
"result": "lose",
|
| 807 |
+
"net": -50,
|
| 808 |
+
"bet_decision": {
|
| 809 |
+
"decision": {
|
| 810 |
+
"type": "choice",
|
| 811 |
+
"choice": "max",
|
| 812 |
+
"probabilities": {
|
| 813 |
+
"min": 0.17446344549537493,
|
| 814 |
+
"standard": 0.3824838673364471,
|
| 815 |
+
"max": 0.44305268716817786
|
| 816 |
+
},
|
| 817 |
+
"confidence": 0.1645790307522668
|
| 818 |
+
},
|
| 819 |
+
"bet": 50,
|
| 820 |
+
"confidence": {
|
| 821 |
+
"type": "score",
|
| 822 |
+
"score": 1.6022477730280071,
|
| 823 |
+
"legend": {
|
| 824 |
+
"0": "沒信心",
|
| 825 |
+
"1": "普通",
|
| 826 |
+
"2": "有信心",
|
| 827 |
+
"3": "非常有信心"
|
| 828 |
+
},
|
| 829 |
+
"probabilities": {
|
| 830 |
+
"0": 0.12490168872094283,
|
| 831 |
+
"1": 0.32284211542202057,
|
| 832 |
+
"2": 0.3773629299651229,
|
| 833 |
+
"3": 0.17489326589191365
|
| 834 |
+
},
|
| 835 |
+
"confidence": 0.2524612412441801
|
| 836 |
+
},
|
| 837 |
+
"latency_ms": 2818
|
| 838 |
+
},
|
| 839 |
+
"bankroll_after": 950,
|
| 840 |
+
"id": "b",
|
| 841 |
+
"name": "阿衝",
|
| 842 |
+
"style": "激進壓迫派"
|
| 843 |
+
}
|
| 844 |
+
],
|
| 845 |
+
"shoe_after": {
|
| 846 |
+
"decks": 6,
|
| 847 |
+
"remaining_cards": 288,
|
| 848 |
+
"penetration_pct": 8,
|
| 849 |
+
"cards_left_by_rank": {
|
| 850 |
+
"A": 21,
|
| 851 |
+
"2": 21,
|
| 852 |
+
"3": 20,
|
| 853 |
+
"4": 23,
|
| 854 |
+
"5": 23,
|
| 855 |
+
"6": 22,
|
| 856 |
+
"7": 23,
|
| 857 |
+
"8": 22,
|
| 858 |
+
"9": 23,
|
| 859 |
+
"10": 21,
|
| 860 |
+
"J": 24,
|
| 861 |
+
"Q": 23,
|
| 862 |
+
"K": 22
|
| 863 |
+
},
|
| 864 |
+
"hi_lo_running_count": 2,
|
| 865 |
+
"true_count": 0.36
|
| 866 |
+
}
|
| 867 |
+
},
|
| 868 |
+
{
|
| 869 |
+
"n": 4,
|
| 870 |
+
"shoe_before": {
|
| 871 |
+
"decks": 6,
|
| 872 |
+
"remaining_cards": 282,
|
| 873 |
+
"penetration_pct": 10,
|
| 874 |
+
"cards_left_by_rank": {
|
| 875 |
+
"A": 20,
|
| 876 |
+
"2": 21,
|
| 877 |
+
"3": 19,
|
| 878 |
+
"4": 23,
|
| 879 |
+
"5": 23,
|
| 880 |
+
"6": 22,
|
| 881 |
+
"7": 22,
|
| 882 |
+
"8": 21,
|
| 883 |
+
"9": 22,
|
| 884 |
+
"10": 21,
|
| 885 |
+
"J": 24,
|
| 886 |
+
"Q": 23,
|
| 887 |
+
"K": 21
|
| 888 |
+
},
|
| 889 |
+
"hi_lo_running_count": 1,
|
| 890 |
+
"true_count": 0.18
|
| 891 |
+
},
|
| 892 |
+
"dealer_up": "7♠",
|
| 893 |
+
"dealer_hole": "K♥",
|
| 894 |
+
"dealer_revealed": true,
|
| 895 |
+
"dealer": {
|
| 896 |
+
"cards": [
|
| 897 |
+
"7♠",
|
| 898 |
+
"K♥"
|
| 899 |
+
],
|
| 900 |
+
"total": 17,
|
| 901 |
+
"soft": false,
|
| 902 |
+
"blackjack": false,
|
| 903 |
+
"bust": false
|
| 904 |
+
},
|
| 905 |
+
"events": [
|
| 906 |
+
"莊家最終 17 點"
|
| 907 |
+
],
|
| 908 |
+
"players": [
|
| 909 |
+
{
|
| 910 |
+
"cards": [
|
| 911 |
+
"8♥",
|
| 912 |
+
"A♦"
|
| 913 |
+
],
|
| 914 |
+
"bet": 10,
|
| 915 |
+
"doubled": false,
|
| 916 |
+
"total": 19,
|
| 917 |
+
"soft": true,
|
| 918 |
+
"bust": false,
|
| 919 |
+
"blackjack": false,
|
| 920 |
+
"steps": [
|
| 921 |
+
{
|
| 922 |
+
"type": "action",
|
| 923 |
+
"cards": [
|
| 924 |
+
"8♥",
|
| 925 |
+
"A♦"
|
| 926 |
+
],
|
| 927 |
+
"total": 19,
|
| 928 |
+
"soft": true,
|
| 929 |
+
"dealer_up": "7♠",
|
| 930 |
+
"allowed": [
|
| 931 |
+
"hit",
|
| 932 |
+
"stand"
|
| 933 |
+
],
|
| 934 |
+
"decision": {
|
| 935 |
+
"type": "choice",
|
| 936 |
+
"choice": "stand",
|
| 937 |
+
"probabilities": {
|
| 938 |
+
"hit": 0.2797133858793715,
|
| 939 |
+
"stand": 0.7202866141206284
|
| 940 |
+
},
|
| 941 |
+
"confidence": 0.4405732282412569
|
| 942 |
+
},
|
| 943 |
+
"risk": {
|
| 944 |
+
"type": "score",
|
| 945 |
+
"score": 1.0755577590046503,
|
| 946 |
+
"legend": {
|
| 947 |
+
"0": "非常有利",
|
| 948 |
+
"1": "稍微有利",
|
| 949 |
+
"2": "有點不利",
|
| 950 |
+
"3": "非常不利"
|
| 951 |
+
},
|
| 952 |
+
"probabilities": {
|
| 953 |
+
"0": 0.2653823049012448,
|
| 954 |
+
"1": 0.42404250943999217,
|
| 955 |
+
"2": 0.2802103074116306,
|
| 956 |
+
"3": 0.030364878247132344
|
| 957 |
+
},
|
| 958 |
+
"confidence": 0.3936776311928599
|
| 959 |
+
},
|
| 960 |
+
"latency_ms": 3126,
|
| 961 |
+
"drawn": null,
|
| 962 |
+
"total_after": 19
|
| 963 |
+
}
|
| 964 |
+
],
|
| 965 |
+
"player_id": "a",
|
| 966 |
+
"result": "win",
|
| 967 |
+
"net": 10,
|
| 968 |
+
"bet_decision": {
|
| 969 |
+
"decision": {
|
| 970 |
+
"type": "choice",
|
| 971 |
+
"choice": "min",
|
| 972 |
+
"probabilities": {
|
| 973 |
+
"min": 0.7249067710505043,
|
| 974 |
+
"standard": 0.17984278558883143,
|
| 975 |
+
"max": 0.09525044336066422
|
| 976 |
+
},
|
| 977 |
+
"confidence": 0.5873601565757564
|
| 978 |
+
},
|
| 979 |
+
"bet": 10,
|
| 980 |
+
"confidence": {
|
| 981 |
+
"type": "score",
|
| 982 |
+
"score": 1.1590048973716087,
|
| 983 |
+
"legend": {
|
| 984 |
+
"0": "沒信心",
|
| 985 |
+
"1": "普通",
|
| 986 |
+
"2": "有信心",
|
| 987 |
+
"3": "非常有信心"
|
| 988 |
+
},
|
| 989 |
+
"probabilities": {
|
| 990 |
+
"0": 0.21064627872594902,
|
| 991 |
+
"1": 0.4865225308156261,
|
| 992 |
+
"2": 0.23601120481929241,
|
| 993 |
+
"3": 0.06681998563913258
|
| 994 |
+
},
|
| 995 |
+
"confidence": 0.4197025451764934
|
| 996 |
+
},
|
| 997 |
+
"latency_ms": 2265
|
| 998 |
+
},
|
| 999 |
+
"bankroll_after": 980,
|
| 1000 |
+
"id": "a",
|
| 1001 |
+
"name": "阿保",
|
| 1002 |
+
"style": "保守算牌派"
|
| 1003 |
+
},
|
| 1004 |
+
{
|
| 1005 |
+
"cards": [
|
| 1006 |
+
"9♠",
|
| 1007 |
+
"3♦",
|
| 1008 |
+
"K♥"
|
| 1009 |
+
],
|
| 1010 |
+
"bet": 50,
|
| 1011 |
+
"doubled": false,
|
| 1012 |
+
"total": 22,
|
| 1013 |
+
"soft": false,
|
| 1014 |
+
"bust": true,
|
| 1015 |
+
"blackjack": false,
|
| 1016 |
+
"steps": [
|
| 1017 |
+
{
|
| 1018 |
+
"type": "action",
|
| 1019 |
+
"cards": [
|
| 1020 |
+
"9♠",
|
| 1021 |
+
"3♦"
|
| 1022 |
+
],
|
| 1023 |
+
"total": 12,
|
| 1024 |
+
"soft": false,
|
| 1025 |
+
"dealer_up": "7♠",
|
| 1026 |
+
"allowed": [
|
| 1027 |
+
"hit",
|
| 1028 |
+
"stand"
|
| 1029 |
+
],
|
| 1030 |
+
"decision": {
|
| 1031 |
+
"type": "choice",
|
| 1032 |
+
"choice": "hit",
|
| 1033 |
+
"probabilities": {
|
| 1034 |
+
"hit": 0.7049549850357781,
|
| 1035 |
+
"stand": 0.295045014964222
|
| 1036 |
+
},
|
| 1037 |
+
"confidence": 0.4099099700715563
|
| 1038 |
+
},
|
| 1039 |
+
"risk": {
|
| 1040 |
+
"type": "score",
|
| 1041 |
+
"score": 1.5146466061989001,
|
| 1042 |
+
"legend": {
|
| 1043 |
+
"0": "非常有利",
|
| 1044 |
+
"1": "稍微有利",
|
| 1045 |
+
"2": "有點不利",
|
| 1046 |
+
"3": "非常不利"
|
| 1047 |
+
},
|
| 1048 |
+
"probabilities": {
|
| 1049 |
+
"0": 0.12572631444400376,
|
| 1050 |
+
"1": 0.33533827834479635,
|
| 1051 |
+
"2": 0.43749789377949644,
|
| 1052 |
+
"3": 0.10143751343170365
|
| 1053 |
+
},
|
| 1054 |
+
"confidence": 0.3117715793354925
|
| 1055 |
+
},
|
| 1056 |
+
"latency_ms": 3834,
|
| 1057 |
+
"drawn": "K♥",
|
| 1058 |
+
"total_after": 22
|
| 1059 |
+
}
|
| 1060 |
+
],
|
| 1061 |
+
"player_id": "b",
|
| 1062 |
+
"result": "lose",
|
| 1063 |
+
"net": -50,
|
| 1064 |
+
"bet_decision": {
|
| 1065 |
+
"decision": {
|
| 1066 |
+
"type": "choice",
|
| 1067 |
+
"choice": "max",
|
| 1068 |
+
"probabilities": {
|
| 1069 |
+
"min": 0.19672050531851887,
|
| 1070 |
+
"standard": 0.3845980993465475,
|
| 1071 |
+
"max": 0.41868139533493354
|
| 1072 |
+
},
|
| 1073 |
+
"confidence": 0.1280220930024003
|
| 1074 |
+
},
|
| 1075 |
+
"bet": 50,
|
| 1076 |
+
"confidence": {
|
| 1077 |
+
"type": "score",
|
| 1078 |
+
"score": 1.5623486708262693,
|
| 1079 |
+
"legend": {
|
| 1080 |
+
"0": "沒信心",
|
| 1081 |
+
"1": "普通",
|
| 1082 |
+
"2": "有信心",
|
| 1083 |
+
"3": "非常有信心"
|
| 1084 |
+
},
|
| 1085 |
+
"probabilities": {
|
| 1086 |
+
"0": 0.13876933530621044,
|
| 1087 |
+
"1": 0.3289561938301259,
|
| 1088 |
+
"2": 0.36343093559484774,
|
| 1089 |
+
"3": 0.1688435352688159
|
| 1090 |
+
},
|
| 1091 |
+
"confidence": 0.22466160028863735
|
| 1092 |
+
},
|
| 1093 |
+
"latency_ms": 3123
|
| 1094 |
+
},
|
| 1095 |
+
"bankroll_after": 900,
|
| 1096 |
+
"id": "b",
|
| 1097 |
+
"name": "阿衝",
|
| 1098 |
+
"style": "激進壓迫派"
|
| 1099 |
+
}
|
| 1100 |
+
],
|
| 1101 |
+
"shoe_after": {
|
| 1102 |
+
"decks": 6,
|
| 1103 |
+
"remaining_cards": 281,
|
| 1104 |
+
"penetration_pct": 10,
|
| 1105 |
+
"cards_left_by_rank": {
|
| 1106 |
+
"A": 20,
|
| 1107 |
+
"2": 21,
|
| 1108 |
+
"3": 19,
|
| 1109 |
+
"4": 23,
|
| 1110 |
+
"5": 23,
|
| 1111 |
+
"6": 22,
|
| 1112 |
+
"7": 22,
|
| 1113 |
+
"8": 21,
|
| 1114 |
+
"9": 22,
|
| 1115 |
+
"10": 21,
|
| 1116 |
+
"J": 24,
|
| 1117 |
+
"Q": 23,
|
| 1118 |
+
"K": 20
|
| 1119 |
+
},
|
| 1120 |
+
"hi_lo_running_count": 0,
|
| 1121 |
+
"true_count": 0.0
|
| 1122 |
+
}
|
| 1123 |
+
},
|
| 1124 |
+
{
|
| 1125 |
+
"n": 5,
|
| 1126 |
+
"shoe_before": {
|
| 1127 |
+
"decks": 6,
|
| 1128 |
+
"remaining_cards": 275,
|
| 1129 |
+
"penetration_pct": 12,
|
| 1130 |
+
"cards_left_by_rank": {
|
| 1131 |
+
"A": 19,
|
| 1132 |
+
"2": 20,
|
| 1133 |
+
"3": 19,
|
| 1134 |
+
"4": 23,
|
| 1135 |
+
"5": 23,
|
| 1136 |
+
"6": 21,
|
| 1137 |
+
"7": 21,
|
| 1138 |
+
"8": 21,
|
| 1139 |
+
"9": 22,
|
| 1140 |
+
"10": 20,
|
| 1141 |
+
"J": 23,
|
| 1142 |
+
"Q": 23,
|
| 1143 |
+
"K": 20
|
| 1144 |
+
},
|
| 1145 |
+
"hi_lo_running_count": -1,
|
| 1146 |
+
"true_count": -0.19
|
| 1147 |
+
},
|
| 1148 |
+
"dealer_up": "A♣",
|
| 1149 |
+
"dealer_hole": "J♠",
|
| 1150 |
+
"dealer_revealed": true,
|
| 1151 |
+
"dealer": {
|
| 1152 |
+
"cards": [
|
| 1153 |
+
"A♣",
|
| 1154 |
+
"J♠"
|
| 1155 |
+
],
|
| 1156 |
+
"total": 21,
|
| 1157 |
+
"soft": true,
|
| 1158 |
+
"blackjack": true,
|
| 1159 |
+
"bust": false
|
| 1160 |
+
},
|
| 1161 |
+
"events": [
|
| 1162 |
+
"莊家明牌為 A 或 10 點,進行暗牌確認",
|
| 1163 |
+
"莊家直接拿到 Blackjack,本局立即結束"
|
| 1164 |
+
],
|
| 1165 |
+
"players": [
|
| 1166 |
+
{
|
| 1167 |
+
"cards": [
|
| 1168 |
+
"10♦",
|
| 1169 |
+
"2♦"
|
| 1170 |
+
],
|
| 1171 |
+
"bet": 10,
|
| 1172 |
+
"doubled": false,
|
| 1173 |
+
"blackjack": false,
|
| 1174 |
+
"bust": false,
|
| 1175 |
+
"total": 12,
|
| 1176 |
+
"soft": false,
|
| 1177 |
+
"result": "lose",
|
| 1178 |
+
"net": -10,
|
| 1179 |
+
"bet_decision": {
|
| 1180 |
+
"decision": {
|
| 1181 |
+
"type": "choice",
|
| 1182 |
+
"choice": "min",
|
| 1183 |
+
"probabilities": {
|
| 1184 |
+
"min": 0.7562291100284627,
|
| 1185 |
+
"standard": 0.16685391773152444,
|
| 1186 |
+
"max": 0.07691697224001287
|
| 1187 |
+
},
|
| 1188 |
+
"confidence": 0.634343665042694
|
| 1189 |
+
},
|
| 1190 |
+
"bet": 10,
|
| 1191 |
+
"confidence": {
|
| 1192 |
+
"type": "score",
|
| 1193 |
+
"score": 1.2320991965875188,
|
| 1194 |
+
"legend": {
|
| 1195 |
+
"0": "沒信心",
|
| 1196 |
+
"1": "普通",
|
| 1197 |
+
"2": "有信心",
|
| 1198 |
+
"3": "非常有信心"
|
| 1199 |
+
},
|
| 1200 |
+
"probabilities": {
|
| 1201 |
+
"0": 0.19676299225478636,
|
| 1202 |
+
"1": 0.46805537175271755,
|
| 1203 |
+
"2": 0.24150108314268687,
|
| 1204 |
+
"3": 0.09368055284980918
|
| 1205 |
+
},
|
| 1206 |
+
"confidence": 0.37437481890290836
|
| 1207 |
+
},
|
| 1208 |
+
"latency_ms": 2277
|
| 1209 |
+
},
|
| 1210 |
+
"bankroll_after": 970,
|
| 1211 |
+
"id": "a",
|
| 1212 |
+
"name": "阿保",
|
| 1213 |
+
"style": "保守算牌派"
|
| 1214 |
+
},
|
| 1215 |
+
{
|
| 1216 |
+
"cards": [
|
| 1217 |
+
"7♠",
|
| 1218 |
+
"6♦"
|
| 1219 |
+
],
|
| 1220 |
+
"bet": 50,
|
| 1221 |
+
"doubled": false,
|
| 1222 |
+
"blackjack": false,
|
| 1223 |
+
"bust": false,
|
| 1224 |
+
"total": 13,
|
| 1225 |
+
"soft": false,
|
| 1226 |
+
"result": "lose",
|
| 1227 |
+
"net": -50,
|
| 1228 |
+
"bet_decision": {
|
| 1229 |
+
"decision": {
|
| 1230 |
+
"type": "choice",
|
| 1231 |
+
"choice": "max",
|
| 1232 |
+
"probabilities": {
|
| 1233 |
+
"min": 0.20601333171277045,
|
| 1234 |
+
"standard": 0.34842502176841295,
|
| 1235 |
+
"max": 0.4455616465188165
|
| 1236 |
+
},
|
| 1237 |
+
"confidence": 0.16834246977822478
|
| 1238 |
+
},
|
| 1239 |
+
"bet": 50,
|
| 1240 |
+
"confidence": {
|
| 1241 |
+
"type": "score",
|
| 1242 |
+
"score": 1.564656149705211,
|
| 1243 |
+
"legend": {
|
| 1244 |
+
"0": "沒信心",
|
| 1245 |
+
"1": "普通",
|
| 1246 |
+
"2": "有信心",
|
| 1247 |
+
"3": "非常有信心"
|
| 1248 |
+
},
|
| 1249 |
+
"probabilities": {
|
| 1250 |
+
"0": 0.14718873328060164,
|
| 1251 |
+
"1": 0.32229188630260436,
|
| 1252 |
+
"2": 0.34919387784777556,
|
| 1253 |
+
"3": 0.1813255025690185
|
| 1254 |
+
},
|
| 1255 |
+
"confidence": 0.2020051445671739
|
| 1256 |
+
},
|
| 1257 |
+
"latency_ms": 3202
|
| 1258 |
+
},
|
| 1259 |
+
"bankroll_after": 850,
|
| 1260 |
+
"id": "b",
|
| 1261 |
+
"name": "阿衝",
|
| 1262 |
+
"style": "激進壓迫派"
|
| 1263 |
+
}
|
| 1264 |
+
],
|
| 1265 |
+
"shoe_after": {
|
| 1266 |
+
"decks": 6,
|
| 1267 |
+
"remaining_cards": 275,
|
| 1268 |
+
"penetration_pct": 12,
|
| 1269 |
+
"cards_left_by_rank": {
|
| 1270 |
+
"A": 19,
|
| 1271 |
+
"2": 20,
|
| 1272 |
+
"3": 19,
|
| 1273 |
+
"4": 23,
|
| 1274 |
+
"5": 23,
|
| 1275 |
+
"6": 21,
|
| 1276 |
+
"7": 21,
|
| 1277 |
+
"8": 21,
|
| 1278 |
+
"9": 22,
|
| 1279 |
+
"10": 20,
|
| 1280 |
+
"J": 23,
|
| 1281 |
+
"Q": 23,
|
| 1282 |
+
"K": 20
|
| 1283 |
+
},
|
| 1284 |
+
"hi_lo_running_count": -1,
|
| 1285 |
+
"true_count": -0.19
|
| 1286 |
+
}
|
| 1287 |
+
},
|
| 1288 |
+
{
|
| 1289 |
+
"n": 6,
|
| 1290 |
+
"shoe_before": {
|
| 1291 |
+
"decks": 6,
|
| 1292 |
+
"remaining_cards": 269,
|
| 1293 |
+
"penetration_pct": 14,
|
| 1294 |
+
"cards_left_by_rank": {
|
| 1295 |
+
"A": 17,
|
| 1296 |
+
"2": 20,
|
| 1297 |
+
"3": 18,
|
| 1298 |
+
"4": 23,
|
| 1299 |
+
"5": 22,
|
| 1300 |
+
"6": 21,
|
| 1301 |
+
"7": 21,
|
| 1302 |
+
"8": 21,
|
| 1303 |
+
"9": 21,
|
| 1304 |
+
"10": 20,
|
| 1305 |
+
"J": 23,
|
| 1306 |
+
"Q": 23,
|
| 1307 |
+
"K": 19
|
| 1308 |
+
},
|
| 1309 |
+
"hi_lo_running_count": -2,
|
| 1310 |
+
"true_count": -0.39
|
| 1311 |
+
},
|
| 1312 |
+
"dealer_up": "9♦",
|
| 1313 |
+
"dealer_hole": "5♣",
|
| 1314 |
+
"dealer_revealed": true,
|
| 1315 |
+
"dealer": {
|
| 1316 |
+
"cards": [
|
| 1317 |
+
"9♦",
|
| 1318 |
+
"5♣",
|
| 1319 |
+
"8♣"
|
| 1320 |
+
],
|
| 1321 |
+
"total": 22,
|
| 1322 |
+
"soft": false,
|
| 1323 |
+
"blackjack": false,
|
| 1324 |
+
"bust": true
|
| 1325 |
+
},
|
| 1326 |
+
"events": [
|
| 1327 |
+
"莊家補牌 8♣ → 22 點",
|
| 1328 |
+
"莊家最終 22 點,爆牌"
|
| 1329 |
+
],
|
| 1330 |
+
"players": [
|
| 1331 |
+
{
|
| 1332 |
+
"cards": [
|
| 1333 |
+
"3♠",
|
| 1334 |
+
"A♦",
|
| 1335 |
+
"K♣",
|
| 1336 |
+
"7♠"
|
| 1337 |
+
],
|
| 1338 |
+
"bet": 10,
|
| 1339 |
+
"doubled": false,
|
| 1340 |
+
"total": 21,
|
| 1341 |
+
"soft": false,
|
| 1342 |
+
"bust": false,
|
| 1343 |
+
"blackjack": false,
|
| 1344 |
+
"steps": [
|
| 1345 |
+
{
|
| 1346 |
+
"type": "action",
|
| 1347 |
+
"cards": [
|
| 1348 |
+
"3♠",
|
| 1349 |
+
"A♦"
|
| 1350 |
+
],
|
| 1351 |
+
"total": 14,
|
| 1352 |
+
"soft": true,
|
| 1353 |
+
"dealer_up": "9♦",
|
| 1354 |
+
"allowed": [
|
| 1355 |
+
"hit",
|
| 1356 |
+
"stand",
|
| 1357 |
+
"double"
|
| 1358 |
+
],
|
| 1359 |
+
"decision": {
|
| 1360 |
+
"type": "choice",
|
| 1361 |
+
"choice": "hit",
|
| 1362 |
+
"probabilities": {
|
| 1363 |
+
"hit": 0.408721663227952,
|
| 1364 |
+
"stand": 0.29303182266998506,
|
| 1365 |
+
"double": 0.29824651410206277
|
| 1366 |
+
},
|
| 1367 |
+
"confidence": 0.11308249484192802
|
| 1368 |
+
},
|
| 1369 |
+
"risk": {
|
| 1370 |
+
"type": "score",
|
| 1371 |
+
"score": 1.5610409422586708,
|
| 1372 |
+
"legend": {
|
| 1373 |
+
"0": "非常有利",
|
| 1374 |
+
"1": "稍微有利",
|
| 1375 |
+
"2": "有點不利",
|
| 1376 |
+
"3": "非常不利"
|
| 1377 |
+
},
|
| 1378 |
+
"probabilities": {
|
| 1379 |
+
"0": 0.10553867166975685,
|
| 1380 |
+
"1": 0.32969436958342446,
|
| 1381 |
+
"2": 0.46295430356520967,
|
| 1382 |
+
"3": 0.10181265518160906
|
| 1383 |
+
},
|
| 1384 |
+
"confidence": 0.3574156318954528
|
| 1385 |
+
},
|
| 1386 |
+
"latency_ms": 3156,
|
| 1387 |
+
"drawn": "K♣",
|
| 1388 |
+
"total_after": 14
|
| 1389 |
+
},
|
| 1390 |
+
{
|
| 1391 |
+
"type": "action",
|
| 1392 |
+
"cards": [
|
| 1393 |
+
"3♠",
|
| 1394 |
+
"A♦",
|
| 1395 |
+
"K♣"
|
| 1396 |
+
],
|
| 1397 |
+
"total": 14,
|
| 1398 |
+
"soft": false,
|
| 1399 |
+
"dealer_up": "9♦",
|
| 1400 |
+
"allowed": [
|
| 1401 |
+
"hit",
|
| 1402 |
+
"stand"
|
| 1403 |
+
],
|
| 1404 |
+
"decision": {
|
| 1405 |
+
"type": "choice",
|
| 1406 |
+
"choice": "hit",
|
| 1407 |
+
"probabilities": {
|
| 1408 |
+
"hit": 0.5122837140095126,
|
| 1409 |
+
"stand": 0.48771628599048733
|
| 1410 |
+
},
|
| 1411 |
+
"confidence": 0.024567428019025117
|
| 1412 |
+
},
|
| 1413 |
+
"risk": {
|
| 1414 |
+
"type": "score",
|
| 1415 |
+
"score": 1.5881935559142273,
|
| 1416 |
+
"legend": {
|
| 1417 |
+
"0": "非常有利",
|
| 1418 |
+
"1": "稍微有利",
|
| 1419 |
+
"2": "有點不利",
|
| 1420 |
+
"3": "非常不利"
|
| 1421 |
+
},
|
| 1422 |
+
"probabilities": {
|
| 1423 |
+
"0": 0.1232644149177596,
|
| 1424 |
+
"1": 0.3080900808526087,
|
| 1425 |
+
"2": 0.4258330376272768,
|
| 1426 |
+
"3": 0.14281246660235497
|
| 1427 |
+
},
|
| 1428 |
+
"confidence": 0.30256862270951723
|
| 1429 |
+
},
|
| 1430 |
+
"latency_ms": 2963,
|
| 1431 |
+
"drawn": "7♠",
|
| 1432 |
+
"total_after": 21
|
| 1433 |
+
}
|
| 1434 |
+
],
|
| 1435 |
+
"player_id": "a",
|
| 1436 |
+
"result": "win",
|
| 1437 |
+
"net": 10,
|
| 1438 |
+
"bet_decision": {
|
| 1439 |
+
"decision": {
|
| 1440 |
+
"type": "choice",
|
| 1441 |
+
"choice": "min",
|
| 1442 |
+
"probabilities": {
|
| 1443 |
+
"min": 0.7443943535681774,
|
| 1444 |
+
"standard": 0.1724434598311167,
|
| 1445 |
+
"max": 0.08316218660070591
|
| 1446 |
+
},
|
| 1447 |
+
"confidence": 0.6165915303522661
|
| 1448 |
+
},
|
| 1449 |
+
"bet": 10,
|
| 1450 |
+
"confidence": {
|
| 1451 |
+
"type": "score",
|
| 1452 |
+
"score": 1.2134591868984879,
|
| 1453 |
+
"legend": {
|
| 1454 |
+
"0": "沒信心",
|
| 1455 |
+
"1": "普通",
|
| 1456 |
+
"2": "有信心",
|
| 1457 |
+
"3": "非常有信心"
|
| 1458 |
+
},
|
| 1459 |
+
"probabilities": {
|
| 1460 |
+
"0": 0.1889940408450792,
|
| 1461 |
+
"1": 0.48665170909181565,
|
| 1462 |
+
"2": 0.24625527238264336,
|
| 1463 |
+
"3": 0.07809897768046187
|
| 1464 |
+
},
|
| 1465 |
+
"confidence": 0.40855273141135373
|
| 1466 |
+
},
|
| 1467 |
+
"latency_ms": 2442
|
| 1468 |
+
},
|
| 1469 |
+
"bankroll_after": 980,
|
| 1470 |
+
"id": "a",
|
| 1471 |
+
"name": "阿保",
|
| 1472 |
+
"style": "保守算牌派"
|
| 1473 |
+
},
|
| 1474 |
+
{
|
| 1475 |
+
"cards": [
|
| 1476 |
+
"A♠",
|
| 1477 |
+
"K♣"
|
| 1478 |
+
],
|
| 1479 |
+
"bet": 50,
|
| 1480 |
+
"doubled": false,
|
| 1481 |
+
"total": 21,
|
| 1482 |
+
"soft": true,
|
| 1483 |
+
"bust": false,
|
| 1484 |
+
"blackjack": true,
|
| 1485 |
+
"steps": [],
|
| 1486 |
+
"player_id": "b",
|
| 1487 |
+
"result": "blackjack",
|
| 1488 |
+
"net": 75,
|
| 1489 |
+
"bet_decision": {
|
| 1490 |
+
"decision": {
|
| 1491 |
+
"type": "choice",
|
| 1492 |
+
"choice": "max",
|
| 1493 |
+
"probabilities": {
|
| 1494 |
+
"min": 0.1771453995341398,
|
| 1495 |
+
"standard": 0.3923319399455236,
|
| 1496 |
+
"max": 0.43052266052033666
|
| 1497 |
+
},
|
| 1498 |
+
"confidence": 0.145783990780505
|
| 1499 |
+
},
|
| 1500 |
+
"bet": 50,
|
| 1501 |
+
"confidence": {
|
| 1502 |
+
"type": "score",
|
| 1503 |
+
"score": 1.5896896540311678,
|
| 1504 |
+
"legend": {
|
| 1505 |
+
"0": "沒信心",
|
| 1506 |
+
"1": "普通",
|
| 1507 |
+
"2": "有信心",
|
| 1508 |
+
"3": "非常有信心"
|
| 1509 |
+
},
|
| 1510 |
+
"probabilities": {
|
| 1511 |
+
"0": 0.13640040426554614,
|
| 1512 |
+
"1": 0.3189928849419576,
|
| 1513 |
+
"2": 0.36312336328827843,
|
| 1514 |
+
"3": 0.18148334750421774
|
| 1515 |
+
},
|
| 1516 |
+
"confidence": 0.22672295902273243
|
| 1517 |
+
},
|
| 1518 |
+
"latency_ms": 3056
|
| 1519 |
+
},
|
| 1520 |
+
"bankroll_after": 925,
|
| 1521 |
+
"id": "b",
|
| 1522 |
+
"name": "阿衝",
|
| 1523 |
+
"style": "激進壓迫派"
|
| 1524 |
+
}
|
| 1525 |
+
],
|
| 1526 |
+
"shoe_after": {
|
| 1527 |
+
"decks": 6,
|
| 1528 |
+
"remaining_cards": 266,
|
| 1529 |
+
"penetration_pct": 15,
|
| 1530 |
+
"cards_left_by_rank": {
|
| 1531 |
+
"A": 17,
|
| 1532 |
+
"2": 20,
|
| 1533 |
+
"3": 18,
|
| 1534 |
+
"4": 23,
|
| 1535 |
+
"5": 22,
|
| 1536 |
+
"6": 21,
|
| 1537 |
+
"7": 20,
|
| 1538 |
+
"8": 20,
|
| 1539 |
+
"9": 21,
|
| 1540 |
+
"10": 20,
|
| 1541 |
+
"J": 23,
|
| 1542 |
+
"Q": 23,
|
| 1543 |
+
"K": 18
|
| 1544 |
+
},
|
| 1545 |
+
"hi_lo_running_count": -3,
|
| 1546 |
+
"true_count": -0.59
|
| 1547 |
+
}
|
| 1548 |
+
},
|
| 1549 |
+
{
|
| 1550 |
+
"n": 7,
|
| 1551 |
+
"shoe_before": {
|
| 1552 |
+
"decks": 6,
|
| 1553 |
+
"remaining_cards": 260,
|
| 1554 |
+
"penetration_pct": 17,
|
| 1555 |
+
"cards_left_by_rank": {
|
| 1556 |
+
"A": 16,
|
| 1557 |
+
"2": 19,
|
| 1558 |
+
"3": 17,
|
| 1559 |
+
"4": 23,
|
| 1560 |
+
"5": 22,
|
| 1561 |
+
"6": 21,
|
| 1562 |
+
"7": 20,
|
| 1563 |
+
"8": 19,
|
| 1564 |
+
"9": 21,
|
| 1565 |
+
"10": 18,
|
| 1566 |
+
"J": 23,
|
| 1567 |
+
"Q": 23,
|
| 1568 |
+
"K": 18
|
| 1569 |
+
},
|
| 1570 |
+
"hi_lo_running_count": -4,
|
| 1571 |
+
"true_count": -0.8
|
| 1572 |
+
},
|
| 1573 |
+
"dealer_up": "8♠",
|
| 1574 |
+
"dealer_hole": "3♦",
|
| 1575 |
+
"dealer_revealed": true,
|
| 1576 |
+
"dealer": {
|
| 1577 |
+
"cards": [
|
| 1578 |
+
"8♠",
|
| 1579 |
+
"3♦",
|
| 1580 |
+
"7♦"
|
| 1581 |
+
],
|
| 1582 |
+
"total": 18,
|
| 1583 |
+
"soft": false,
|
| 1584 |
+
"blackjack": false,
|
| 1585 |
+
"bust": false
|
| 1586 |
+
},
|
| 1587 |
+
"events": [
|
| 1588 |
+
"莊家補牌 7♦ → 18 點",
|
| 1589 |
+
"莊家最終 18 點"
|
| 1590 |
+
],
|
| 1591 |
+
"players": [
|
| 1592 |
+
{
|
| 1593 |
+
"cards": [
|
| 1594 |
+
"10♣",
|
| 1595 |
+
"2♣",
|
| 1596 |
+
"K♠"
|
| 1597 |
+
],
|
| 1598 |
+
"bet": 10,
|
| 1599 |
+
"doubled": false,
|
| 1600 |
+
"total": 22,
|
| 1601 |
+
"soft": false,
|
| 1602 |
+
"bust": true,
|
| 1603 |
+
"blackjack": false,
|
| 1604 |
+
"steps": [
|
| 1605 |
+
{
|
| 1606 |
+
"type": "action",
|
| 1607 |
+
"cards": [
|
| 1608 |
+
"10♣",
|
| 1609 |
+
"2♣"
|
| 1610 |
+
],
|
| 1611 |
+
"total": 12,
|
| 1612 |
+
"soft": false,
|
| 1613 |
+
"dealer_up": "8♠",
|
| 1614 |
+
"allowed": [
|
| 1615 |
+
"hit",
|
| 1616 |
+
"stand"
|
| 1617 |
+
],
|
| 1618 |
+
"decision": {
|
| 1619 |
+
"type": "choice",
|
| 1620 |
+
"choice": "hit",
|
| 1621 |
+
"probabilities": {
|
| 1622 |
+
"hit": 0.5500152300334371,
|
| 1623 |
+
"stand": 0.44998476996656295
|
| 1624 |
+
},
|
| 1625 |
+
"confidence": 0.10003046006687422
|
| 1626 |
+
},
|
| 1627 |
+
"risk": {
|
| 1628 |
+
"type": "score",
|
| 1629 |
+
"score": 1.5209343777308968,
|
| 1630 |
+
"legend": {
|
| 1631 |
+
"0": "非常有利",
|
| 1632 |
+
"1": "稍微有利",
|
| 1633 |
+
"2": "有點不利",
|
| 1634 |
+
"3": "非常不利"
|
| 1635 |
+
},
|
| 1636 |
+
"probabilities": {
|
| 1637 |
+
"0": 0.1166634305190376,
|
| 1638 |
+
"1": 0.3360687076482218,
|
| 1639 |
+
"2": 0.4569379154155469,
|
| 1640 |
+
"3": 0.0903299464171938
|
| 1641 |
+
},
|
| 1642 |
+
"confidence": 0.34027448489650913
|
| 1643 |
+
},
|
| 1644 |
+
"latency_ms": 3071,
|
| 1645 |
+
"drawn": "K♠",
|
| 1646 |
+
"total_after": 22
|
| 1647 |
+
}
|
| 1648 |
+
],
|
| 1649 |
+
"player_id": "a",
|
| 1650 |
+
"result": "lose",
|
| 1651 |
+
"net": -10,
|
| 1652 |
+
"bet_decision": {
|
| 1653 |
+
"decision": {
|
| 1654 |
+
"type": "choice",
|
| 1655 |
+
"choice": "min",
|
| 1656 |
+
"probabilities": {
|
| 1657 |
+
"min": 0.69219418752702,
|
| 1658 |
+
"standard": 0.21671663493691912,
|
| 1659 |
+
"max": 0.09108917753606088
|
| 1660 |
+
},
|
| 1661 |
+
"confidence": 0.5382912812905299
|
| 1662 |
+
},
|
| 1663 |
+
"bet": 10,
|
| 1664 |
+
"confidence": {
|
| 1665 |
+
"type": "score",
|
| 1666 |
+
"score": 1.2634052808341587,
|
| 1667 |
+
"legend": {
|
| 1668 |
+
"0": "沒信心",
|
| 1669 |
+
"1": "普通",
|
| 1670 |
+
"2": "有信心",
|
| 1671 |
+
"3": "非常有信心"
|
| 1672 |
+
},
|
| 1673 |
+
"probabilities": {
|
| 1674 |
+
"0": 0.17743488347704425,
|
| 1675 |
+
"1": 0.46766459898284457,
|
| 1676 |
+
"2": 0.2689608707690193,
|
| 1677 |
+
"3": 0.08593964677109182
|
| 1678 |
+
},
|
| 1679 |
+
"confidence": 0.38172495221175273
|
| 1680 |
+
},
|
| 1681 |
+
"latency_ms": 2427
|
| 1682 |
+
},
|
| 1683 |
+
"bankroll_after": 970,
|
| 1684 |
+
"id": "a",
|
| 1685 |
+
"name": "阿保",
|
| 1686 |
+
"style": "保守算牌派"
|
| 1687 |
+
},
|
| 1688 |
+
{
|
| 1689 |
+
"cards": [
|
| 1690 |
+
"10♠",
|
| 1691 |
+
"A♠"
|
| 1692 |
+
],
|
| 1693 |
+
"bet": 50,
|
| 1694 |
+
"doubled": false,
|
| 1695 |
+
"total": 21,
|
| 1696 |
+
"soft": true,
|
| 1697 |
+
"bust": false,
|
| 1698 |
+
"blackjack": true,
|
| 1699 |
+
"steps": [],
|
| 1700 |
+
"player_id": "b",
|
| 1701 |
+
"result": "blackjack",
|
| 1702 |
+
"net": 75,
|
| 1703 |
+
"bet_decision": {
|
| 1704 |
+
"decision": {
|
| 1705 |
+
"type": "choice",
|
| 1706 |
+
"choice": "max",
|
| 1707 |
+
"probabilities": {
|
| 1708 |
+
"min": 0.17415716770498335,
|
| 1709 |
+
"standard": 0.3849795123905866,
|
| 1710 |
+
"max": 0.44086331990443
|
| 1711 |
+
},
|
| 1712 |
+
"confidence": 0.161294979856645
|
| 1713 |
+
},
|
| 1714 |
+
"bet": 50,
|
| 1715 |
+
"confidence": {
|
| 1716 |
+
"type": "score",
|
| 1717 |
+
"score": 1.6670431235423304,
|
| 1718 |
+
"legend": {
|
| 1719 |
+
"0": "沒信心",
|
| 1720 |
+
"1": "普通",
|
| 1721 |
+
"2": "有信心",
|
| 1722 |
+
"3": "非常有信心"
|
| 1723 |
+
},
|
| 1724 |
+
"probabilities": {
|
| 1725 |
+
"0": 0.12673888933460348,
|
| 1726 |
+
"1": 0.2859844451294625,
|
| 1727 |
+
"2": 0.38077131819493437,
|
| 1728 |
+
"3": 0.20650534734099973
|
| 1729 |
+
},
|
| 1730 |
+
"confidence": 0.25403242886033084
|
| 1731 |
+
},
|
| 1732 |
+
"latency_ms": 2975
|
| 1733 |
+
},
|
| 1734 |
+
"bankroll_after": 1000,
|
| 1735 |
+
"id": "b",
|
| 1736 |
+
"name": "阿衝",
|
| 1737 |
+
"style": "激進壓迫派"
|
| 1738 |
+
}
|
| 1739 |
+
],
|
| 1740 |
+
"shoe_after": {
|
| 1741 |
+
"decks": 6,
|
| 1742 |
+
"remaining_cards": 258,
|
| 1743 |
+
"penetration_pct": 17,
|
| 1744 |
+
"cards_left_by_rank": {
|
| 1745 |
+
"A": 16,
|
| 1746 |
+
"2": 19,
|
| 1747 |
+
"3": 17,
|
| 1748 |
+
"4": 23,
|
| 1749 |
+
"5": 22,
|
| 1750 |
+
"6": 21,
|
| 1751 |
+
"7": 19,
|
| 1752 |
+
"8": 19,
|
| 1753 |
+
"9": 21,
|
| 1754 |
+
"10": 18,
|
| 1755 |
+
"J": 23,
|
| 1756 |
+
"Q": 23,
|
| 1757 |
+
"K": 17
|
| 1758 |
+
},
|
| 1759 |
+
"hi_lo_running_count": -5,
|
| 1760 |
+
"true_count": -1.01
|
| 1761 |
+
}
|
| 1762 |
+
},
|
| 1763 |
+
{
|
| 1764 |
+
"n": 8,
|
| 1765 |
+
"shoe_before": {
|
| 1766 |
+
"decks": 6,
|
| 1767 |
+
"remaining_cards": 252,
|
| 1768 |
+
"penetration_pct": 19,
|
| 1769 |
+
"cards_left_by_rank": {
|
| 1770 |
+
"A": 16,
|
| 1771 |
+
"2": 19,
|
| 1772 |
+
"3": 16,
|
| 1773 |
+
"4": 23,
|
| 1774 |
+
"5": 21,
|
| 1775 |
+
"6": 21,
|
| 1776 |
+
"7": 19,
|
| 1777 |
+
"8": 19,
|
| 1778 |
+
"9": 19,
|
| 1779 |
+
"10": 17,
|
| 1780 |
+
"J": 23,
|
| 1781 |
+
"Q": 22,
|
| 1782 |
+
"K": 17
|
| 1783 |
+
},
|
| 1784 |
+
"hi_lo_running_count": -5,
|
| 1785 |
+
"true_count": -1.03
|
| 1786 |
+
},
|
| 1787 |
+
"dealer_up": "10♦",
|
| 1788 |
+
"dealer_hole": "9♦",
|
| 1789 |
+
"dealer_revealed": true,
|
| 1790 |
+
"dealer": {
|
| 1791 |
+
"cards": [
|
| 1792 |
+
"10♦",
|
| 1793 |
+
"9♦"
|
| 1794 |
+
],
|
| 1795 |
+
"total": 19,
|
| 1796 |
+
"soft": false,
|
| 1797 |
+
"blackjack": false,
|
| 1798 |
+
"bust": false
|
| 1799 |
+
},
|
| 1800 |
+
"events": [
|
| 1801 |
+
"莊家明牌為 A 或 10 點,進行暗牌確認",
|
| 1802 |
+
"確認結果:莊家沒有 Blackjack",
|
| 1803 |
+
"莊家最終 19 點"
|
| 1804 |
+
],
|
| 1805 |
+
"players": [
|
| 1806 |
+
{
|
| 1807 |
+
"cards": [
|
| 1808 |
+
"3♥",
|
| 1809 |
+
"Q♦"
|
| 1810 |
+
],
|
| 1811 |
+
"bet": 10,
|
| 1812 |
+
"doubled": false,
|
| 1813 |
+
"total": 13,
|
| 1814 |
+
"soft": false,
|
| 1815 |
+
"bust": false,
|
| 1816 |
+
"blackjack": false,
|
| 1817 |
+
"steps": [
|
| 1818 |
+
{
|
| 1819 |
+
"type": "action",
|
| 1820 |
+
"cards": [
|
| 1821 |
+
"3♥",
|
| 1822 |
+
"Q♦"
|
| 1823 |
+
],
|
| 1824 |
+
"total": 13,
|
| 1825 |
+
"soft": false,
|
| 1826 |
+
"dealer_up": "10♦",
|
| 1827 |
+
"allowed": [
|
| 1828 |
+
"hit",
|
| 1829 |
+
"stand"
|
| 1830 |
+
],
|
| 1831 |
+
"decision": {
|
| 1832 |
+
"type": "choice",
|
| 1833 |
+
"choice": "stand",
|
| 1834 |
+
"probabilities": {
|
| 1835 |
+
"hit": 0.39634239573570534,
|
| 1836 |
+
"stand": 0.6036576042642946
|
| 1837 |
+
},
|
| 1838 |
+
"confidence": 0.2073152085285892
|
| 1839 |
+
},
|
| 1840 |
+
"risk": {
|
| 1841 |
+
"type": "score",
|
| 1842 |
+
"score": 1.3805666512347543,
|
| 1843 |
+
"legend": {
|
| 1844 |
+
"0": "非常有利",
|
| 1845 |
+
"1": "稍微有利",
|
| 1846 |
+
"2": "有點不利",
|
| 1847 |
+
"3": "非常不利"
|
| 1848 |
+
},
|
| 1849 |
+
"probabilities": {
|
| 1850 |
+
"0": 0.16969624145522536,
|
| 1851 |
+
"1": 0.3621346556920123,
|
| 1852 |
+
"2": 0.3860753130155444,
|
| 1853 |
+
"3": 0.08209378983721773
|
| 1854 |
+
},
|
| 1855 |
+
"confidence": 0.2163790715603192
|
| 1856 |
+
},
|
| 1857 |
+
"latency_ms": 2496,
|
| 1858 |
+
"drawn": null,
|
| 1859 |
+
"total_after": 13
|
| 1860 |
+
}
|
| 1861 |
+
],
|
| 1862 |
+
"player_id": "a",
|
| 1863 |
+
"result": "lose",
|
| 1864 |
+
"net": -10,
|
| 1865 |
+
"bet_decision": {
|
| 1866 |
+
"decision": {
|
| 1867 |
+
"type": "choice",
|
| 1868 |
+
"choice": "min",
|
| 1869 |
+
"probabilities": {
|
| 1870 |
+
"min": 0.7656214237065283,
|
| 1871 |
+
"standard": 0.14200820159033867,
|
| 1872 |
+
"max": 0.09237037470313315
|
| 1873 |
+
},
|
| 1874 |
+
"confidence": 0.6484321355597924
|
| 1875 |
+
},
|
| 1876 |
+
"bet": 10,
|
| 1877 |
+
"confidence": {
|
| 1878 |
+
"type": "score",
|
| 1879 |
+
"score": 1.2299018689518944,
|
| 1880 |
+
"legend": {
|
| 1881 |
+
"0": "沒信心",
|
| 1882 |
+
"1": "普通",
|
| 1883 |
+
"2": "有信心",
|
| 1884 |
+
"3": "非常有信心"
|
| 1885 |
+
},
|
| 1886 |
+
"probabilities": {
|
| 1887 |
+
"0": 0.19870975918080533,
|
| 1888 |
+
"1": 0.4610254792488059,
|
| 1889 |
+
"2": 0.2519178950080773,
|
| 1890 |
+
"3": 0.08834686656231128
|
| 1891 |
+
},
|
| 1892 |
+
"confidence": 0.3726786126864948
|
| 1893 |
+
},
|
| 1894 |
+
"latency_ms": 2564
|
| 1895 |
+
},
|
| 1896 |
+
"bankroll_after": 960,
|
| 1897 |
+
"id": "a",
|
| 1898 |
+
"name": "阿保",
|
| 1899 |
+
"style": "保守算牌派"
|
| 1900 |
+
},
|
| 1901 |
+
{
|
| 1902 |
+
"cards": [
|
| 1903 |
+
"5♦",
|
| 1904 |
+
"9♣",
|
| 1905 |
+
"3♠"
|
| 1906 |
+
],
|
| 1907 |
+
"bet": 50,
|
| 1908 |
+
"doubled": false,
|
| 1909 |
+
"total": 17,
|
| 1910 |
+
"soft": false,
|
| 1911 |
+
"bust": false,
|
| 1912 |
+
"blackjack": false,
|
| 1913 |
+
"steps": [
|
| 1914 |
+
{
|
| 1915 |
+
"type": "action",
|
| 1916 |
+
"cards": [
|
| 1917 |
+
"5♦",
|
| 1918 |
+
"9♣"
|
| 1919 |
+
],
|
| 1920 |
+
"total": 14,
|
| 1921 |
+
"soft": false,
|
| 1922 |
+
"dealer_up": "10♦",
|
| 1923 |
+
"allowed": [
|
| 1924 |
+
"hit",
|
| 1925 |
+
"stand"
|
| 1926 |
+
],
|
| 1927 |
+
"decision": {
|
| 1928 |
+
"type": "choice",
|
| 1929 |
+
"choice": "hit",
|
| 1930 |
+
"probabilities": {
|
| 1931 |
+
"hit": 0.6363603957200638,
|
| 1932 |
+
"stand": 0.3636396042799362
|
| 1933 |
+
},
|
| 1934 |
+
"confidence": 0.2727207914401275
|
| 1935 |
+
},
|
| 1936 |
+
"risk": {
|
| 1937 |
+
"type": "score",
|
| 1938 |
+
"score": 1.621485575327213,
|
| 1939 |
+
"legend": {
|
| 1940 |
+
"0": "非常有利",
|
| 1941 |
+
"1": "稍微有利",
|
| 1942 |
+
"2": "有點不利",
|
| 1943 |
+
"3": "非常不利"
|
| 1944 |
+
},
|
| 1945 |
+
"probabilities": {
|
| 1946 |
+
"0": 0.12499958056879856,
|
| 1947 |
+
"1": 0.28623851581568904,
|
| 1948 |
+
"2": 0.4310386513350132,
|
| 1949 |
+
"3": 0.15772325228049922
|
| 1950 |
+
},
|
| 1951 |
+
"confidence": 0.3060390707662146
|
| 1952 |
+
},
|
| 1953 |
+
"latency_ms": 4541,
|
| 1954 |
+
"drawn": "3♠",
|
| 1955 |
+
"total_after": 17
|
| 1956 |
+
},
|
| 1957 |
+
{
|
| 1958 |
+
"type": "action",
|
| 1959 |
+
"cards": [
|
| 1960 |
+
"5♦",
|
| 1961 |
+
"9♣",
|
| 1962 |
+
"3♠"
|
| 1963 |
+
],
|
| 1964 |
+
"total": 17,
|
| 1965 |
+
"soft": false,
|
| 1966 |
+
"dealer_up": "10♦",
|
| 1967 |
+
"allowed": [
|
| 1968 |
+
"hit",
|
| 1969 |
+
"stand"
|
| 1970 |
+
],
|
| 1971 |
+
"decision": {
|
| 1972 |
+
"type": "choice",
|
| 1973 |
+
"choice": "stand",
|
| 1974 |
+
"probabilities": {
|
| 1975 |
+
"hit": 0.41681588405691944,
|
| 1976 |
+
"stand": 0.5831841159430805
|
| 1977 |
+
},
|
| 1978 |
+
"confidence": 0.166368231886161
|
| 1979 |
+
},
|
| 1980 |
+
"risk": {
|
| 1981 |
+
"type": "score",
|
| 1982 |
+
"score": 1.3499666013886922,
|
| 1983 |
+
"legend": {
|
| 1984 |
+
"0": "非常有利",
|
| 1985 |
+
"1": "稍微有利",
|
| 1986 |
+
"2": "有點不利",
|
| 1987 |
+
"3": "非常不利"
|
| 1988 |
+
},
|
| 1989 |
+
"probabilities": {
|
| 1990 |
+
"0": 0.19919145911352995,
|
| 1991 |
+
"1": 0.34421202407439494,
|
| 1992 |
+
"2": 0.364034973121928,
|
| 1993 |
+
"3": 0.09256154369014709
|
| 1994 |
+
},
|
| 1995 |
+
"confidence": 0.16484351400839803
|
| 1996 |
+
},
|
| 1997 |
+
"latency_ms": 3635,
|
| 1998 |
+
"drawn": null,
|
| 1999 |
+
"total_after": 17
|
| 2000 |
+
}
|
| 2001 |
+
],
|
| 2002 |
+
"player_id": "b",
|
| 2003 |
+
"result": "lose",
|
| 2004 |
+
"net": -50,
|
| 2005 |
+
"bet_decision": {
|
| 2006 |
+
"decision": {
|
| 2007 |
+
"type": "choice",
|
| 2008 |
+
"choice": "max",
|
| 2009 |
+
"probabilities": {
|
| 2010 |
+
"min": 0.17807237353368274,
|
| 2011 |
+
"standard": 0.3629673835384618,
|
| 2012 |
+
"max": 0.4589602429278555
|
| 2013 |
+
},
|
| 2014 |
+
"confidence": 0.18844036439178322
|
| 2015 |
+
},
|
| 2016 |
+
"bet": 50,
|
| 2017 |
+
"confidence": {
|
| 2018 |
+
"type": "score",
|
| 2019 |
+
"score": 1.6467384307127664,
|
| 2020 |
+
"legend": {
|
| 2021 |
+
"0": "沒信心",
|
| 2022 |
+
"1": "普通",
|
| 2023 |
+
"2": "有信心",
|
| 2024 |
+
"3": "非常有信心"
|
| 2025 |
+
},
|
| 2026 |
+
"probabilities": {
|
| 2027 |
+
"0": 0.12712966024657835,
|
| 2028 |
+
"1": 0.29849798780601905,
|
| 2029 |
+
"2": 0.37487661293546065,
|
| 2030 |
+
"3": 0.199495739011942
|
| 2031 |
+
},
|
| 2032 |
+
"confidence": 0.24774695268888225
|
| 2033 |
+
},
|
| 2034 |
+
"latency_ms": 3080
|
| 2035 |
+
},
|
| 2036 |
+
"bankroll_after": 950,
|
| 2037 |
+
"id": "b",
|
| 2038 |
+
"name": "阿衝",
|
| 2039 |
+
"style": "激進壓迫派"
|
| 2040 |
+
}
|
| 2041 |
+
],
|
| 2042 |
+
"shoe_after": {
|
| 2043 |
+
"decks": 6,
|
| 2044 |
+
"remaining_cards": 251,
|
| 2045 |
+
"penetration_pct": 20,
|
| 2046 |
+
"cards_left_by_rank": {
|
| 2047 |
+
"A": 16,
|
| 2048 |
+
"2": 19,
|
| 2049 |
+
"3": 15,
|
| 2050 |
+
"4": 23,
|
| 2051 |
+
"5": 21,
|
| 2052 |
+
"6": 21,
|
| 2053 |
+
"7": 19,
|
| 2054 |
+
"8": 19,
|
| 2055 |
+
"9": 19,
|
| 2056 |
+
"10": 17,
|
| 2057 |
+
"J": 23,
|
| 2058 |
+
"Q": 22,
|
| 2059 |
+
"K": 17
|
| 2060 |
+
},
|
| 2061 |
+
"hi_lo_running_count": -4,
|
| 2062 |
+
"true_count": -0.83
|
| 2063 |
+
}
|
| 2064 |
+
},
|
| 2065 |
+
{
|
| 2066 |
+
"n": 9,
|
| 2067 |
+
"shoe_before": {
|
| 2068 |
+
"decks": 6,
|
| 2069 |
+
"remaining_cards": 245,
|
| 2070 |
+
"penetration_pct": 21,
|
| 2071 |
+
"cards_left_by_rank": {
|
| 2072 |
+
"A": 14,
|
| 2073 |
+
"2": 19,
|
| 2074 |
+
"3": 15,
|
| 2075 |
+
"4": 23,
|
| 2076 |
+
"5": 21,
|
| 2077 |
+
"6": 20,
|
| 2078 |
+
"7": 18,
|
| 2079 |
+
"8": 19,
|
| 2080 |
+
"9": 18,
|
| 2081 |
+
"10": 17,
|
| 2082 |
+
"J": 23,
|
| 2083 |
+
"Q": 22,
|
| 2084 |
+
"K": 16
|
| 2085 |
+
},
|
| 2086 |
+
"hi_lo_running_count": -6,
|
| 2087 |
+
"true_count": -1.27
|
| 2088 |
+
},
|
| 2089 |
+
"dealer_up": "K♥",
|
| 2090 |
+
"dealer_hole": "7♣",
|
| 2091 |
+
"dealer_revealed": true,
|
| 2092 |
+
"dealer": {
|
| 2093 |
+
"cards": [
|
| 2094 |
+
"K♥",
|
| 2095 |
+
"7♣"
|
| 2096 |
+
],
|
| 2097 |
+
"total": 17,
|
| 2098 |
+
"soft": false,
|
| 2099 |
+
"blackjack": false,
|
| 2100 |
+
"bust": false
|
| 2101 |
+
},
|
| 2102 |
+
"events": [
|
| 2103 |
+
"莊家明牌為 A 或 10 點,進行暗牌確認",
|
| 2104 |
+
"確認結果:莊家沒有 Blackjack",
|
| 2105 |
+
"莊家最終 17 點"
|
| 2106 |
+
],
|
| 2107 |
+
"players": [
|
| 2108 |
+
{
|
| 2109 |
+
"cards": [
|
| 2110 |
+
"6♦",
|
| 2111 |
+
"9♦"
|
| 2112 |
+
],
|
| 2113 |
+
"bet": 10,
|
| 2114 |
+
"doubled": false,
|
| 2115 |
+
"total": 15,
|
| 2116 |
+
"soft": false,
|
| 2117 |
+
"bust": false,
|
| 2118 |
+
"blackjack": false,
|
| 2119 |
+
"steps": [
|
| 2120 |
+
{
|
| 2121 |
+
"type": "action",
|
| 2122 |
+
"cards": [
|
| 2123 |
+
"6♦",
|
| 2124 |
+
"9♦"
|
| 2125 |
+
],
|
| 2126 |
+
"total": 15,
|
| 2127 |
+
"soft": false,
|
| 2128 |
+
"dealer_up": "K♥",
|
| 2129 |
+
"allowed": [
|
| 2130 |
+
"hit",
|
| 2131 |
+
"stand"
|
| 2132 |
+
],
|
| 2133 |
+
"decision": {
|
| 2134 |
+
"type": "choice",
|
| 2135 |
+
"choice": "stand",
|
| 2136 |
+
"probabilities": {
|
| 2137 |
+
"hit": 0.4065556882060927,
|
| 2138 |
+
"stand": 0.5934443117939072
|
| 2139 |
+
},
|
| 2140 |
+
"confidence": 0.18688862358781444
|
| 2141 |
+
},
|
| 2142 |
+
"risk": {
|
| 2143 |
+
"type": "score",
|
| 2144 |
+
"score": 1.4625511690470288,
|
| 2145 |
+
"legend": {
|
| 2146 |
+
"0": "非常有利",
|
| 2147 |
+
"1": "稍微有利",
|
| 2148 |
+
"2": "有點不利",
|
| 2149 |
+
"3": "非常不利"
|
| 2150 |
+
},
|
| 2151 |
+
"probabilities": {
|
| 2152 |
+
"0": 0.13799581285873977,
|
| 2153 |
+
"1": 0.34352512010586594,
|
| 2154 |
+
"2": 0.43641115216501986,
|
| 2155 |
+
"3": 0.08206791487037438
|
| 2156 |
+
},
|
| 2157 |
+
"confidence": 0.29841533930628017
|
| 2158 |
+
},
|
| 2159 |
+
"latency_ms": 2666,
|
| 2160 |
+
"drawn": null,
|
| 2161 |
+
"total_after": 15
|
| 2162 |
+
}
|
| 2163 |
+
],
|
| 2164 |
+
"player_id": "a",
|
| 2165 |
+
"result": "lose",
|
| 2166 |
+
"net": -10,
|
| 2167 |
+
"bet_decision": {
|
| 2168 |
+
"decision": {
|
| 2169 |
+
"type": "choice",
|
| 2170 |
+
"choice": "min",
|
| 2171 |
+
"probabilities": {
|
| 2172 |
+
"min": 0.7083570651368878,
|
| 2173 |
+
"standard": 0.19545918656860584,
|
| 2174 |
+
"max": 0.09618374829450631
|
| 2175 |
+
},
|
| 2176 |
+
"confidence": 0.5625355977053317
|
| 2177 |
+
},
|
| 2178 |
+
"bet": 10,
|
| 2179 |
+
"confidence": {
|
| 2180 |
+
"type": "score",
|
| 2181 |
+
"score": 1.213641546925921,
|
| 2182 |
+
"legend": {
|
| 2183 |
+
"0": "沒信心",
|
| 2184 |
+
"1": "普通",
|
| 2185 |
+
"2": "有信心",
|
| 2186 |
+
"3": "非常有信心"
|
| 2187 |
+
},
|
| 2188 |
+
"probabilities": {
|
| 2189 |
+
"0": 0.20136564532087406,
|
| 2190 |
+
"1": 0.4638059699745116,
|
| 2191 |
+
"2": 0.25464957716243375,
|
| 2192 |
+
"3": 0.08017880754218061
|
| 2193 |
+
},
|
| 2194 |
+
"confidence": 0.38362716243233097
|
| 2195 |
+
},
|
| 2196 |
+
"latency_ms": 1984
|
| 2197 |
+
},
|
| 2198 |
+
"bankroll_after": 950,
|
| 2199 |
+
"id": "a",
|
| 2200 |
+
"name": "阿保",
|
| 2201 |
+
"style": "保守算牌派"
|
| 2202 |
+
},
|
| 2203 |
+
{
|
| 2204 |
+
"cards": [
|
| 2205 |
+
"A♥",
|
| 2206 |
+
"A♠",
|
| 2207 |
+
"Q♠",
|
| 2208 |
+
"8♣"
|
| 2209 |
+
],
|
| 2210 |
+
"bet": 50,
|
| 2211 |
+
"doubled": false,
|
| 2212 |
+
"total": 20,
|
| 2213 |
+
"soft": false,
|
| 2214 |
+
"bust": false,
|
| 2215 |
+
"blackjack": false,
|
| 2216 |
+
"steps": [
|
| 2217 |
+
{
|
| 2218 |
+
"type": "action",
|
| 2219 |
+
"cards": [
|
| 2220 |
+
"A♥",
|
| 2221 |
+
"A♠"
|
| 2222 |
+
],
|
| 2223 |
+
"total": 12,
|
| 2224 |
+
"soft": true,
|
| 2225 |
+
"dealer_up": "K♥",
|
| 2226 |
+
"allowed": [
|
| 2227 |
+
"hit",
|
| 2228 |
+
"stand"
|
| 2229 |
+
],
|
| 2230 |
+
"decision": {
|
| 2231 |
+
"type": "choice",
|
| 2232 |
+
"choice": "hit",
|
| 2233 |
+
"probabilities": {
|
| 2234 |
+
"hit": 0.5939977924213096,
|
| 2235 |
+
"stand": 0.4060022075786904
|
| 2236 |
+
},
|
| 2237 |
+
"confidence": 0.18799558484261913
|
| 2238 |
+
},
|
| 2239 |
+
"risk": {
|
| 2240 |
+
"type": "score",
|
| 2241 |
+
"score": 1.4685976982166182,
|
| 2242 |
+
"legend": {
|
| 2243 |
+
"0": "非常有利",
|
| 2244 |
+
"1": "稍微有利",
|
| 2245 |
+
"2": "有點不利",
|
| 2246 |
+
"3": "非常不利"
|
| 2247 |
+
},
|
| 2248 |
+
"probabilities": {
|
| 2249 |
+
"0": 0.18819208543459834,
|
| 2250 |
+
"1": 0.30035061456540596,
|
| 2251 |
+
"2": 0.3661248163487746,
|
| 2252 |
+
"3": 0.14533248365122103
|
| 2253 |
+
},
|
| 2254 |
+
"confidence": 0.17793273091417627
|
| 2255 |
+
},
|
| 2256 |
+
"latency_ms": 3686,
|
| 2257 |
+
"drawn": "Q♠",
|
| 2258 |
+
"total_after": 12
|
| 2259 |
+
},
|
| 2260 |
+
{
|
| 2261 |
+
"type": "action",
|
| 2262 |
+
"cards": [
|
| 2263 |
+
"A♥",
|
| 2264 |
+
"A♠",
|
| 2265 |
+
"Q♠"
|
| 2266 |
+
],
|
| 2267 |
+
"total": 12,
|
| 2268 |
+
"soft": false,
|
| 2269 |
+
"dealer_up": "K♥",
|
| 2270 |
+
"allowed": [
|
| 2271 |
+
"hit",
|
| 2272 |
+
"stand"
|
| 2273 |
+
],
|
| 2274 |
+
"decision": {
|
| 2275 |
+
"type": "choice",
|
| 2276 |
+
"choice": "hit",
|
| 2277 |
+
"probabilities": {
|
| 2278 |
+
"hit": 0.6052363769804255,
|
| 2279 |
+
"stand": 0.39476362301957446
|
| 2280 |
+
},
|
| 2281 |
+
"confidence": 0.21047275396085108
|
| 2282 |
+
},
|
| 2283 |
+
"risk": {
|
| 2284 |
+
"type": "score",
|
| 2285 |
+
"score": 1.5574917347618844,
|
| 2286 |
+
"legend": {
|
| 2287 |
+
"0": "非常有利",
|
| 2288 |
+
"1": "稍微有利",
|
| 2289 |
+
"2": "有點不利",
|
| 2290 |
+
"3": "非常不利"
|
| 2291 |
+
},
|
| 2292 |
+
"probabilities": {
|
| 2293 |
+
"0": 0.16846858723710814,
|
| 2294 |
+
"1": 0.28599754847447173,
|
| 2295 |
+
"2": 0.3651074065778472,
|
| 2296 |
+
"3": 0.18042645771057275
|
| 2297 |
+
},
|
| 2298 |
+
"confidence": 0.19663881934073923
|
| 2299 |
+
},
|
| 2300 |
+
"latency_ms": 3449,
|
| 2301 |
+
"drawn": "8♣",
|
| 2302 |
+
"total_after": 20
|
| 2303 |
+
},
|
| 2304 |
+
{
|
| 2305 |
+
"type": "action",
|
| 2306 |
+
"cards": [
|
| 2307 |
+
"A♥",
|
| 2308 |
+
"A♠",
|
| 2309 |
+
"Q♠",
|
| 2310 |
+
"8♣"
|
| 2311 |
+
],
|
| 2312 |
+
"total": 20,
|
| 2313 |
+
"soft": false,
|
| 2314 |
+
"dealer_up": "K♥",
|
| 2315 |
+
"allowed": [
|
| 2316 |
+
"hit",
|
| 2317 |
+
"stand"
|
| 2318 |
+
],
|
| 2319 |
+
"decision": {
|
| 2320 |
+
"type": "choice",
|
| 2321 |
+
"choice": "stand",
|
| 2322 |
+
"probabilities": {
|
| 2323 |
+
"hit": 0.3199784228049078,
|
| 2324 |
+
"stand": 0.6800215771950923
|
| 2325 |
+
},
|
| 2326 |
+
"confidence": 0.3600431543901845
|
| 2327 |
+
},
|
| 2328 |
+
"risk": {
|
| 2329 |
+
"type": "score",
|
| 2330 |
+
"score": 1.2731733674465937,
|
| 2331 |
+
"legend": {
|
| 2332 |
+
"0": "非常有利",
|
| 2333 |
+
"1": "稍微有利",
|
| 2334 |
+
"2": "有點不利",
|
| 2335 |
+
"3": "非常不利"
|
| 2336 |
+
},
|
| 2337 |
+
"probabilities": {
|
| 2338 |
+
"0": 0.2453494945322414,
|
| 2339 |
+
"1": 0.3366067819479323,
|
| 2340 |
+
"2": 0.31756458506081714,
|
| 2341 |
+
"3": 0.10047913845900906
|
| 2342 |
+
},
|
| 2343 |
+
"confidence": 0.23612764348892334
|
| 2344 |
+
},
|
| 2345 |
+
"latency_ms": 3726,
|
| 2346 |
+
"drawn": null,
|
| 2347 |
+
"total_after": 20
|
| 2348 |
+
}
|
| 2349 |
+
],
|
| 2350 |
+
"player_id": "b",
|
| 2351 |
+
"result": "win",
|
| 2352 |
+
"net": 50,
|
| 2353 |
+
"bet_decision": {
|
| 2354 |
+
"decision": {
|
| 2355 |
+
"type": "choice",
|
| 2356 |
+
"choice": "max",
|
| 2357 |
+
"probabilities": {
|
| 2358 |
+
"min": 0.18317788211857033,
|
| 2359 |
+
"standard": 0.37100120794841007,
|
| 2360 |
+
"max": 0.4458209099330196
|
| 2361 |
+
},
|
| 2362 |
+
"confidence": 0.1687313648995294
|
| 2363 |
+
},
|
| 2364 |
+
"bet": 50,
|
| 2365 |
+
"confidence": {
|
| 2366 |
+
"type": "score",
|
| 2367 |
+
"score": 1.7016208412378346,
|
| 2368 |
+
"legend": {
|
| 2369 |
+
"0": "沒信心",
|
| 2370 |
+
"1": "普通",
|
| 2371 |
+
"2": "有信心",
|
| 2372 |
+
"3": "非常有信心"
|
| 2373 |
+
},
|
| 2374 |
+
"probabilities": {
|
| 2375 |
+
"0": 0.1245882351848078,
|
| 2376 |
+
"1": 0.2690667058185058,
|
| 2377 |
+
"2": 0.3864810415707307,
|
| 2378 |
+
"3": 0.21986401742595574
|
| 2379 |
+
},
|
| 2380 |
+
"confidence": 0.2618928063859228
|
| 2381 |
+
},
|
| 2382 |
+
"latency_ms": 2915
|
| 2383 |
+
},
|
| 2384 |
+
"bankroll_after": 1000,
|
| 2385 |
+
"id": "b",
|
| 2386 |
+
"name": "阿衝",
|
| 2387 |
+
"style": "激進壓迫派"
|
| 2388 |
+
}
|
| 2389 |
+
],
|
| 2390 |
+
"shoe_after": {
|
| 2391 |
+
"decks": 6,
|
| 2392 |
+
"remaining_cards": 243,
|
| 2393 |
+
"penetration_pct": 22,
|
| 2394 |
+
"cards_left_by_rank": {
|
| 2395 |
+
"A": 14,
|
| 2396 |
+
"2": 19,
|
| 2397 |
+
"3": 15,
|
| 2398 |
+
"4": 23,
|
| 2399 |
+
"5": 21,
|
| 2400 |
+
"6": 20,
|
| 2401 |
+
"7": 18,
|
| 2402 |
+
"8": 18,
|
| 2403 |
+
"9": 18,
|
| 2404 |
+
"10": 17,
|
| 2405 |
+
"J": 23,
|
| 2406 |
+
"Q": 21,
|
| 2407 |
+
"K": 16
|
| 2408 |
+
},
|
| 2409 |
+
"hi_lo_running_count": -7,
|
| 2410 |
+
"true_count": -1.5
|
| 2411 |
+
}
|
| 2412 |
+
},
|
| 2413 |
+
{
|
| 2414 |
+
"n": 10,
|
| 2415 |
+
"shoe_before": {
|
| 2416 |
+
"decks": 6,
|
| 2417 |
+
"remaining_cards": 237,
|
| 2418 |
+
"penetration_pct": 24,
|
| 2419 |
+
"cards_left_by_rank": {
|
| 2420 |
+
"A": 14,
|
| 2421 |
+
"2": 18,
|
| 2422 |
+
"3": 15,
|
| 2423 |
+
"4": 22,
|
| 2424 |
+
"5": 19,
|
| 2425 |
+
"6": 20,
|
| 2426 |
+
"7": 18,
|
| 2427 |
+
"8": 18,
|
| 2428 |
+
"9": 17,
|
| 2429 |
+
"10": 17,
|
| 2430 |
+
"J": 23,
|
| 2431 |
+
"Q": 21,
|
| 2432 |
+
"K": 15
|
| 2433 |
+
},
|
| 2434 |
+
"hi_lo_running_count": -4,
|
| 2435 |
+
"true_count": -0.88
|
| 2436 |
+
},
|
| 2437 |
+
"dealer_up": "4♦",
|
| 2438 |
+
"dealer_hole": "5♣",
|
| 2439 |
+
"dealer_revealed": true,
|
| 2440 |
+
"dealer": {
|
| 2441 |
+
"cards": [
|
| 2442 |
+
"4♦",
|
| 2443 |
+
"5♣",
|
| 2444 |
+
"A♦"
|
| 2445 |
+
],
|
| 2446 |
+
"total": 20,
|
| 2447 |
+
"soft": true,
|
| 2448 |
+
"blackjack": false,
|
| 2449 |
+
"bust": false
|
| 2450 |
+
},
|
| 2451 |
+
"events": [
|
| 2452 |
+
"莊家補牌 A♦ → 20 點",
|
| 2453 |
+
"莊家最終 20 點"
|
| 2454 |
+
],
|
| 2455 |
+
"players": [
|
| 2456 |
+
{
|
| 2457 |
+
"cards": [
|
| 2458 |
+
"5♥",
|
| 2459 |
+
"2♦",
|
| 2460 |
+
"3♦",
|
| 2461 |
+
"5♥",
|
| 2462 |
+
"Q♠"
|
| 2463 |
+
],
|
| 2464 |
+
"bet": 10,
|
| 2465 |
+
"doubled": false,
|
| 2466 |
+
"total": 25,
|
| 2467 |
+
"soft": false,
|
| 2468 |
+
"bust": true,
|
| 2469 |
+
"blackjack": false,
|
| 2470 |
+
"steps": [
|
| 2471 |
+
{
|
| 2472 |
+
"type": "action",
|
| 2473 |
+
"cards": [
|
| 2474 |
+
"5♥",
|
| 2475 |
+
"2♦"
|
| 2476 |
+
],
|
| 2477 |
+
"total": 7,
|
| 2478 |
+
"soft": false,
|
| 2479 |
+
"dealer_up": "4♦",
|
| 2480 |
+
"allowed": [
|
| 2481 |
+
"hit",
|
| 2482 |
+
"stand"
|
| 2483 |
+
],
|
| 2484 |
+
"decision": {
|
| 2485 |
+
"type": "choice",
|
| 2486 |
+
"choice": "hit",
|
| 2487 |
+
"probabilities": {
|
| 2488 |
+
"hit": 0.7694504382534303,
|
| 2489 |
+
"stand": 0.23054956174656965
|
| 2490 |
+
},
|
| 2491 |
+
"confidence": 0.5389008765068606
|
| 2492 |
+
},
|
| 2493 |
+
"risk": {
|
| 2494 |
+
"type": "score",
|
| 2495 |
+
"score": 2.1228442812944257,
|
| 2496 |
+
"legend": {
|
| 2497 |
+
"0": "非常有利",
|
| 2498 |
+
"1": "稍微有利",
|
| 2499 |
+
"2": "有點不利",
|
| 2500 |
+
"3": "非常不利"
|
| 2501 |
+
},
|
| 2502 |
+
"probabilities": {
|
| 2503 |
+
"0": 0.054250014780109976,
|
| 2504 |
+
"1": 0.1406058459410434,
|
| 2505 |
+
"2": 0.4331939824831577,
|
| 2506 |
+
"3": 0.37195015679568905
|
| 2507 |
+
},
|
| 2508 |
+
"confidence": 0.3789439677030476
|
| 2509 |
+
},
|
| 2510 |
+
"latency_ms": 2707,
|
| 2511 |
+
"drawn": "3♦",
|
| 2512 |
+
"total_after": 10
|
| 2513 |
+
},
|
| 2514 |
+
{
|
| 2515 |
+
"type": "action",
|
| 2516 |
+
"cards": [
|
| 2517 |
+
"5♥",
|
| 2518 |
+
"2♦",
|
| 2519 |
+
"3♦"
|
| 2520 |
+
],
|
| 2521 |
+
"total": 10,
|
| 2522 |
+
"soft": false,
|
| 2523 |
+
"dealer_up": "4♦",
|
| 2524 |
+
"allowed": [
|
| 2525 |
+
"hit",
|
| 2526 |
+
"stand"
|
| 2527 |
+
],
|
| 2528 |
+
"decision": {
|
| 2529 |
+
"type": "choice",
|
| 2530 |
+
"choice": "hit",
|
| 2531 |
+
"probabilities": {
|
| 2532 |
+
"hit": 0.7577920957558095,
|
| 2533 |
+
"stand": 0.24220790424419048
|
| 2534 |
+
},
|
| 2535 |
+
"confidence": 0.515584191511619
|
| 2536 |
+
},
|
| 2537 |
+
"risk": {
|
| 2538 |
+
"type": "score",
|
| 2539 |
+
"score": 1.6885405801021331,
|
| 2540 |
+
"legend": {
|
| 2541 |
+
"0": "非常有利",
|
| 2542 |
+
"1": "稍微有利",
|
| 2543 |
+
"2": "有點不利",
|
| 2544 |
+
"3": "非常不利"
|
| 2545 |
+
},
|
| 2546 |
+
"probabilities": {
|
| 2547 |
+
"0": 0.08428075713214693,
|
| 2548 |
+
"1": 0.28471381134884216,
|
| 2549 |
+
"2": 0.489189525803742,
|
| 2550 |
+
"3": 0.14181590571526903
|
| 2551 |
+
},
|
| 2552 |
+
"confidence": 0.404908768671595
|
| 2553 |
+
},
|
| 2554 |
+
"latency_ms": 2613,
|
| 2555 |
+
"drawn": "5♥",
|
| 2556 |
+
"total_after": 15
|
| 2557 |
+
},
|
| 2558 |
+
{
|
| 2559 |
+
"type": "action",
|
| 2560 |
+
"cards": [
|
| 2561 |
+
"5♥",
|
| 2562 |
+
"2♦",
|
| 2563 |
+
"3♦",
|
| 2564 |
+
"5♥"
|
| 2565 |
+
],
|
| 2566 |
+
"total": 15,
|
| 2567 |
+
"soft": false,
|
| 2568 |
+
"dealer_up": "4♦",
|
| 2569 |
+
"allowed": [
|
| 2570 |
+
"hit",
|
| 2571 |
+
"stand"
|
| 2572 |
+
],
|
| 2573 |
+
"decision": {
|
| 2574 |
+
"type": "choice",
|
| 2575 |
+
"choice": "hit",
|
| 2576 |
+
"probabilities": {
|
| 2577 |
+
"hit": 0.5788127737999339,
|
| 2578 |
+
"stand": 0.421187226200066
|
| 2579 |
+
},
|
| 2580 |
+
"confidence": 0.1576255475998678
|
| 2581 |
+
},
|
| 2582 |
+
"risk": {
|
| 2583 |
+
"type": "score",
|
| 2584 |
+
"score": 1.4818083791924999,
|
| 2585 |
+
"legend": {
|
| 2586 |
+
"0": "非常有利",
|
| 2587 |
+
"1": "稍微有利",
|
| 2588 |
+
"2": "有點不利",
|
| 2589 |
+
"3": "非常不利"
|
| 2590 |
+
},
|
| 2591 |
+
"probabilities": {
|
| 2592 |
+
"0": 0.11315441258530298,
|
| 2593 |
+
"1": 0.36485505760468034,
|
| 2594 |
+
"2": 0.4490182678422307,
|
| 2595 |
+
"3": 0.07297226196778603
|
| 2596 |
+
},
|
| 2597 |
+
"confidence": 0.33586385525692763
|
| 2598 |
+
},
|
| 2599 |
+
"latency_ms": 2554,
|
| 2600 |
+
"drawn": "Q♠",
|
| 2601 |
+
"total_after": 25
|
| 2602 |
+
}
|
| 2603 |
+
],
|
| 2604 |
+
"player_id": "a",
|
| 2605 |
+
"result": "lose",
|
| 2606 |
+
"net": -10,
|
| 2607 |
+
"bet_decision": {
|
| 2608 |
+
"decision": {
|
| 2609 |
+
"type": "choice",
|
| 2610 |
+
"choice": "min",
|
| 2611 |
+
"probabilities": {
|
| 2612 |
+
"min": 0.7286488493616847,
|
| 2613 |
+
"standard": 0.175529357430751,
|
| 2614 |
+
"max": 0.09582179320756426
|
| 2615 |
+
},
|
| 2616 |
+
"confidence": 0.5929732740425271
|
| 2617 |
+
},
|
| 2618 |
+
"bet": 10,
|
| 2619 |
+
"confidence": {
|
| 2620 |
+
"type": "score",
|
| 2621 |
+
"score": 1.169905749673971,
|
| 2622 |
+
"legend": {
|
| 2623 |
+
"0": "沒信心",
|
| 2624 |
+
"1": "普通",
|
| 2625 |
+
"2": "有信心",
|
| 2626 |
+
"3": "非常有信心"
|
| 2627 |
+
},
|
| 2628 |
+
"probabilities": {
|
| 2629 |
+
"0": 0.216524204542941,
|
| 2630 |
+
"1": 0.46785689500435057,
|
| 2631 |
+
"2": 0.24480784668850483,
|
| 2632 |
+
"3": 0.07081105376420364
|
| 2633 |
+
},
|
| 2634 |
+
"confidence": 0.39704584124014686
|
| 2635 |
+
},
|
| 2636 |
+
"latency_ms": 2177
|
| 2637 |
+
},
|
| 2638 |
+
"bankroll_after": 940,
|
| 2639 |
+
"id": "a",
|
| 2640 |
+
"name": "阿保",
|
| 2641 |
+
"style": "保守算牌派"
|
| 2642 |
+
},
|
| 2643 |
+
{
|
| 2644 |
+
"cards": [
|
| 2645 |
+
"K♠",
|
| 2646 |
+
"9♥"
|
| 2647 |
+
],
|
| 2648 |
+
"bet": 50,
|
| 2649 |
+
"doubled": false,
|
| 2650 |
+
"total": 19,
|
| 2651 |
+
"soft": false,
|
| 2652 |
+
"bust": false,
|
| 2653 |
+
"blackjack": false,
|
| 2654 |
+
"steps": [
|
| 2655 |
+
{
|
| 2656 |
+
"type": "action",
|
| 2657 |
+
"cards": [
|
| 2658 |
+
"K♠",
|
| 2659 |
+
"9♥"
|
| 2660 |
+
],
|
| 2661 |
+
"total": 19,
|
| 2662 |
+
"soft": false,
|
| 2663 |
+
"dealer_up": "4♦",
|
| 2664 |
+
"allowed": [
|
| 2665 |
+
"hit",
|
| 2666 |
+
"stand"
|
| 2667 |
+
],
|
| 2668 |
+
"decision": {
|
| 2669 |
+
"type": "choice",
|
| 2670 |
+
"choice": "stand",
|
| 2671 |
+
"probabilities": {
|
| 2672 |
+
"hit": 0.29597815331328925,
|
| 2673 |
+
"stand": 0.7040218466867108
|
| 2674 |
+
},
|
| 2675 |
+
"confidence": 0.4080436933734215
|
| 2676 |
+
},
|
| 2677 |
+
"risk": {
|
| 2678 |
+
"type": "score",
|
| 2679 |
+
"score": 1.0629240761621292,
|
| 2680 |
+
"legend": {
|
| 2681 |
+
"0": "非常有利",
|
| 2682 |
+
"1": "稍微有利",
|
| 2683 |
+
"2": "有點不利",
|
| 2684 |
+
"3": "非常不利"
|
| 2685 |
+
},
|
| 2686 |
+
"probabilities": {
|
| 2687 |
+
"0": 0.2850042266335565,
|
| 2688 |
+
"1": 0.40113920626045185,
|
| 2689 |
+
"2": 0.2797848314162972,
|
| 2690 |
+
"3": 0.03407173568969435
|
| 2691 |
+
},
|
| 2692 |
+
"confidence": 0.3670674705707576
|
| 2693 |
+
},
|
| 2694 |
+
"latency_ms": 3464,
|
| 2695 |
+
"drawn": null,
|
| 2696 |
+
"total_after": 19
|
| 2697 |
+
}
|
| 2698 |
+
],
|
| 2699 |
+
"player_id": "b",
|
| 2700 |
+
"result": "lose",
|
| 2701 |
+
"net": -50,
|
| 2702 |
+
"bet_decision": {
|
| 2703 |
+
"decision": {
|
| 2704 |
+
"type": "choice",
|
| 2705 |
+
"choice": "max",
|
| 2706 |
+
"probabilities": {
|
| 2707 |
+
"min": 0.17685549991326993,
|
| 2708 |
+
"standard": 0.3770309433442875,
|
| 2709 |
+
"max": 0.44611355674244274
|
| 2710 |
+
},
|
| 2711 |
+
"confidence": 0.1691703351136641
|
| 2712 |
+
},
|
| 2713 |
+
"bet": 50,
|
| 2714 |
+
"confidence": {
|
| 2715 |
+
"type": "score",
|
| 2716 |
+
"score": 1.6562748371119183,
|
| 2717 |
+
"legend": {
|
| 2718 |
+
"0": "沒信心",
|
| 2719 |
+
"1": "普通",
|
| 2720 |
+
"2": "有信心",
|
| 2721 |
+
"3": "非常有信心"
|
| 2722 |
+
},
|
| 2723 |
+
"probabilities": {
|
| 2724 |
+
"0": 0.1332817973157961,
|
| 2725 |
+
"1": 0.2844193759896284,
|
| 2726 |
+
"2": 0.3750410189614367,
|
| 2727 |
+
"3": 0.20725780773313882
|
| 2728 |
+
},
|
| 2729 |
+
"confidence": 0.24175922164564057
|
| 2730 |
+
},
|
| 2731 |
+
"latency_ms": 2647
|
| 2732 |
+
},
|
| 2733 |
+
"bankroll_after": 950,
|
| 2734 |
+
"id": "b",
|
| 2735 |
+
"name": "阿衝",
|
| 2736 |
+
"style": "激進壓迫派"
|
| 2737 |
+
}
|
| 2738 |
+
],
|
| 2739 |
+
"shoe_after": {
|
| 2740 |
+
"decks": 6,
|
| 2741 |
+
"remaining_cards": 233,
|
| 2742 |
+
"penetration_pct": 25,
|
| 2743 |
+
"cards_left_by_rank": {
|
| 2744 |
+
"A": 13,
|
| 2745 |
+
"2": 18,
|
| 2746 |
+
"3": 14,
|
| 2747 |
+
"4": 22,
|
| 2748 |
+
"5": 18,
|
| 2749 |
+
"6": 20,
|
| 2750 |
+
"7": 18,
|
| 2751 |
+
"8": 18,
|
| 2752 |
+
"9": 17,
|
| 2753 |
+
"10": 17,
|
| 2754 |
+
"J": 23,
|
| 2755 |
+
"Q": 20,
|
| 2756 |
+
"K": 15
|
| 2757 |
+
},
|
| 2758 |
+
"hi_lo_running_count": -4,
|
| 2759 |
+
"true_count": -0.89
|
| 2760 |
+
}
|
| 2761 |
+
},
|
| 2762 |
+
{
|
| 2763 |
+
"n": 11,
|
| 2764 |
+
"shoe_before": {
|
| 2765 |
+
"decks": 6,
|
| 2766 |
+
"remaining_cards": 227,
|
| 2767 |
+
"penetration_pct": 27,
|
| 2768 |
+
"cards_left_by_rank": {
|
| 2769 |
+
"A": 13,
|
| 2770 |
+
"2": 17,
|
| 2771 |
+
"3": 13,
|
| 2772 |
+
"4": 22,
|
| 2773 |
+
"5": 18,
|
| 2774 |
+
"6": 19,
|
| 2775 |
+
"7": 18,
|
| 2776 |
+
"8": 18,
|
| 2777 |
+
"9": 17,
|
| 2778 |
+
"10": 17,
|
| 2779 |
+
"J": 22,
|
| 2780 |
+
"Q": 20,
|
| 2781 |
+
"K": 13
|
| 2782 |
+
},
|
| 2783 |
+
"hi_lo_running_count": -4,
|
| 2784 |
+
"true_count": -0.92
|
| 2785 |
+
},
|
| 2786 |
+
"dealer_up": "J♥",
|
| 2787 |
+
"dealer_hole": "6♦",
|
| 2788 |
+
"dealer_revealed": true,
|
| 2789 |
+
"dealer": {
|
| 2790 |
+
"cards": [
|
| 2791 |
+
"J♥",
|
| 2792 |
+
"6♦",
|
| 2793 |
+
"2♠"
|
| 2794 |
+
],
|
| 2795 |
+
"total": 18,
|
| 2796 |
+
"soft": false,
|
| 2797 |
+
"blackjack": false,
|
| 2798 |
+
"bust": false
|
| 2799 |
+
},
|
| 2800 |
+
"events": [
|
| 2801 |
+
"莊家明牌為 A 或 10 點,進行暗牌確認",
|
| 2802 |
+
"確認結果:莊家沒有 Blackjack",
|
| 2803 |
+
"莊家補牌 2♠ → 18 點",
|
| 2804 |
+
"莊家最終 18 點"
|
| 2805 |
+
],
|
| 2806 |
+
"players": [
|
| 2807 |
+
{
|
| 2808 |
+
"cards": [
|
| 2809 |
+
"K♦",
|
| 2810 |
+
"K♠"
|
| 2811 |
+
],
|
| 2812 |
+
"bet": 10,
|
| 2813 |
+
"doubled": false,
|
| 2814 |
+
"total": 20,
|
| 2815 |
+
"soft": false,
|
| 2816 |
+
"bust": false,
|
| 2817 |
+
"blackjack": false,
|
| 2818 |
+
"steps": [
|
| 2819 |
+
{
|
| 2820 |
+
"type": "action",
|
| 2821 |
+
"cards": [
|
| 2822 |
+
"K♦",
|
| 2823 |
+
"K♠"
|
| 2824 |
+
],
|
| 2825 |
+
"total": 20,
|
| 2826 |
+
"soft": false,
|
| 2827 |
+
"dealer_up": "J♥",
|
| 2828 |
+
"allowed": [
|
| 2829 |
+
"hit",
|
| 2830 |
+
"stand"
|
| 2831 |
+
],
|
| 2832 |
+
"decision": {
|
| 2833 |
+
"type": "choice",
|
| 2834 |
+
"choice": "stand",
|
| 2835 |
+
"probabilities": {
|
| 2836 |
+
"hit": 0.20113812988182753,
|
| 2837 |
+
"stand": 0.7988618701181724
|
| 2838 |
+
},
|
| 2839 |
+
"confidence": 0.5977237402363449
|
| 2840 |
+
},
|
| 2841 |
+
"risk": {
|
| 2842 |
+
"type": "score",
|
| 2843 |
+
"score": 0.9617831874646433,
|
| 2844 |
+
"legend": {
|
| 2845 |
+
"0": "非常有利",
|
| 2846 |
+
"1": "稍微有利",
|
| 2847 |
+
"2": "有點不利",
|
| 2848 |
+
"3": "非常不利"
|
| 2849 |
+
},
|
| 2850 |
+
"probabilities": {
|
| 2851 |
+
"0": 0.37266878093983297,
|
| 2852 |
+
"1": 0.3419774875432344,
|
| 2853 |
+
"2": 0.2362554946293891,
|
| 2854 |
+
"3": 0.04909823688754356
|
| 2855 |
+
},
|
| 2856 |
+
"confidence": 0.03821681253535669
|
| 2857 |
+
},
|
| 2858 |
+
"latency_ms": 2807,
|
| 2859 |
+
"drawn": null,
|
| 2860 |
+
"total_after": 20
|
| 2861 |
+
}
|
| 2862 |
+
],
|
| 2863 |
+
"player_id": "a",
|
| 2864 |
+
"result": "win",
|
| 2865 |
+
"net": 10,
|
| 2866 |
+
"bet_decision": {
|
| 2867 |
+
"decision": {
|
| 2868 |
+
"type": "choice",
|
| 2869 |
+
"choice": "min",
|
| 2870 |
+
"probabilities": {
|
| 2871 |
+
"min": 0.705457858991765,
|
| 2872 |
+
"standard": 0.19844058997151406,
|
| 2873 |
+
"max": 0.09610155103672088
|
| 2874 |
+
},
|
| 2875 |
+
"confidence": 0.5581867884876476
|
| 2876 |
+
},
|
| 2877 |
+
"bet": 10,
|
| 2878 |
+
"confidence": {
|
| 2879 |
+
"type": "score",
|
| 2880 |
+
"score": 1.1786459226702077,
|
| 2881 |
+
"legend": {
|
| 2882 |
+
"0": "沒信心",
|
| 2883 |
+
"1": "普通",
|
| 2884 |
+
"2": "有信心",
|
| 2885 |
+
"3": "非常有信心"
|
| 2886 |
+
},
|
| 2887 |
+
"probabilities": {
|
| 2888 |
+
"0": 0.2098777886585959,
|
| 2889 |
+
"1": 0.4725124514370032,
|
| 2890 |
+
"2": 0.24669580847999806,
|
| 2891 |
+
"3": 0.07091395142440278
|
| 2892 |
+
},
|
| 2893 |
+
"confidence": 0.40159850001260045
|
| 2894 |
+
},
|
| 2895 |
+
"latency_ms": 2281
|
| 2896 |
+
},
|
| 2897 |
+
"bankroll_after": 950,
|
| 2898 |
+
"id": "a",
|
| 2899 |
+
"name": "阿保",
|
| 2900 |
+
"style": "保守算牌派"
|
| 2901 |
+
},
|
| 2902 |
+
{
|
| 2903 |
+
"cards": [
|
| 2904 |
+
"2♦",
|
| 2905 |
+
"3♥",
|
| 2906 |
+
"2♥",
|
| 2907 |
+
"A♠"
|
| 2908 |
+
],
|
| 2909 |
+
"bet": 50,
|
| 2910 |
+
"doubled": false,
|
| 2911 |
+
"total": 18,
|
| 2912 |
+
"soft": true,
|
| 2913 |
+
"bust": false,
|
| 2914 |
+
"blackjack": false,
|
| 2915 |
+
"steps": [
|
| 2916 |
+
{
|
| 2917 |
+
"type": "action",
|
| 2918 |
+
"cards": [
|
| 2919 |
+
"2♦",
|
| 2920 |
+
"3♥"
|
| 2921 |
+
],
|
| 2922 |
+
"total": 5,
|
| 2923 |
+
"soft": false,
|
| 2924 |
+
"dealer_up": "J♥",
|
| 2925 |
+
"allowed": [
|
| 2926 |
+
"hit",
|
| 2927 |
+
"stand"
|
| 2928 |
+
],
|
| 2929 |
+
"decision": {
|
| 2930 |
+
"type": "choice",
|
| 2931 |
+
"choice": "hit",
|
| 2932 |
+
"probabilities": {
|
| 2933 |
+
"hit": 0.8339648493225332,
|
| 2934 |
+
"stand": 0.1660351506774667
|
| 2935 |
+
},
|
| 2936 |
+
"confidence": 0.6679296986450665
|
| 2937 |
+
},
|
| 2938 |
+
"risk": {
|
| 2939 |
+
"type": "score",
|
| 2940 |
+
"score": 1.9632358033511101,
|
| 2941 |
+
"legend": {
|
| 2942 |
+
"0": "非常有利",
|
| 2943 |
+
"1": "稍微有利",
|
| 2944 |
+
"2": "有點不利",
|
| 2945 |
+
"3": "非常不利"
|
| 2946 |
+
},
|
| 2947 |
+
"probabilities": {
|
| 2948 |
+
"0": 0.08778014756208807,
|
| 2949 |
+
"1": 0.17939112699816237,
|
| 2950 |
+
"2": 0.414641499966301,
|
| 2951 |
+
"3": 0.3181872254734486
|
| 2952 |
+
},
|
| 2953 |
+
"confidence": 0.3268613524042129
|
| 2954 |
+
},
|
| 2955 |
+
"latency_ms": 3984,
|
| 2956 |
+
"drawn": "2♥",
|
| 2957 |
+
"total_after": 7
|
| 2958 |
+
},
|
| 2959 |
+
{
|
| 2960 |
+
"type": "action",
|
| 2961 |
+
"cards": [
|
| 2962 |
+
"2♦",
|
| 2963 |
+
"3♥",
|
| 2964 |
+
"2♥"
|
| 2965 |
+
],
|
| 2966 |
+
"total": 7,
|
| 2967 |
+
"soft": false,
|
| 2968 |
+
"dealer_up": "J♥",
|
| 2969 |
+
"allowed": [
|
| 2970 |
+
"hit",
|
| 2971 |
+
"stand"
|
| 2972 |
+
],
|
| 2973 |
+
"decision": {
|
| 2974 |
+
"type": "choice",
|
| 2975 |
+
"choice": "hit",
|
| 2976 |
+
"probabilities": {
|
| 2977 |
+
"hit": 0.8254946752313272,
|
| 2978 |
+
"stand": 0.17450532476867275
|
| 2979 |
+
},
|
| 2980 |
+
"confidence": 0.6509893504626545
|
| 2981 |
+
},
|
| 2982 |
+
"risk": {
|
| 2983 |
+
"type": "score",
|
| 2984 |
+
"score": 1.9199601944797384,
|
| 2985 |
+
"legend": {
|
| 2986 |
+
"0": "非常有利",
|
| 2987 |
+
"1": "稍微有利",
|
| 2988 |
+
"2": "有點不利",
|
| 2989 |
+
"3": "非常不利"
|
| 2990 |
+
},
|
| 2991 |
+
"probabilities": {
|
| 2992 |
+
"0": 0.08961070661335929,
|
| 2993 |
+
"1": 0.1944316247075459,
|
| 2994 |
+
"2": 0.4223444362650919,
|
| 2995 |
+
"3": 0.2936132324140029
|
| 2996 |
+
},
|
| 2997 |
+
"confidence": 0.33273372965173253
|
| 2998 |
+
},
|
| 2999 |
+
"latency_ms": 3647,
|
| 3000 |
+
"drawn": "A♠",
|
| 3001 |
+
"total_after": 18
|
| 3002 |
+
},
|
| 3003 |
+
{
|
| 3004 |
+
"type": "action",
|
| 3005 |
+
"cards": [
|
| 3006 |
+
"2♦",
|
| 3007 |
+
"3♥",
|
| 3008 |
+
"2♥",
|
| 3009 |
+
"A♠"
|
| 3010 |
+
],
|
| 3011 |
+
"total": 18,
|
| 3012 |
+
"soft": true,
|
| 3013 |
+
"dealer_up": "J♥",
|
| 3014 |
+
"allowed": [
|
| 3015 |
+
"hit",
|
| 3016 |
+
"stand"
|
| 3017 |
+
],
|
| 3018 |
+
"decision": {
|
| 3019 |
+
"type": "choice",
|
| 3020 |
+
"choice": "stand",
|
| 3021 |
+
"probabilities": {
|
| 3022 |
+
"hit": 0.4516507728170757,
|
| 3023 |
+
"stand": 0.5483492271829243
|
| 3024 |
+
},
|
| 3025 |
+
"confidence": 0.09669845436584867
|
| 3026 |
+
},
|
| 3027 |
+
"risk": {
|
| 3028 |
+
"type": "score",
|
| 3029 |
+
"score": 1.3633877251471496,
|
| 3030 |
+
"legend": {
|
| 3031 |
+
"0": "非常有利",
|
| 3032 |
+
"1": "稍微有利",
|
| 3033 |
+
"2": "有點不利",
|
| 3034 |
+
"3": "非常不利"
|
| 3035 |
+
},
|
| 3036 |
+
"probabilities": {
|
| 3037 |
+
"0": 0.1898920049001741,
|
| 3038 |
+
"1": 0.35446905502554144,
|
| 3039 |
+
"2": 0.3579981501012455,
|
| 3040 |
+
"3": 0.09764078997303899
|
| 3041 |
+
},
|
| 3042 |
+
"confidence": 0.16810614520107148
|
| 3043 |
+
},
|
| 3044 |
+
"latency_ms": 3469,
|
| 3045 |
+
"drawn": null,
|
| 3046 |
+
"total_after": 18
|
| 3047 |
+
}
|
| 3048 |
+
],
|
| 3049 |
+
"player_id": "b",
|
| 3050 |
+
"result": "push",
|
| 3051 |
+
"net": 0,
|
| 3052 |
+
"bet_decision": {
|
| 3053 |
+
"decision": {
|
| 3054 |
+
"type": "choice",
|
| 3055 |
+
"choice": "max",
|
| 3056 |
+
"probabilities": {
|
| 3057 |
+
"min": 0.17659368546509877,
|
| 3058 |
+
"standard": 0.38463171221217046,
|
| 3059 |
+
"max": 0.43877460232273074
|
| 3060 |
+
},
|
| 3061 |
+
"confidence": 0.15816190348409612
|
| 3062 |
+
},
|
| 3063 |
+
"bet": 50,
|
| 3064 |
+
"confidence": {
|
| 3065 |
+
"type": "score",
|
| 3066 |
+
"score": 1.6844293372148722,
|
| 3067 |
+
"legend": {
|
| 3068 |
+
"0": "沒信心",
|
| 3069 |
+
"1": "普通",
|
| 3070 |
+
"2": "有信心",
|
| 3071 |
+
"3": "非常有信心"
|
| 3072 |
+
},
|
| 3073 |
+
"probabilities": {
|
| 3074 |
+
"0": 0.12644015955206472,
|
| 3075 |
+
"1": 0.27508981392517495,
|
| 3076 |
+
"2": 0.3860705562785838,
|
| 3077 |
+
"3": 0.21239947024417657
|
| 3078 |
+
},
|
| 3079 |
+
"confidence": 0.259630396726519
|
| 3080 |
+
},
|
| 3081 |
+
"latency_ms": 2718
|
| 3082 |
+
},
|
| 3083 |
+
"bankroll_after": 950,
|
| 3084 |
+
"id": "b",
|
| 3085 |
+
"name": "阿衝",
|
| 3086 |
+
"style": "激進壓迫派"
|
| 3087 |
+
}
|
| 3088 |
+
],
|
| 3089 |
+
"shoe_after": {
|
| 3090 |
+
"decks": 6,
|
| 3091 |
+
"remaining_cards": 224,
|
| 3092 |
+
"penetration_pct": 28,
|
| 3093 |
+
"cards_left_by_rank": {
|
| 3094 |
+
"A": 12,
|
| 3095 |
+
"2": 15,
|
| 3096 |
+
"3": 13,
|
| 3097 |
+
"4": 22,
|
| 3098 |
+
"5": 18,
|
| 3099 |
+
"6": 19,
|
| 3100 |
+
"7": 18,
|
| 3101 |
+
"8": 18,
|
| 3102 |
+
"9": 17,
|
| 3103 |
+
"10": 17,
|
| 3104 |
+
"J": 22,
|
| 3105 |
+
"Q": 20,
|
| 3106 |
+
"K": 13
|
| 3107 |
+
},
|
| 3108 |
+
"hi_lo_running_count": -3,
|
| 3109 |
+
"true_count": -0.7
|
| 3110 |
+
}
|
| 3111 |
+
},
|
| 3112 |
+
{
|
| 3113 |
+
"n": 12,
|
| 3114 |
+
"shoe_before": {
|
| 3115 |
+
"decks": 6,
|
| 3116 |
+
"remaining_cards": 218,
|
| 3117 |
+
"penetration_pct": 30,
|
| 3118 |
+
"cards_left_by_rank": {
|
| 3119 |
+
"A": 12,
|
| 3120 |
+
"2": 15,
|
| 3121 |
+
"3": 13,
|
| 3122 |
+
"4": 22,
|
| 3123 |
+
"5": 18,
|
| 3124 |
+
"6": 18,
|
| 3125 |
+
"7": 16,
|
| 3126 |
+
"8": 17,
|
| 3127 |
+
"9": 17,
|
| 3128 |
+
"10": 17,
|
| 3129 |
+
"J": 22,
|
| 3130 |
+
"Q": 19,
|
| 3131 |
+
"K": 12
|
| 3132 |
+
},
|
| 3133 |
+
"hi_lo_running_count": -4,
|
| 3134 |
+
"true_count": -0.95
|
| 3135 |
+
},
|
| 3136 |
+
"dealer_up": "K♦",
|
| 3137 |
+
"dealer_hole": "8♦",
|
| 3138 |
+
"dealer_revealed": true,
|
| 3139 |
+
"dealer": {
|
| 3140 |
+
"cards": [
|
| 3141 |
+
"K♦",
|
| 3142 |
+
"8♦"
|
| 3143 |
+
],
|
| 3144 |
+
"total": 18,
|
| 3145 |
+
"soft": false,
|
| 3146 |
+
"blackjack": false,
|
| 3147 |
+
"bust": false
|
| 3148 |
+
},
|
| 3149 |
+
"events": [
|
| 3150 |
+
"莊家明牌為 A 或 10 點,進行暗牌確認",
|
| 3151 |
+
"確認結果:莊家沒有 Blackjack",
|
| 3152 |
+
"莊家最終 18 點"
|
| 3153 |
+
],
|
| 3154 |
+
"players": [
|
| 3155 |
+
{
|
| 3156 |
+
"cards": [
|
| 3157 |
+
"Q♦",
|
| 3158 |
+
"6♣"
|
| 3159 |
+
],
|
| 3160 |
+
"bet": 10,
|
| 3161 |
+
"doubled": false,
|
| 3162 |
+
"total": 16,
|
| 3163 |
+
"soft": false,
|
| 3164 |
+
"bust": false,
|
| 3165 |
+
"blackjack": false,
|
| 3166 |
+
"steps": [
|
| 3167 |
+
{
|
| 3168 |
+
"type": "action",
|
| 3169 |
+
"cards": [
|
| 3170 |
+
"Q♦",
|
| 3171 |
+
"6♣"
|
| 3172 |
+
],
|
| 3173 |
+
"total": 16,
|
| 3174 |
+
"soft": false,
|
| 3175 |
+
"dealer_up": "K♦",
|
| 3176 |
+
"allowed": [
|
| 3177 |
+
"hit",
|
| 3178 |
+
"stand"
|
| 3179 |
+
],
|
| 3180 |
+
"decision": {
|
| 3181 |
+
"type": "choice",
|
| 3182 |
+
"choice": "stand",
|
| 3183 |
+
"probabilities": {
|
| 3184 |
+
"hit": 0.39016492109703543,
|
| 3185 |
+
"stand": 0.6098350789029646
|
| 3186 |
+
},
|
| 3187 |
+
"confidence": 0.21967015780592924
|
| 3188 |
+
},
|
| 3189 |
+
"risk": {
|
| 3190 |
+
"type": "score",
|
| 3191 |
+
"score": 1.7370799154639605,
|
| 3192 |
+
"legend": {
|
| 3193 |
+
"0": "非常有利",
|
| 3194 |
+
"1": "稍微有利",
|
| 3195 |
+
"2": "有點不利",
|
| 3196 |
+
"3": "非常不利"
|
| 3197 |
+
},
|
| 3198 |
+
"probabilities": {
|
| 3199 |
+
"0": 0.09344393533096118,
|
| 3200 |
+
"1": 0.2685085201031415,
|
| 3201 |
+
"2": 0.4455712383368731,
|
| 3202 |
+
"3": 0.19247630622902423
|
| 3203 |
+
},
|
| 3204 |
+
"confidence": 0.3521273030059119
|
| 3205 |
+
},
|
| 3206 |
+
"latency_ms": 2460,
|
| 3207 |
+
"drawn": null,
|
| 3208 |
+
"total_after": 16
|
| 3209 |
+
}
|
| 3210 |
+
],
|
| 3211 |
+
"player_id": "a",
|
| 3212 |
+
"result": "lose",
|
| 3213 |
+
"net": -10,
|
| 3214 |
+
"bet_decision": {
|
| 3215 |
+
"decision": {
|
| 3216 |
+
"type": "choice",
|
| 3217 |
+
"choice": "min",
|
| 3218 |
+
"probabilities": {
|
| 3219 |
+
"min": 0.7338713056072282,
|
| 3220 |
+
"standard": 0.171097765938789,
|
| 3221 |
+
"max": 0.09503092845398282
|
| 3222 |
+
},
|
| 3223 |
+
"confidence": 0.6008069584108423
|
| 3224 |
+
},
|
| 3225 |
+
"bet": 10,
|
| 3226 |
+
"confidence": {
|
| 3227 |
+
"type": "score",
|
| 3228 |
+
"score": 1.2594716391475735,
|
| 3229 |
+
"legend": {
|
| 3230 |
+
"0": "沒信心",
|
| 3231 |
+
"1": "普通",
|
| 3232 |
+
"2": "有信心",
|
| 3233 |
+
"3": "非常有信心"
|
| 3234 |
+
},
|
| 3235 |
+
"probabilities": {
|
| 3236 |
+
"0": 0.16968632812925138,
|
| 3237 |
+
"1": 0.4794955805426685,
|
| 3238 |
+
"2": 0.2724782153793357,
|
| 3239 |
+
"3": 0.07833987594874454
|
| 3240 |
+
},
|
| 3241 |
+
"confidence": 0.4011557045939238
|
| 3242 |
+
},
|
| 3243 |
+
"latency_ms": 1995
|
| 3244 |
+
},
|
| 3245 |
+
"bankroll_after": 940,
|
| 3246 |
+
"id": "a",
|
| 3247 |
+
"name": "阿保",
|
| 3248 |
+
"style": "保守算牌派"
|
| 3249 |
+
},
|
| 3250 |
+
{
|
| 3251 |
+
"cards": [
|
| 3252 |
+
"7♥",
|
| 3253 |
+
"7♦",
|
| 3254 |
+
"3♥"
|
| 3255 |
+
],
|
| 3256 |
+
"bet": 50,
|
| 3257 |
+
"doubled": false,
|
| 3258 |
+
"total": 17,
|
| 3259 |
+
"soft": false,
|
| 3260 |
+
"bust": false,
|
| 3261 |
+
"blackjack": false,
|
| 3262 |
+
"steps": [
|
| 3263 |
+
{
|
| 3264 |
+
"type": "action",
|
| 3265 |
+
"cards": [
|
| 3266 |
+
"7♥",
|
| 3267 |
+
"7♦"
|
| 3268 |
+
],
|
| 3269 |
+
"total": 14,
|
| 3270 |
+
"soft": false,
|
| 3271 |
+
"dealer_up": "K♦",
|
| 3272 |
+
"allowed": [
|
| 3273 |
+
"hit",
|
| 3274 |
+
"stand"
|
| 3275 |
+
],
|
| 3276 |
+
"decision": {
|
| 3277 |
+
"type": "choice",
|
| 3278 |
+
"choice": "hit",
|
| 3279 |
+
"probabilities": {
|
| 3280 |
+
"hit": 0.608349823613437,
|
| 3281 |
+
"stand": 0.3916501763865629
|
| 3282 |
+
},
|
| 3283 |
+
"confidence": 0.21669964722687407
|
| 3284 |
+
},
|
| 3285 |
+
"risk": {
|
| 3286 |
+
"type": "score",
|
| 3287 |
+
"score": 1.4682163416498657,
|
| 3288 |
+
"legend": {
|
| 3289 |
+
"0": "非常有利",
|
| 3290 |
+
"1": "稍微有利",
|
| 3291 |
+
"2": "有點不利",
|
| 3292 |
+
"3": "非常不利"
|
| 3293 |
+
},
|
| 3294 |
+
"probabilities": {
|
| 3295 |
+
"0": 0.17040055353150113,
|
| 3296 |
+
"1": 0.3169814206595017,
|
| 3297 |
+
"2": 0.38661915643662725,
|
| 3298 |
+
"3": 0.12599886937236981
|
| 3299 |
+
},
|
| 3300 |
+
"confidence": 0.21621860290512618
|
| 3301 |
+
},
|
| 3302 |
+
"latency_ms": 3602,
|
| 3303 |
+
"drawn": "3♥",
|
| 3304 |
+
"total_after": 17
|
| 3305 |
+
},
|
| 3306 |
+
{
|
| 3307 |
+
"type": "action",
|
| 3308 |
+
"cards": [
|
| 3309 |
+
"7♥",
|
| 3310 |
+
"7♦",
|
| 3311 |
+
"3♥"
|
| 3312 |
+
],
|
| 3313 |
+
"total": 17,
|
| 3314 |
+
"soft": false,
|
| 3315 |
+
"dealer_up": "K♦",
|
| 3316 |
+
"allowed": [
|
| 3317 |
+
"hit",
|
| 3318 |
+
"stand"
|
| 3319 |
+
],
|
| 3320 |
+
"decision": {
|
| 3321 |
+
"type": "choice",
|
| 3322 |
+
"choice": "stand",
|
| 3323 |
+
"probabilities": {
|
| 3324 |
+
"hit": 0.4373052794431983,
|
| 3325 |
+
"stand": 0.5626947205568017
|
| 3326 |
+
},
|
| 3327 |
+
"confidence": 0.1253894411136034
|
| 3328 |
+
},
|
| 3329 |
+
"risk": {
|
| 3330 |
+
"type": "score",
|
| 3331 |
+
"score": 1.27082220318929,
|
| 3332 |
+
"legend": {
|
| 3333 |
+
"0": "非常有利",
|
| 3334 |
+
"1": "稍微有利",
|
| 3335 |
+
"2": "有點不利",
|
| 3336 |
+
"3": "非常不利"
|
| 3337 |
+
},
|
| 3338 |
+
"probabilities": {
|
| 3339 |
+
"0": 0.21799232108026073,
|
| 3340 |
+
"1": 0.36294600367568364,
|
| 3341 |
+
"2": 0.34930882621856046,
|
| 3342 |
+
"3": 0.06975284902549515
|
| 3343 |
+
},
|
| 3344 |
+
"confidence": 0.29319315465018847
|
| 3345 |
+
},
|
| 3346 |
+
"latency_ms": 3726,
|
| 3347 |
+
"drawn": null,
|
| 3348 |
+
"total_after": 17
|
| 3349 |
+
}
|
| 3350 |
+
],
|
| 3351 |
+
"player_id": "b",
|
| 3352 |
+
"result": "lose",
|
| 3353 |
+
"net": -50,
|
| 3354 |
+
"bet_decision": {
|
| 3355 |
+
"decision": {
|
| 3356 |
+
"type": "choice",
|
| 3357 |
+
"choice": "max",
|
| 3358 |
+
"probabilities": {
|
| 3359 |
+
"min": 0.18420429014082615,
|
| 3360 |
+
"standard": 0.39700684030663047,
|
| 3361 |
+
"max": 0.4187888695525434
|
| 3362 |
+
},
|
| 3363 |
+
"confidence": 0.1281833043288151
|
| 3364 |
+
},
|
| 3365 |
+
"bet": 50,
|
| 3366 |
+
"confidence": {
|
| 3367 |
+
"type": "score",
|
| 3368 |
+
"score": 1.6458985137494473,
|
| 3369 |
+
"legend": {
|
| 3370 |
+
"0": "沒信心",
|
| 3371 |
+
"1": "普通",
|
| 3372 |
+
"2": "有信心",
|
| 3373 |
+
"3": "非常有信心"
|
| 3374 |
+
},
|
| 3375 |
+
"probabilities": {
|
| 3376 |
+
"0": 0.1260372937960504,
|
| 3377 |
+
"1": 0.2951391749084489,
|
| 3378 |
+
"2": 0.3857112550455043,
|
| 3379 |
+
"3": 0.19311227624999663
|
| 3380 |
+
},
|
| 3381 |
+
"confidence": 0.25967396124945374
|
| 3382 |
+
},
|
| 3383 |
+
"latency_ms": 3039
|
| 3384 |
+
},
|
| 3385 |
+
"bankroll_after": 900,
|
| 3386 |
+
"id": "b",
|
| 3387 |
+
"name": "阿衝",
|
| 3388 |
+
"style": "激進壓迫派"
|
| 3389 |
+
}
|
| 3390 |
+
],
|
| 3391 |
+
"shoe_after": {
|
| 3392 |
+
"decks": 6,
|
| 3393 |
+
"remaining_cards": 217,
|
| 3394 |
+
"penetration_pct": 30,
|
| 3395 |
+
"cards_left_by_rank": {
|
| 3396 |
+
"A": 12,
|
| 3397 |
+
"2": 15,
|
| 3398 |
+
"3": 12,
|
| 3399 |
+
"4": 22,
|
| 3400 |
+
"5": 18,
|
| 3401 |
+
"6": 18,
|
| 3402 |
+
"7": 16,
|
| 3403 |
+
"8": 17,
|
| 3404 |
+
"9": 17,
|
| 3405 |
+
"10": 17,
|
| 3406 |
+
"J": 22,
|
| 3407 |
+
"Q": 19,
|
| 3408 |
+
"K": 12
|
| 3409 |
+
},
|
| 3410 |
+
"hi_lo_running_count": -3,
|
| 3411 |
+
"true_count": -0.72
|
| 3412 |
+
}
|
| 3413 |
+
}
|
| 3414 |
+
]
|
| 3415 |
+
}
|
index.html
CHANGED
|
@@ -1,19 +1,674 @@
|
|
| 1 |
<!doctype html>
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
</html>
|
|
|
|
| 1 |
<!doctype html>
|
| 2 |
+
<html lang="zh-Hant">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 6 |
+
<title>d1 × d1 21 點對戰實錄</title>
|
| 7 |
+
<meta name="description" content="兩個 LiquidAI d1-3B 決策模型,透過 llama.cpp 在同一個牌桌上對打">
|
| 8 |
+
<style>
|
| 9 |
+
:root{
|
| 10 |
+
--bg:#07120e; --bg2:#0b1c16; --panel:#0e241c; --panel2:#123026;
|
| 11 |
+
--line:rgba(255,255,255,.09); --text:#e8f3ed; --muted:#8fae9f;
|
| 12 |
+
--gold:#f6c453; --green:#39d98a; --red:#ff6b6b; --blue:#5cc8ff; --amber:#ffb020;
|
| 13 |
+
--hit:#ff6b6b; --stand:#5cc8ff; --double:#f6c453;
|
| 14 |
+
--shadow:0 18px 50px rgba(0,0,0,.45);
|
| 15 |
+
}
|
| 16 |
+
*{box-sizing:border-box}
|
| 17 |
+
html,body{margin:0;padding:0}
|
| 18 |
+
body{
|
| 19 |
+
background:
|
| 20 |
+
radial-gradient(1100px 700px at 12% -10%, rgba(57,217,138,.13), transparent 60%),
|
| 21 |
+
radial-gradient(900px 600px at 92% 0%, rgba(246,196,83,.10), transparent 55%),
|
| 22 |
+
var(--bg);
|
| 23 |
+
color:var(--text);
|
| 24 |
+
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans TC","PingFang TC","Microsoft JhengHei",sans-serif;
|
| 25 |
+
line-height:1.6; min-height:100vh;
|
| 26 |
+
}
|
| 27 |
+
a{color:var(--gold)}
|
| 28 |
+
button{font:inherit;color:inherit;cursor:pointer}
|
| 29 |
+
.topbar{
|
| 30 |
+
position:sticky;top:0;z-index:50;backdrop-filter:blur(14px);
|
| 31 |
+
background:rgba(7,18,14,.8);border-bottom:1px solid var(--line);
|
| 32 |
+
}
|
| 33 |
+
.topbar-inner{max-width:1180px;margin:0 auto;padding:12px 20px;display:flex;gap:18px;align-items:center;flex-wrap:wrap}
|
| 34 |
+
.brand{display:flex;gap:10px;align-items:center;font-weight:800;letter-spacing:.02em}
|
| 35 |
+
.brand .logo{
|
| 36 |
+
width:30px;height:30px;border-radius:9px;display:grid;place-items:center;
|
| 37 |
+
background:linear-gradient(145deg,#f6c453,#b8860b);color:#1b1200;font-weight:900;
|
| 38 |
+
}
|
| 39 |
+
nav{display:flex;gap:4px;margin-left:auto;flex-wrap:wrap}
|
| 40 |
+
nav button{
|
| 41 |
+
border:1px solid transparent;background:transparent;padding:8px 14px;border-radius:999px;
|
| 42 |
+
color:var(--muted);font-weight:600;
|
| 43 |
+
}
|
| 44 |
+
nav button:hover{color:var(--text);background:rgba(255,255,255,.05)}
|
| 45 |
+
nav button.active{color:#1b1200;background:var(--gold);border-color:var(--gold)}
|
| 46 |
+
main{max-width:1180px;margin:0 auto;padding:28px 20px 60px}
|
| 47 |
+
.view{display:none}
|
| 48 |
+
.view.active{display:block;animation:fade .25s ease}
|
| 49 |
+
@keyframes fade{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
|
| 50 |
+
.hero{padding:18px 0 6px}
|
| 51 |
+
.hero h1{font-size:clamp(30px,4vw,50px);line-height:1.15;margin:0 0 10px;letter-spacing:-.01em}
|
| 52 |
+
.hero h1 em{font-style:normal;color:var(--gold)}
|
| 53 |
+
.hero p{color:var(--muted);margin:0;max-width:760px;font-size:16px}
|
| 54 |
+
.chips{display:flex;gap:8px;flex-wrap:wrap;margin-top:18px}
|
| 55 |
+
.chip{
|
| 56 |
+
font-size:12.5px;color:#cfe4d9;background:rgba(255,255,255,.045);
|
| 57 |
+
border:1px solid var(--line);border-radius:999px;padding:5px 11px;
|
| 58 |
+
}
|
| 59 |
+
.chip b{color:var(--gold)}
|
| 60 |
+
.scoreboard{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;margin:22px 0 24px}
|
| 61 |
+
.score{
|
| 62 |
+
position:relative;overflow:hidden;border:1px solid var(--line);border-radius:18px;
|
| 63 |
+
padding:18px 18px 16px;background:linear-gradient(160deg,var(--panel2),var(--panel));
|
| 64 |
+
box-shadow:var(--shadow);
|
| 65 |
+
}
|
| 66 |
+
.score:before{content:"";position:absolute;inset:0 auto 0 0;width:4px;background:var(--accent,var(--gold))}
|
| 67 |
+
.score .id{font-size:12px;letter-spacing:.16em;text-transform:uppercase;color:var(--muted)}
|
| 68 |
+
.score .name{font-size:22px;font-weight:800;margin:2px 0 0}
|
| 69 |
+
.score .style{font-size:13px;color:var(--accent,var(--gold))}
|
| 70 |
+
.score .bank{font-size:34px;font-weight:900;letter-spacing:-.02em;margin-top:10px;font-variant-numeric:tabular-nums}
|
| 71 |
+
.score .sub{font-size:13px;color:var(--muted)}
|
| 72 |
+
.big-delta{font-weight:800}
|
| 73 |
+
.pos{color:var(--green)}.neg{color:var(--red)}.neutral{color:var(--muted)}
|
| 74 |
+
.panel{
|
| 75 |
+
border:1px solid var(--line);border-radius:20px;background:linear-gradient(180deg,rgba(255,255,255,.025),rgba(0,0,0,.12));
|
| 76 |
+
box-shadow:var(--shadow);
|
| 77 |
+
}
|
| 78 |
+
.table-panel{padding:16px}
|
| 79 |
+
.round-toolbar{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:14px}
|
| 80 |
+
.icon-btn{
|
| 81 |
+
border:1px solid var(--line);background:rgba(255,255,255,.05);border-radius:10px;
|
| 82 |
+
padding:8px 13px;font-weight:700;color:var(--text);
|
| 83 |
+
}
|
| 84 |
+
.icon-btn:hover{background:rgba(255,255,255,.1)}
|
| 85 |
+
.icon-btn:disabled{opacity:.35;cursor:not-allowed}
|
| 86 |
+
.round-pills{display:flex;gap:6px;overflow-x:auto;padding:2px;max-width:100%;scrollbar-width:thin}
|
| 87 |
+
.pill{
|
| 88 |
+
flex:0 0 auto;min-width:38px;height:34px;border-radius:9px;border:1px solid var(--line);
|
| 89 |
+
background:rgba(255,255,255,.04);font-weight:800;font-size:13px;color:var(--muted);
|
| 90 |
+
display:grid;place-items:center;padding:0 8px;
|
| 91 |
+
}
|
| 92 |
+
.pill:hover{border-color:rgba(246,196,83,.5);color:var(--text)}
|
| 93 |
+
.pill.active{background:var(--gold);border-color:var(--gold);color:#1b1200}
|
| 94 |
+
.pill .dot{width:6px;height:6px;border-radius:50%;display:block;margin:0 auto 2px}
|
| 95 |
+
.pill.win .dot{background:var(--green)}
|
| 96 |
+
.pill.lose .dot{background:var(--red)}
|
| 97 |
+
.pill.mixed .dot{background:var(--amber)}
|
| 98 |
+
.pill.push .dot{background:#93a89d}
|
| 99 |
+
.round-label{margin-left:auto;color:var(--muted);font-size:13px;font-variant-numeric:tabular-nums}
|
| 100 |
+
.round-head{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:10px}
|
| 101 |
+
.round-head h2{margin:0;font-size:20px}
|
| 102 |
+
.round-head .shoe{color:var(--muted);font-size:12.5px}
|
| 103 |
+
.dealer{
|
| 104 |
+
border:1px solid rgba(246,196,83,.25);border-radius:16px;padding:14px 16px;margin-bottom:14px;
|
| 105 |
+
background:radial-gradient(700px 200px at 20% 0%,rgba(246,196,83,.1),transparent 70%),rgba(0,0,0,.18);
|
| 106 |
+
display:flex;gap:16px;align-items:center;flex-wrap:wrap;
|
| 107 |
+
}
|
| 108 |
+
.dealer .label{font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--gold);font-weight:800}
|
| 109 |
+
.dealer .total-badge{font-size:26px;font-weight:900;font-variant-numeric:tabular-nums}
|
| 110 |
+
.dealer .note{color:var(--muted);font-size:13px;flex:1;min-width:180px}
|
| 111 |
+
.players{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
|
| 112 |
+
.player{
|
| 113 |
+
border:1px solid var(--line);border-radius:16px;padding:15px;background:rgba(255,255,255,.025);
|
| 114 |
+
}
|
| 115 |
+
.player.win{border-color:rgba(57,217,138,.45);box-shadow:inset 0 0 0 1px rgba(57,217,138,.12)}
|
| 116 |
+
.player.lose{border-color:rgba(255,107,107,.35)}
|
| 117 |
+
.player.blackjack{border-color:rgba(246,196,83,.6)}
|
| 118 |
+
.p-head{display:flex;justify-content:space-between;gap:10px;align-items:flex-start;flex-wrap:wrap}
|
| 119 |
+
.p-head h3{margin:0;font-size:18px}
|
| 120 |
+
.p-head .style{font-size:12.5px;color:var(--accent,var(--gold))}
|
| 121 |
+
.p-head .bet{
|
| 122 |
+
font-size:12.5px;color:var(--muted);border:1px solid var(--line);border-radius:999px;padding:4px 10px;
|
| 123 |
+
background:rgba(0,0,0,.2);white-space:nowrap;
|
| 124 |
+
}
|
| 125 |
+
.p-head .bet b{color:var(--gold);font-size:15px}
|
| 126 |
+
.hand{display:flex;gap:14px;align-items:center;margin:12px 0 8px;flex-wrap:wrap}
|
| 127 |
+
.cards{display:flex;gap:6px;flex-wrap:wrap}
|
| 128 |
+
.card{
|
| 129 |
+
width:52px;height:72px;border-radius:8px;background:#f7f7f2;color:#141414;
|
| 130 |
+
display:flex;flex-direction:column;align-items:center;justify-content:center;
|
| 131 |
+
font-weight:900;box-shadow:0 6px 14px rgba(0,0,0,.35);border:1px solid rgba(0,0,0,.15);
|
| 132 |
+
line-height:1;user-select:none;
|
| 133 |
+
}
|
| 134 |
+
.card .r{font-size:21px}
|
| 135 |
+
.card .s{font-size:17px;margin-top:2px}
|
| 136 |
+
.card.red{color:#c0182b}
|
| 137 |
+
.card.down{background:repeating-linear-gradient(45deg,#123c6b,#123c6b 5px,#0b2a4d 5px,#0b2a4d 10px);color:transparent;border-color:#0a2340}
|
| 138 |
+
.total{
|
| 139 |
+
font-size:30px;font-weight:900;font-variant-numeric:tabular-nums;display:flex;align-items:baseline;gap:6px;
|
| 140 |
+
}
|
| 141 |
+
.total small{font-size:11px;font-weight:700;color:var(--muted);border:1px solid var(--line);border-radius:5px;padding:1px 5px}
|
| 142 |
+
.result{
|
| 143 |
+
display:inline-flex;align-items:center;gap:6px;font-size:12.5px;font-weight:800;
|
| 144 |
+
padding:3px 10px;border-radius:999px;border:1px solid var(--line);
|
| 145 |
+
}
|
| 146 |
+
.result.win{color:var(--green);border-color:rgba(57,217,138,.4);background:rgba(57,217,138,.1)}
|
| 147 |
+
.result.lose{color:var(--red);border-color:rgba(255,107,107,.4);background:rgba(255,107,107,.08)}
|
| 148 |
+
.result.push{color:#b9c9c0;background:rgba(255,255,255,.05)}
|
| 149 |
+
.result.blackjack{color:var(--gold);border-color:rgba(246,196,83,.5);background:rgba(246,196,83,.1)}
|
| 150 |
+
.bet-line{display:flex;gap:8px;align-items:center;flex-wrap:wrap;font-size:12.5px;color:var(--muted);margin:6px 0 2px}
|
| 151 |
+
.bet-line .tag{border:1px solid var(--line);border-radius:6px;padding:1px 7px;color:#cfe4d9}
|
| 152 |
+
details{margin-top:8px}
|
| 153 |
+
summary{cursor:pointer;color:var(--muted);font-size:12.5px;font-weight:700}
|
| 154 |
+
summary:hover{color:var(--text)}
|
| 155 |
+
details .body{padding:8px 0 0;color:var(--muted);font-size:13px}
|
| 156 |
+
.steps{list-style:none;margin:12px 0 0;padding:0;display:flex;flex-direction:column;gap:8px}
|
| 157 |
+
.step{
|
| 158 |
+
border:1px solid var(--line);border-radius:11px;padding:9px 11px;background:rgba(0,0,0,.2);
|
| 159 |
+
}
|
| 160 |
+
.step.enter{animation:slide .3s ease}
|
| 161 |
+
@keyframes slide{from{opacity:0;transform:translateX(-6px)}to{opacity:1;transform:none}}
|
| 162 |
+
.step-head{display:flex;gap:8px;align-items:center;flex-wrap:wrap;font-size:12.5px}
|
| 163 |
+
.step-no{color:var(--muted);font-weight:800;font-variant-numeric:tabular-nums}
|
| 164 |
+
.mini{font-weight:800;font-variant-numeric:tabular-nums}
|
| 165 |
+
.action{
|
| 166 |
+
font-weight:900;font-size:12px;letter-spacing:.04em;padding:2px 9px;border-radius:6px;color:#0b0f0d;
|
| 167 |
+
}
|
| 168 |
+
.action.hit{background:var(--hit)}.action.stand{background:var(--stand)}.action.double{background:var(--double)}
|
| 169 |
+
.action.unknown{background:#7a8a81}
|
| 170 |
+
.step .meta{margin-left:auto;color:var(--muted);font-size:11.5px;display:flex;gap:8px;flex-wrap:wrap}
|
| 171 |
+
.probs{display:grid;grid-template-columns:64px 1fr 46px;gap:6px;align-items:center;margin-top:7px;font-size:11.5px;color:var(--muted)}
|
| 172 |
+
.bar{height:7px;border-radius:4px;background:rgba(255,255,255,.07);overflow:hidden}
|
| 173 |
+
.bar>i{display:block;height:100%;border-radius:4px;background:var(--c,var(--gold));width:0;transition:width .5s ease}
|
| 174 |
+
.probs .pct{text-align:right;font-variant-numeric:tabular-nums;color:#d6e7de}
|
| 175 |
+
.step-foot{display:flex;gap:10px;flex-wrap:wrap;color:var(--muted);font-size:11.5px;margin-top:7px}
|
| 176 |
+
.step-foot b{color:#d6e7de}
|
| 177 |
+
.net{margin-top:11px;padding-top:10px;border-top:1px solid var(--line);display:flex;justify-content:space-between;align-items:center;font-size:13px}
|
| 178 |
+
.net .amt{font-size:19px;font-weight:900;font-variant-numeric:tabular-nums}
|
| 179 |
+
.events{margin-top:14px;border-top:1px dashed var(--line);padding-top:10px;color:var(--muted);font-size:12.5px}
|
| 180 |
+
.events ul{margin:6px 0 0;padding-left:18px}
|
| 181 |
+
.events li{margin:2px 0}
|
| 182 |
+
.arena-note{margin-top:14px;color:var(--muted);font-size:12px}
|
| 183 |
+
.section-title{font-size:22px;margin:26px 0 12px}
|
| 184 |
+
.stats-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
|
| 185 |
+
.stat-card{border:1px solid var(--line);border-radius:16px;padding:16px;background:rgba(255,255,255,.025)}
|
| 186 |
+
.stat-card h3{margin:0 0 4px;font-size:17px}
|
| 187 |
+
.stat-card .style{font-size:12.5px;color:var(--accent,var(--gold))}
|
| 188 |
+
.kv{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;margin-top:12px}
|
| 189 |
+
.kv .k{font-size:11.5px;color:var(--muted)}
|
| 190 |
+
.kv .v{font-size:19px;font-weight:800;font-variant-numeric:tabular-nums}
|
| 191 |
+
.dist{margin-top:12px}
|
| 192 |
+
.dist .row{display:grid;grid-template-columns:66px 1fr 44px;gap:7px;align-items:center;font-size:12px;margin:5px 0;color:var(--muted)}
|
| 193 |
+
.mini-title{font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin-top:14px}
|
| 194 |
+
.chart-wrap{margin-top:14px;border:1px solid var(--line);border-radius:16px;padding:14px;background:rgba(0,0,0,.18)}
|
| 195 |
+
.chart-legend{display:flex;gap:14px;font-size:12px;color:var(--muted);flex-wrap:wrap;margin-bottom:6px}
|
| 196 |
+
.chart-legend i{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:5px}
|
| 197 |
+
.h2h{margin-top:14px;border:1px solid var(--line);border-radius:16px;padding:16px;background:rgba(255,255,255,.025)}
|
| 198 |
+
.h2h .big{font-size:30px;font-weight:900;color:var(--gold)}
|
| 199 |
+
.code-tabs{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px}
|
| 200 |
+
.code-tabs button{border:1px solid var(--line);background:rgba(255,255,255,.04);border-radius:8px;padding:6px 12px;font-size:12.5px;color:var(--muted)}
|
| 201 |
+
.code-tabs button.active{background:var(--gold);color:#1b1200;border-color:var(--gold);font-weight:800}
|
| 202 |
+
.code-bar{display:flex;gap:10px;align-items:center;margin-bottom:8px;color:var(--muted);font-size:12.5px;flex-wrap:wrap}
|
| 203 |
+
pre.code{
|
| 204 |
+
margin:0;border:1px solid var(--line);border-radius:14px;background:#04100c;padding:16px;
|
| 205 |
+
overflow:auto;max-height:70vh;font-size:12.5px;line-height:1.55;
|
| 206 |
+
}
|
| 207 |
+
pre.code code{font-family:"SFMono-Regular",Consolas,"Liberation Mono",Menlo,monospace}
|
| 208 |
+
.data-box{border:1px solid var(--line);border-radius:16px;padding:16px;background:rgba(255,255,255,.025);margin-bottom:14px}
|
| 209 |
+
.data-box h3{margin:0 0 8px;font-size:17px}
|
| 210 |
+
.data-box p{color:var(--muted);margin:6px 0;font-size:14px}
|
| 211 |
+
.btn{
|
| 212 |
+
display:inline-flex;align-items:center;gap:7px;border:1px solid rgba(246,196,83,.45);
|
| 213 |
+
background:rgba(246,196,83,.1);color:var(--gold);padding:7px 13px;border-radius:9px;
|
| 214 |
+
text-decoration:none;font-weight:700;font-size:13px;
|
| 215 |
+
}
|
| 216 |
+
.btn:hover{background:rgba(246,196,83,.18)}
|
| 217 |
+
.btn.ghost{border-color:var(--line);background:rgba(255,255,255,.04);color:var(--text)}
|
| 218 |
+
footer{border-top:1px solid var(--line);margin-top:30px;padding:22px 20px 40px;color:var(--muted);font-size:12.5px;text-align:center}
|
| 219 |
+
.loading{padding:40px;text-align:center;color:var(--muted)}
|
| 220 |
+
.err{border:1px solid rgba(255,107,107,.4);background:rgba(255,107,107,.08);padding:16px;border-radius:12px;color:#ffd4d4}
|
| 221 |
+
@media (max-width:760px){
|
| 222 |
+
.scoreboard,.stats-grid,.players{grid-template-columns:1fr}
|
| 223 |
+
.card{width:44px;height:62px}
|
| 224 |
+
.card .r{font-size:18px}
|
| 225 |
+
.total{font-size:25px}
|
| 226 |
+
.round-label{margin-left:0}
|
| 227 |
+
}
|
| 228 |
+
</style>
|
| 229 |
+
</head>
|
| 230 |
+
<body>
|
| 231 |
+
<header class="topbar">
|
| 232 |
+
<div class="topbar-inner">
|
| 233 |
+
<div class="brand"><span class="logo">21</span><span>d1 × d1 對戰實驗室</span></div>
|
| 234 |
+
<nav>
|
| 235 |
+
<button data-view="battle" class="active">對戰</button>
|
| 236 |
+
<button data-view="stats">統計</button>
|
| 237 |
+
<button data-view="code">整理程式</button>
|
| 238 |
+
<button data-view="data">原始資料</button>
|
| 239 |
+
</nav>
|
| 240 |
+
</div>
|
| 241 |
+
</header>
|
| 242 |
+
|
| 243 |
+
<main>
|
| 244 |
+
<div id="app"><div class="loading">載入對戰資料中…</div></div>
|
| 245 |
+
</main>
|
| 246 |
+
|
| 247 |
+
<footer>
|
| 248 |
+
<div>由 llama.cpp 上的兩個 <b>LiquidAI d1-3B</b> 決策模型自動產生 · 頁面由 build_site.py 靜態編譯</div>
|
| 249 |
+
<div style="margin-top:6px">Hosted on Hugging Face Static Space · <a href="https://huggingface.co/spaces/HelloSun/d1demo">HelloSun/d1demo</a></div>
|
| 250 |
+
</footer>
|
| 251 |
+
|
| 252 |
+
<script>
|
| 253 |
+
window.BATTLE_DATA = {"meta": {"title": "d1 × d1 21 點對戰實錄", "subtitle": "兩個 LiquidAI d1-3B 決策模型,透過 llama.cpp 在同一個牌桌上對打", "created_at": "2026-10-21T11:09:42+02:00", "seed": 20261021, "model": "LiquidAI/d1-3B-GGUF", "quant": "Q8_0", "llama_build": "b11786 (0.7.0-dev)", "rules": {"decks": 6, "dealer": "莊家軟 17 停牌 (S17)", "blackjack_pays": "3:2", "double": "僅限硬 9–11 或軟 13–18 時加倍,加倍後只補一張", "split": "本局不開放分牌", "insurance": "不開放保險", "reshuffle": "剩餘牌數低於 25% 時重洗", "start_bankroll": 1000}, "start_bankroll": 1000, "agents": [{"id": "a", "name": "阿保", "style": "保守算牌派", "description": "以保住本金為第一優先,只有在牌面明顯有利時才加注,遇到風險寧可停牌。", "url": "http://127.0.0.1:8081", "final_bankroll": 940}, {"id": "b", "name": "阿衝", "style": "激進壓迫派", "description": "相信進攻是最好的防守,喜歡加倍與大注,願意為了期望值承擔波動。", "url": "http://127.0.0.1:8082", "final_bankroll": 900}], "rounds_played": 12, "duration_sec": 161.39, "inference": {"a": {"calls": 27, "avg_latency_ms": 2589}, "b": {"calls": 27, "avg_latency_ms": 3387}}}, "rounds": [{"n": 1, "shoe_before": {"decks": 6, "remaining_cards": 306, "penetration_pct": 2, "cards_left_by_rank": {"A": 23, "2": 24, "3": 23, "4": 24, "5": 24, "6": 23, "7": 23, "8": 24, "9": 24, "10": 24, "J": 24, "Q": 23, "K": 23}, "hi_lo_running_count": -1, "true_count": -0.17}, "dealer_up": "A♣", "dealer_hole": "K♣", "dealer_revealed": true, "dealer": {"cards": ["A♣", "K♣"], "total": 21, "soft": true, "blackjack": true, "bust": false}, "events": ["莊家明牌為 A 或 10 點,進行暗牌確認", "莊家直接拿到 Blackjack,本局立即結束"], "players": [{"cards": ["7♥", "3♣"], "bet": 10, "doubled": false, "blackjack": false, "bust": false, "total": 10, "soft": false, "result": "lose", "net": -10, "bet_decision": {"decision": {"type": "choice", "choice": "min", "probabilities": {"min": 0.8015038103456409, "standard": 0.13503222188459674, "max": 0.06346396776976232}, "confidence": 0.7022557155184613}, "bet": 10, "confidence": {"type": "score", "score": 1.2743893137525868, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.177727990768028, "1": 0.48287248232676894, "2": 0.22668174928979082, "3": 0.11271777761541213}, "confidence": 0.3701547047113569}, "latency_ms": 2619}, "bankroll_after": 990, "id": "a", "name": "阿保", "style": "保守算牌派"}, {"cards": ["Q♦", "6♠"], "bet": 50, "doubled": false, "blackjack": false, "bust": false, "total": 16, "soft": false, "result": "lose", "net": -50, "bet_decision": {"decision": {"type": "choice", "choice": "max", "probabilities": {"min": 0.22510969265696443, "standard": 0.3522666589432454, "max": 0.42262364839979016}, "confidence": 0.13393547259968525}, "bet": 50, "confidence": {"type": "score", "score": 1.4739367761682, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.16212488082911708, "1": 0.364637000115999, "2": 0.3104145811124513, "3": 0.16282353794243282}, "confidence": 0.201813462173566}, "latency_ms": 2899}, "bankroll_after": 950, "id": "b", "name": "阿衝", "style": "激進壓迫派"}], "shoe_after": {"decks": 6, "remaining_cards": 306, "penetration_pct": 2, "cards_left_by_rank": {"A": 23, "2": 24, "3": 23, "4": 24, "5": 24, "6": 23, "7": 23, "8": 24, "9": 24, "10": 24, "J": 24, "Q": 23, "K": 23}, "hi_lo_running_count": -1, "true_count": -0.17}}, {"n": 2, "shoe_before": {"decks": 6, "remaining_cards": 300, "penetration_pct": 4, "cards_left_by_rank": {"A": 22, "2": 24, "3": 22, "4": 24, "5": 23, "6": 23, "7": 23, "8": 23, "9": 24, "10": 23, "J": 24, "Q": 23, "K": 22}, "hi_lo_running_count": -2, "true_count": -0.35}, "dealer_up": "8♣", "dealer_hole": "A♠", "dealer_revealed": true, "dealer": {"cards": ["8♣", "A♠"], "total": 19, "soft": true, "blackjack": false, "bust": false}, "events": ["莊家最終 19 點"], "players": [{"cards": ["5♠", "3♥", "9♠"], "bet": 10, "doubled": false, "total": 17, "soft": false, "bust": false, "blackjack": false, "steps": [{"type": "action", "cards": ["5♠", "3♥"], "total": 8, "soft": false, "dealer_up": "8♣", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "hit", "probabilities": {"hit": 0.7903426468089709, "stand": 0.2096573531910292}, "confidence": 0.5806852936179419}, "risk": {"type": "score", "score": 2.1705379533840112, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.04566941566120645, "1": 0.12003841453217244, "2": 0.4523769705680244, "3": 0.38191519923859674}, "confidence": 0.406707554906818}, "latency_ms": 3068, "drawn": "9♠", "total_after": 17}, {"type": "action", "cards": ["5♠", "3♥", "9♠"], "total": 17, "soft": false, "dealer_up": "8♣", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "stand", "probabilities": {"hit": 0.34748348200597706, "stand": 0.6525165179940229}, "confidence": 0.30503303598804576}, "risk": {"type": "score", "score": 1.3057660239341171, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.1680177506175406, "1": 0.4059181822464935, "2": 0.3783443597202743, "3": 0.04771970741569162}, "confidence": 0.3581984748308018}, "latency_ms": 2844, "drawn": null, "total_after": 17}], "player_id": "a", "result": "lose", "net": -10, "bet_decision": {"decision": {"type": "choice", "choice": "min", "probabilities": {"min": 0.7725987262844797, "standard": 0.1413424593652034, "max": 0.08605881435031675}, "confidence": 0.6588980894267195}, "bet": 10, "confidence": {"type": "score", "score": 1.3006956637914562, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.16014576713262269, "1": 0.4790571948548963, "2": 0.2607526451008829, "3": 0.100044392911598}, "confidence": 0.37901280194329834}, "latency_ms": 2449}, "bankroll_after": 980, "id": "a", "name": "阿保", "style": "保守算牌派"}, {"cards": ["K♦", "10♣"], "bet": 50, "doubled": false, "total": 20, "soft": false, "bust": false, "blackjack": false, "steps": [{"type": "action", "cards": ["K♦", "10♣"], "total": 20, "soft": false, "dealer_up": "8♣", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "stand", "probabilities": {"hit": 0.29523031516652504, "stand": 0.704769684833475}, "confidence": 0.40953936966695004}, "risk": {"type": "score", "score": 1.1019380316438792, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.270273972567917, "1": 0.3948283551800062, "2": 0.2975833402923578, "3": 0.03731433195971913}, "confidence": 0.3575140232202869}, "latency_ms": 4289, "drawn": null, "total_after": 20}], "player_id": "b", "result": "win", "net": 50, "bet_decision": {"decision": {"type": "choice", "choice": "max", "probabilities": {"min": 0.19967317203877727, "standard": 0.3811921558998119, "max": 0.4191346720614108}, "confidence": 0.1287020080921162}, "bet": 50, "confidence": {"type": "score", "score": 1.5668315252239973, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.14124839285420554, "1": 0.3269367548074964, "2": 0.3555497865983932, "3": 0.17626506573990477}, "confidence": 0.21430139374418777}, "latency_ms": 2759}, "bankroll_after": 1000, "id": "b", "name": "阿衝", "style": "激進壓迫派"}], "shoe_after": {"decks": 6, "remaining_cards": 299, "penetration_pct": 4, "cards_left_by_rank": {"A": 22, "2": 24, "3": 22, "4": 24, "5": 23, "6": 23, "7": 23, "8": 23, "9": 23, "10": 23, "J": 24, "Q": 23, "K": 22}, "hi_lo_running_count": -2, "true_count": -0.35}}, {"n": 3, "shoe_before": {"decks": 6, "remaining_cards": 293, "penetration_pct": 6, "cards_left_by_rank": {"A": 22, "2": 22, "3": 21, "4": 24, "5": 23, "6": 22, "7": 23, "8": 23, "9": 23, "10": 21, "J": 24, "Q": 23, "K": 22}, "hi_lo_running_count": 0, "true_count": 0.0}, "dealer_up": "3♦", "dealer_hole": "2♠", "dealer_revealed": true, "dealer": {"cards": ["3♦", "2♠", "2♠", "3♦", "A♣"], "total": 21, "soft": true, "blackjack": false, "bust": false}, "events": ["莊家補牌 2♠ → 7 點", "莊家補牌 3♦ → 10 點", "莊家補牌 A♣ → 21 點", "莊家最終 21 點"], "players": [{"cards": ["10♠", "2♦", "8♣"], "bet": 10, "doubled": false, "total": 20, "soft": false, "bust": false, "blackjack": false, "steps": [{"type": "action", "cards": ["10♠", "2♦"], "total": 12, "soft": false, "dealer_up": "3♦", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "hit", "probabilities": {"hit": 0.564536710896258, "stand": 0.43546328910374205}, "confidence": 0.12907342179251602}, "risk": {"type": "score", "score": 1.5462336728175854, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.11070758630941745, "1": 0.3353281508308657, "2": 0.45098726659243077, "3": 0.10297699626728603}, "confidence": 0.34027968028301336}, "latency_ms": 2619, "drawn": "8♣", "total_after": 20}, {"type": "action", "cards": ["10♠", "2♦", "8♣"], "total": 20, "soft": false, "dealer_up": "3♦", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "stand", "probabilities": {"hit": 0.23511610687855905, "stand": 0.764883893121441}, "confidence": 0.529767786242882}, "risk": {"type": "score", "score": 1.1724233566485125, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.2259679727325013, "1": 0.41366566628608514, "2": 0.32234139258181355, "3": 0.038024968399600075}, "confidence": 0.375640697886485}, "latency_ms": 3056, "drawn": null, "total_after": 20}], "player_id": "a", "result": "lose", "net": -10, "bet_decision": {"decision": {"type": "choice", "choice": "min", "probabilities": {"min": 0.7433957153910495, "standard": 0.16651591247876724, "max": 0.09008837213018317}, "confidence": 0.6150935730865743}, "bet": 10, "confidence": {"type": "score", "score": 1.2496433080778582, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.17187017754479506, "1": 0.48519722500579504, "2": 0.26435170927616675, "3": 0.07858088817324316}, "confidence": 0.40661633683255194}, "latency_ms": 2226}, "bankroll_after": 970, "id": "a", "name": "阿保", "style": "保守算牌派"}, {"cards": ["6♦", "10♠", "4♠"], "bet": 50, "doubled": false, "total": 20, "soft": false, "bust": false, "blackjack": false, "steps": [{"type": "action", "cards": ["6♦", "10♠"], "total": 16, "soft": false, "dealer_up": "3♦", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "hit", "probabilities": {"hit": 0.6418322181399073, "stand": 0.3581677818600927}, "confidence": 0.28366443627981464}, "risk": {"type": "score", "score": 1.4828880574132604, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.12154439867797127, "1": 0.3522754018654166, "2": 0.44792794282199283, "3": 0.07825225663461932}, "confidence": 0.3263835441440215}, "latency_ms": 3581, "drawn": "4♠", "total_after": 20}, {"type": "action", "cards": ["6♦", "10♠", "4♠"], "total": 20, "soft": false, "dealer_up": "3♦", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "stand", "probabilities": {"hit": 0.3440449520657691, "stand": 0.6559550479342309}, "confidence": 0.31191009586846175}, "risk": {"type": "score", "score": 1.2733321008675031, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.19239615322374934, "1": 0.3916707506879337, "2": 0.36613793808538125, "3": 0.049795158002935644}, "confidence": 0.3418755926849981}, "latency_ms": 3581, "drawn": null, "total_after": 20}], "player_id": "b", "result": "lose", "net": -50, "bet_decision": {"decision": {"type": "choice", "choice": "max", "probabilities": {"min": 0.17446344549537493, "standard": 0.3824838673364471, "max": 0.44305268716817786}, "confidence": 0.1645790307522668}, "bet": 50, "confidence": {"type": "score", "score": 1.6022477730280071, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.12490168872094283, "1": 0.32284211542202057, "2": 0.3773629299651229, "3": 0.17489326589191365}, "confidence": 0.2524612412441801}, "latency_ms": 2818}, "bankroll_after": 950, "id": "b", "name": "阿衝", "style": "激進壓迫派"}], "shoe_after": {"decks": 6, "remaining_cards": 288, "penetration_pct": 8, "cards_left_by_rank": {"A": 21, "2": 21, "3": 20, "4": 23, "5": 23, "6": 22, "7": 23, "8": 22, "9": 23, "10": 21, "J": 24, "Q": 23, "K": 22}, "hi_lo_running_count": 2, "true_count": 0.36}}, {"n": 4, "shoe_before": {"decks": 6, "remaining_cards": 282, "penetration_pct": 10, "cards_left_by_rank": {"A": 20, "2": 21, "3": 19, "4": 23, "5": 23, "6": 22, "7": 22, "8": 21, "9": 22, "10": 21, "J": 24, "Q": 23, "K": 21}, "hi_lo_running_count": 1, "true_count": 0.18}, "dealer_up": "7♠", "dealer_hole": "K♥", "dealer_revealed": true, "dealer": {"cards": ["7♠", "K♥"], "total": 17, "soft": false, "blackjack": false, "bust": false}, "events": ["莊家最終 17 點"], "players": [{"cards": ["8♥", "A♦"], "bet": 10, "doubled": false, "total": 19, "soft": true, "bust": false, "blackjack": false, "steps": [{"type": "action", "cards": ["8♥", "A♦"], "total": 19, "soft": true, "dealer_up": "7♠", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "stand", "probabilities": {"hit": 0.2797133858793715, "stand": 0.7202866141206284}, "confidence": 0.4405732282412569}, "risk": {"type": "score", "score": 1.0755577590046503, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.2653823049012448, "1": 0.42404250943999217, "2": 0.2802103074116306, "3": 0.030364878247132344}, "confidence": 0.3936776311928599}, "latency_ms": 3126, "drawn": null, "total_after": 19}], "player_id": "a", "result": "win", "net": 10, "bet_decision": {"decision": {"type": "choice", "choice": "min", "probabilities": {"min": 0.7249067710505043, "standard": 0.17984278558883143, "max": 0.09525044336066422}, "confidence": 0.5873601565757564}, "bet": 10, "confidence": {"type": "score", "score": 1.1590048973716087, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.21064627872594902, "1": 0.4865225308156261, "2": 0.23601120481929241, "3": 0.06681998563913258}, "confidence": 0.4197025451764934}, "latency_ms": 2265}, "bankroll_after": 980, "id": "a", "name": "阿保", "style": "保守算牌派"}, {"cards": ["9♠", "3♦", "K♥"], "bet": 50, "doubled": false, "total": 22, "soft": false, "bust": true, "blackjack": false, "steps": [{"type": "action", "cards": ["9♠", "3♦"], "total": 12, "soft": false, "dealer_up": "7♠", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "hit", "probabilities": {"hit": 0.7049549850357781, "stand": 0.295045014964222}, "confidence": 0.4099099700715563}, "risk": {"type": "score", "score": 1.5146466061989001, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.12572631444400376, "1": 0.33533827834479635, "2": 0.43749789377949644, "3": 0.10143751343170365}, "confidence": 0.3117715793354925}, "latency_ms": 3834, "drawn": "K♥", "total_after": 22}], "player_id": "b", "result": "lose", "net": -50, "bet_decision": {"decision": {"type": "choice", "choice": "max", "probabilities": {"min": 0.19672050531851887, "standard": 0.3845980993465475, "max": 0.41868139533493354}, "confidence": 0.1280220930024003}, "bet": 50, "confidence": {"type": "score", "score": 1.5623486708262693, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.13876933530621044, "1": 0.3289561938301259, "2": 0.36343093559484774, "3": 0.1688435352688159}, "confidence": 0.22466160028863735}, "latency_ms": 3123}, "bankroll_after": 900, "id": "b", "name": "阿衝", "style": "激進壓迫派"}], "shoe_after": {"decks": 6, "remaining_cards": 281, "penetration_pct": 10, "cards_left_by_rank": {"A": 20, "2": 21, "3": 19, "4": 23, "5": 23, "6": 22, "7": 22, "8": 21, "9": 22, "10": 21, "J": 24, "Q": 23, "K": 20}, "hi_lo_running_count": 0, "true_count": 0.0}}, {"n": 5, "shoe_before": {"decks": 6, "remaining_cards": 275, "penetration_pct": 12, "cards_left_by_rank": {"A": 19, "2": 20, "3": 19, "4": 23, "5": 23, "6": 21, "7": 21, "8": 21, "9": 22, "10": 20, "J": 23, "Q": 23, "K": 20}, "hi_lo_running_count": -1, "true_count": -0.19}, "dealer_up": "A♣", "dealer_hole": "J♠", "dealer_revealed": true, "dealer": {"cards": ["A♣", "J♠"], "total": 21, "soft": true, "blackjack": true, "bust": false}, "events": ["莊家明牌為 A 或 10 點,進行暗牌確認", "莊家直接拿到 Blackjack,本局立即結束"], "players": [{"cards": ["10♦", "2♦"], "bet": 10, "doubled": false, "blackjack": false, "bust": false, "total": 12, "soft": false, "result": "lose", "net": -10, "bet_decision": {"decision": {"type": "choice", "choice": "min", "probabilities": {"min": 0.7562291100284627, "standard": 0.16685391773152444, "max": 0.07691697224001287}, "confidence": 0.634343665042694}, "bet": 10, "confidence": {"type": "score", "score": 1.2320991965875188, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.19676299225478636, "1": 0.46805537175271755, "2": 0.24150108314268687, "3": 0.09368055284980918}, "confidence": 0.37437481890290836}, "latency_ms": 2277}, "bankroll_after": 970, "id": "a", "name": "阿保", "style": "保守算牌派"}, {"cards": ["7♠", "6♦"], "bet": 50, "doubled": false, "blackjack": false, "bust": false, "total": 13, "soft": false, "result": "lose", "net": -50, "bet_decision": {"decision": {"type": "choice", "choice": "max", "probabilities": {"min": 0.20601333171277045, "standard": 0.34842502176841295, "max": 0.4455616465188165}, "confidence": 0.16834246977822478}, "bet": 50, "confidence": {"type": "score", "score": 1.564656149705211, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.14718873328060164, "1": 0.32229188630260436, "2": 0.34919387784777556, "3": 0.1813255025690185}, "confidence": 0.2020051445671739}, "latency_ms": 3202}, "bankroll_after": 850, "id": "b", "name": "阿衝", "style": "激進壓迫派"}], "shoe_after": {"decks": 6, "remaining_cards": 275, "penetration_pct": 12, "cards_left_by_rank": {"A": 19, "2": 20, "3": 19, "4": 23, "5": 23, "6": 21, "7": 21, "8": 21, "9": 22, "10": 20, "J": 23, "Q": 23, "K": 20}, "hi_lo_running_count": -1, "true_count": -0.19}}, {"n": 6, "shoe_before": {"decks": 6, "remaining_cards": 269, "penetration_pct": 14, "cards_left_by_rank": {"A": 17, "2": 20, "3": 18, "4": 23, "5": 22, "6": 21, "7": 21, "8": 21, "9": 21, "10": 20, "J": 23, "Q": 23, "K": 19}, "hi_lo_running_count": -2, "true_count": -0.39}, "dealer_up": "9♦", "dealer_hole": "5♣", "dealer_revealed": true, "dealer": {"cards": ["9♦", "5♣", "8♣"], "total": 22, "soft": false, "blackjack": false, "bust": true}, "events": ["莊家補牌 8♣ → 22 點", "莊家最終 22 點,爆牌"], "players": [{"cards": ["3♠", "A♦", "K♣", "7♠"], "bet": 10, "doubled": false, "total": 21, "soft": false, "bust": false, "blackjack": false, "steps": [{"type": "action", "cards": ["3♠", "A♦"], "total": 14, "soft": true, "dealer_up": "9♦", "allowed": ["hit", "stand", "double"], "decision": {"type": "choice", "choice": "hit", "probabilities": {"hit": 0.408721663227952, "stand": 0.29303182266998506, "double": 0.29824651410206277}, "confidence": 0.11308249484192802}, "risk": {"type": "score", "score": 1.5610409422586708, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.10553867166975685, "1": 0.32969436958342446, "2": 0.46295430356520967, "3": 0.10181265518160906}, "confidence": 0.3574156318954528}, "latency_ms": 3156, "drawn": "K♣", "total_after": 14}, {"type": "action", "cards": ["3♠", "A♦", "K♣"], "total": 14, "soft": false, "dealer_up": "9♦", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "hit", "probabilities": {"hit": 0.5122837140095126, "stand": 0.48771628599048733}, "confidence": 0.024567428019025117}, "risk": {"type": "score", "score": 1.5881935559142273, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.1232644149177596, "1": 0.3080900808526087, "2": 0.4258330376272768, "3": 0.14281246660235497}, "confidence": 0.30256862270951723}, "latency_ms": 2963, "drawn": "7♠", "total_after": 21}], "player_id": "a", "result": "win", "net": 10, "bet_decision": {"decision": {"type": "choice", "choice": "min", "probabilities": {"min": 0.7443943535681774, "standard": 0.1724434598311167, "max": 0.08316218660070591}, "confidence": 0.6165915303522661}, "bet": 10, "confidence": {"type": "score", "score": 1.2134591868984879, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.1889940408450792, "1": 0.48665170909181565, "2": 0.24625527238264336, "3": 0.07809897768046187}, "confidence": 0.40855273141135373}, "latency_ms": 2442}, "bankroll_after": 980, "id": "a", "name": "阿保", "style": "保守算牌派"}, {"cards": ["A♠", "K♣"], "bet": 50, "doubled": false, "total": 21, "soft": true, "bust": false, "blackjack": true, "steps": [], "player_id": "b", "result": "blackjack", "net": 75, "bet_decision": {"decision": {"type": "choice", "choice": "max", "probabilities": {"min": 0.1771453995341398, "standard": 0.3923319399455236, "max": 0.43052266052033666}, "confidence": 0.145783990780505}, "bet": 50, "confidence": {"type": "score", "score": 1.5896896540311678, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.13640040426554614, "1": 0.3189928849419576, "2": 0.36312336328827843, "3": 0.18148334750421774}, "confidence": 0.22672295902273243}, "latency_ms": 3056}, "bankroll_after": 925, "id": "b", "name": "阿衝", "style": "激進壓迫派"}], "shoe_after": {"decks": 6, "remaining_cards": 266, "penetration_pct": 15, "cards_left_by_rank": {"A": 17, "2": 20, "3": 18, "4": 23, "5": 22, "6": 21, "7": 20, "8": 20, "9": 21, "10": 20, "J": 23, "Q": 23, "K": 18}, "hi_lo_running_count": -3, "true_count": -0.59}}, {"n": 7, "shoe_before": {"decks": 6, "remaining_cards": 260, "penetration_pct": 17, "cards_left_by_rank": {"A": 16, "2": 19, "3": 17, "4": 23, "5": 22, "6": 21, "7": 20, "8": 19, "9": 21, "10": 18, "J": 23, "Q": 23, "K": 18}, "hi_lo_running_count": -4, "true_count": -0.8}, "dealer_up": "8♠", "dealer_hole": "3♦", "dealer_revealed": true, "dealer": {"cards": ["8♠", "3♦", "7♦"], "total": 18, "soft": false, "blackjack": false, "bust": false}, "events": ["莊家補牌 7♦ → 18 點", "莊家最終 18 點"], "players": [{"cards": ["10♣", "2♣", "K♠"], "bet": 10, "doubled": false, "total": 22, "soft": false, "bust": true, "blackjack": false, "steps": [{"type": "action", "cards": ["10♣", "2♣"], "total": 12, "soft": false, "dealer_up": "8♠", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "hit", "probabilities": {"hit": 0.5500152300334371, "stand": 0.44998476996656295}, "confidence": 0.10003046006687422}, "risk": {"type": "score", "score": 1.5209343777308968, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.1166634305190376, "1": 0.3360687076482218, "2": 0.4569379154155469, "3": 0.0903299464171938}, "confidence": 0.34027448489650913}, "latency_ms": 3071, "drawn": "K♠", "total_after": 22}], "player_id": "a", "result": "lose", "net": -10, "bet_decision": {"decision": {"type": "choice", "choice": "min", "probabilities": {"min": 0.69219418752702, "standard": 0.21671663493691912, "max": 0.09108917753606088}, "confidence": 0.5382912812905299}, "bet": 10, "confidence": {"type": "score", "score": 1.2634052808341587, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.17743488347704425, "1": 0.46766459898284457, "2": 0.2689608707690193, "3": 0.08593964677109182}, "confidence": 0.38172495221175273}, "latency_ms": 2427}, "bankroll_after": 970, "id": "a", "name": "阿保", "style": "保守算牌派"}, {"cards": ["10♠", "A♠"], "bet": 50, "doubled": false, "total": 21, "soft": true, "bust": false, "blackjack": true, "steps": [], "player_id": "b", "result": "blackjack", "net": 75, "bet_decision": {"decision": {"type": "choice", "choice": "max", "probabilities": {"min": 0.17415716770498335, "standard": 0.3849795123905866, "max": 0.44086331990443}, "confidence": 0.161294979856645}, "bet": 50, "confidence": {"type": "score", "score": 1.6670431235423304, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.12673888933460348, "1": 0.2859844451294625, "2": 0.38077131819493437, "3": 0.20650534734099973}, "confidence": 0.25403242886033084}, "latency_ms": 2975}, "bankroll_after": 1000, "id": "b", "name": "阿衝", "style": "激進壓迫派"}], "shoe_after": {"decks": 6, "remaining_cards": 258, "penetration_pct": 17, "cards_left_by_rank": {"A": 16, "2": 19, "3": 17, "4": 23, "5": 22, "6": 21, "7": 19, "8": 19, "9": 21, "10": 18, "J": 23, "Q": 23, "K": 17}, "hi_lo_running_count": -5, "true_count": -1.01}}, {"n": 8, "shoe_before": {"decks": 6, "remaining_cards": 252, "penetration_pct": 19, "cards_left_by_rank": {"A": 16, "2": 19, "3": 16, "4": 23, "5": 21, "6": 21, "7": 19, "8": 19, "9": 19, "10": 17, "J": 23, "Q": 22, "K": 17}, "hi_lo_running_count": -5, "true_count": -1.03}, "dealer_up": "10♦", "dealer_hole": "9♦", "dealer_revealed": true, "dealer": {"cards": ["10♦", "9♦"], "total": 19, "soft": false, "blackjack": false, "bust": false}, "events": ["莊家明牌為 A 或 10 點,進行暗牌確認", "確認結果:莊家沒有 Blackjack", "莊家最終 19 點"], "players": [{"cards": ["3♥", "Q♦"], "bet": 10, "doubled": false, "total": 13, "soft": false, "bust": false, "blackjack": false, "steps": [{"type": "action", "cards": ["3♥", "Q♦"], "total": 13, "soft": false, "dealer_up": "10♦", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "stand", "probabilities": {"hit": 0.39634239573570534, "stand": 0.6036576042642946}, "confidence": 0.2073152085285892}, "risk": {"type": "score", "score": 1.3805666512347543, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.16969624145522536, "1": 0.3621346556920123, "2": 0.3860753130155444, "3": 0.08209378983721773}, "confidence": 0.2163790715603192}, "latency_ms": 2496, "drawn": null, "total_after": 13}], "player_id": "a", "result": "lose", "net": -10, "bet_decision": {"decision": {"type": "choice", "choice": "min", "probabilities": {"min": 0.7656214237065283, "standard": 0.14200820159033867, "max": 0.09237037470313315}, "confidence": 0.6484321355597924}, "bet": 10, "confidence": {"type": "score", "score": 1.2299018689518944, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.19870975918080533, "1": 0.4610254792488059, "2": 0.2519178950080773, "3": 0.08834686656231128}, "confidence": 0.3726786126864948}, "latency_ms": 2564}, "bankroll_after": 960, "id": "a", "name": "阿保", "style": "保守算牌派"}, {"cards": ["5♦", "9♣", "3♠"], "bet": 50, "doubled": false, "total": 17, "soft": false, "bust": false, "blackjack": false, "steps": [{"type": "action", "cards": ["5♦", "9♣"], "total": 14, "soft": false, "dealer_up": "10♦", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "hit", "probabilities": {"hit": 0.6363603957200638, "stand": 0.3636396042799362}, "confidence": 0.2727207914401275}, "risk": {"type": "score", "score": 1.621485575327213, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.12499958056879856, "1": 0.28623851581568904, "2": 0.4310386513350132, "3": 0.15772325228049922}, "confidence": 0.3060390707662146}, "latency_ms": 4541, "drawn": "3♠", "total_after": 17}, {"type": "action", "cards": ["5♦", "9♣", "3♠"], "total": 17, "soft": false, "dealer_up": "10♦", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "stand", "probabilities": {"hit": 0.41681588405691944, "stand": 0.5831841159430805}, "confidence": 0.166368231886161}, "risk": {"type": "score", "score": 1.3499666013886922, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.19919145911352995, "1": 0.34421202407439494, "2": 0.364034973121928, "3": 0.09256154369014709}, "confidence": 0.16484351400839803}, "latency_ms": 3635, "drawn": null, "total_after": 17}], "player_id": "b", "result": "lose", "net": -50, "bet_decision": {"decision": {"type": "choice", "choice": "max", "probabilities": {"min": 0.17807237353368274, "standard": 0.3629673835384618, "max": 0.4589602429278555}, "confidence": 0.18844036439178322}, "bet": 50, "confidence": {"type": "score", "score": 1.6467384307127664, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.12712966024657835, "1": 0.29849798780601905, "2": 0.37487661293546065, "3": 0.199495739011942}, "confidence": 0.24774695268888225}, "latency_ms": 3080}, "bankroll_after": 950, "id": "b", "name": "阿衝", "style": "激進壓迫派"}], "shoe_after": {"decks": 6, "remaining_cards": 251, "penetration_pct": 20, "cards_left_by_rank": {"A": 16, "2": 19, "3": 15, "4": 23, "5": 21, "6": 21, "7": 19, "8": 19, "9": 19, "10": 17, "J": 23, "Q": 22, "K": 17}, "hi_lo_running_count": -4, "true_count": -0.83}}, {"n": 9, "shoe_before": {"decks": 6, "remaining_cards": 245, "penetration_pct": 21, "cards_left_by_rank": {"A": 14, "2": 19, "3": 15, "4": 23, "5": 21, "6": 20, "7": 18, "8": 19, "9": 18, "10": 17, "J": 23, "Q": 22, "K": 16}, "hi_lo_running_count": -6, "true_count": -1.27}, "dealer_up": "K♥", "dealer_hole": "7♣", "dealer_revealed": true, "dealer": {"cards": ["K♥", "7♣"], "total": 17, "soft": false, "blackjack": false, "bust": false}, "events": ["莊家明牌為 A 或 10 點,進行暗牌確認", "確認結果:莊家沒有 Blackjack", "莊家最終 17 點"], "players": [{"cards": ["6♦", "9♦"], "bet": 10, "doubled": false, "total": 15, "soft": false, "bust": false, "blackjack": false, "steps": [{"type": "action", "cards": ["6♦", "9♦"], "total": 15, "soft": false, "dealer_up": "K♥", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "stand", "probabilities": {"hit": 0.4065556882060927, "stand": 0.5934443117939072}, "confidence": 0.18688862358781444}, "risk": {"type": "score", "score": 1.4625511690470288, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.13799581285873977, "1": 0.34352512010586594, "2": 0.43641115216501986, "3": 0.08206791487037438}, "confidence": 0.29841533930628017}, "latency_ms": 2666, "drawn": null, "total_after": 15}], "player_id": "a", "result": "lose", "net": -10, "bet_decision": {"decision": {"type": "choice", "choice": "min", "probabilities": {"min": 0.7083570651368878, "standard": 0.19545918656860584, "max": 0.09618374829450631}, "confidence": 0.5625355977053317}, "bet": 10, "confidence": {"type": "score", "score": 1.213641546925921, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.20136564532087406, "1": 0.4638059699745116, "2": 0.25464957716243375, "3": 0.08017880754218061}, "confidence": 0.38362716243233097}, "latency_ms": 1984}, "bankroll_after": 950, "id": "a", "name": "阿保", "style": "保守算牌派"}, {"cards": ["A♥", "A♠", "Q♠", "8♣"], "bet": 50, "doubled": false, "total": 20, "soft": false, "bust": false, "blackjack": false, "steps": [{"type": "action", "cards": ["A♥", "A♠"], "total": 12, "soft": true, "dealer_up": "K♥", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "hit", "probabilities": {"hit": 0.5939977924213096, "stand": 0.4060022075786904}, "confidence": 0.18799558484261913}, "risk": {"type": "score", "score": 1.4685976982166182, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.18819208543459834, "1": 0.30035061456540596, "2": 0.3661248163487746, "3": 0.14533248365122103}, "confidence": 0.17793273091417627}, "latency_ms": 3686, "drawn": "Q♠", "total_after": 12}, {"type": "action", "cards": ["A♥", "A♠", "Q♠"], "total": 12, "soft": false, "dealer_up": "K♥", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "hit", "probabilities": {"hit": 0.6052363769804255, "stand": 0.39476362301957446}, "confidence": 0.21047275396085108}, "risk": {"type": "score", "score": 1.5574917347618844, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.16846858723710814, "1": 0.28599754847447173, "2": 0.3651074065778472, "3": 0.18042645771057275}, "confidence": 0.19663881934073923}, "latency_ms": 3449, "drawn": "8♣", "total_after": 20}, {"type": "action", "cards": ["A♥", "A♠", "Q♠", "8♣"], "total": 20, "soft": false, "dealer_up": "K♥", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "stand", "probabilities": {"hit": 0.3199784228049078, "stand": 0.6800215771950923}, "confidence": 0.3600431543901845}, "risk": {"type": "score", "score": 1.2731733674465937, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.2453494945322414, "1": 0.3366067819479323, "2": 0.31756458506081714, "3": 0.10047913845900906}, "confidence": 0.23612764348892334}, "latency_ms": 3726, "drawn": null, "total_after": 20}], "player_id": "b", "result": "win", "net": 50, "bet_decision": {"decision": {"type": "choice", "choice": "max", "probabilities": {"min": 0.18317788211857033, "standard": 0.37100120794841007, "max": 0.4458209099330196}, "confidence": 0.1687313648995294}, "bet": 50, "confidence": {"type": "score", "score": 1.7016208412378346, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.1245882351848078, "1": 0.2690667058185058, "2": 0.3864810415707307, "3": 0.21986401742595574}, "confidence": 0.2618928063859228}, "latency_ms": 2915}, "bankroll_after": 1000, "id": "b", "name": "阿衝", "style": "激進壓迫派"}], "shoe_after": {"decks": 6, "remaining_cards": 243, "penetration_pct": 22, "cards_left_by_rank": {"A": 14, "2": 19, "3": 15, "4": 23, "5": 21, "6": 20, "7": 18, "8": 18, "9": 18, "10": 17, "J": 23, "Q": 21, "K": 16}, "hi_lo_running_count": -7, "true_count": -1.5}}, {"n": 10, "shoe_before": {"decks": 6, "remaining_cards": 237, "penetration_pct": 24, "cards_left_by_rank": {"A": 14, "2": 18, "3": 15, "4": 22, "5": 19, "6": 20, "7": 18, "8": 18, "9": 17, "10": 17, "J": 23, "Q": 21, "K": 15}, "hi_lo_running_count": -4, "true_count": -0.88}, "dealer_up": "4♦", "dealer_hole": "5♣", "dealer_revealed": true, "dealer": {"cards": ["4♦", "5♣", "A♦"], "total": 20, "soft": true, "blackjack": false, "bust": false}, "events": ["莊家補牌 A♦ → 20 點", "莊家最終 20 點"], "players": [{"cards": ["5♥", "2♦", "3♦", "5♥", "Q♠"], "bet": 10, "doubled": false, "total": 25, "soft": false, "bust": true, "blackjack": false, "steps": [{"type": "action", "cards": ["5♥", "2♦"], "total": 7, "soft": false, "dealer_up": "4♦", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "hit", "probabilities": {"hit": 0.7694504382534303, "stand": 0.23054956174656965}, "confidence": 0.5389008765068606}, "risk": {"type": "score", "score": 2.1228442812944257, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.054250014780109976, "1": 0.1406058459410434, "2": 0.4331939824831577, "3": 0.37195015679568905}, "confidence": 0.3789439677030476}, "latency_ms": 2707, "drawn": "3♦", "total_after": 10}, {"type": "action", "cards": ["5♥", "2♦", "3♦"], "total": 10, "soft": false, "dealer_up": "4♦", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "hit", "probabilities": {"hit": 0.7577920957558095, "stand": 0.24220790424419048}, "confidence": 0.515584191511619}, "risk": {"type": "score", "score": 1.6885405801021331, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.08428075713214693, "1": 0.28471381134884216, "2": 0.489189525803742, "3": 0.14181590571526903}, "confidence": 0.404908768671595}, "latency_ms": 2613, "drawn": "5♥", "total_after": 15}, {"type": "action", "cards": ["5♥", "2♦", "3♦", "5♥"], "total": 15, "soft": false, "dealer_up": "4♦", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "hit", "probabilities": {"hit": 0.5788127737999339, "stand": 0.421187226200066}, "confidence": 0.1576255475998678}, "risk": {"type": "score", "score": 1.4818083791924999, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.11315441258530298, "1": 0.36485505760468034, "2": 0.4490182678422307, "3": 0.07297226196778603}, "confidence": 0.33586385525692763}, "latency_ms": 2554, "drawn": "Q♠", "total_after": 25}], "player_id": "a", "result": "lose", "net": -10, "bet_decision": {"decision": {"type": "choice", "choice": "min", "probabilities": {"min": 0.7286488493616847, "standard": 0.175529357430751, "max": 0.09582179320756426}, "confidence": 0.5929732740425271}, "bet": 10, "confidence": {"type": "score", "score": 1.169905749673971, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.216524204542941, "1": 0.46785689500435057, "2": 0.24480784668850483, "3": 0.07081105376420364}, "confidence": 0.39704584124014686}, "latency_ms": 2177}, "bankroll_after": 940, "id": "a", "name": "阿保", "style": "保守算牌派"}, {"cards": ["K♠", "9♥"], "bet": 50, "doubled": false, "total": 19, "soft": false, "bust": false, "blackjack": false, "steps": [{"type": "action", "cards": ["K♠", "9♥"], "total": 19, "soft": false, "dealer_up": "4♦", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "stand", "probabilities": {"hit": 0.29597815331328925, "stand": 0.7040218466867108}, "confidence": 0.4080436933734215}, "risk": {"type": "score", "score": 1.0629240761621292, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.2850042266335565, "1": 0.40113920626045185, "2": 0.2797848314162972, "3": 0.03407173568969435}, "confidence": 0.3670674705707576}, "latency_ms": 3464, "drawn": null, "total_after": 19}], "player_id": "b", "result": "lose", "net": -50, "bet_decision": {"decision": {"type": "choice", "choice": "max", "probabilities": {"min": 0.17685549991326993, "standard": 0.3770309433442875, "max": 0.44611355674244274}, "confidence": 0.1691703351136641}, "bet": 50, "confidence": {"type": "score", "score": 1.6562748371119183, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.1332817973157961, "1": 0.2844193759896284, "2": 0.3750410189614367, "3": 0.20725780773313882}, "confidence": 0.24175922164564057}, "latency_ms": 2647}, "bankroll_after": 950, "id": "b", "name": "阿衝", "style": "激進壓迫派"}], "shoe_after": {"decks": 6, "remaining_cards": 233, "penetration_pct": 25, "cards_left_by_rank": {"A": 13, "2": 18, "3": 14, "4": 22, "5": 18, "6": 20, "7": 18, "8": 18, "9": 17, "10": 17, "J": 23, "Q": 20, "K": 15}, "hi_lo_running_count": -4, "true_count": -0.89}}, {"n": 11, "shoe_before": {"decks": 6, "remaining_cards": 227, "penetration_pct": 27, "cards_left_by_rank": {"A": 13, "2": 17, "3": 13, "4": 22, "5": 18, "6": 19, "7": 18, "8": 18, "9": 17, "10": 17, "J": 22, "Q": 20, "K": 13}, "hi_lo_running_count": -4, "true_count": -0.92}, "dealer_up": "J♥", "dealer_hole": "6♦", "dealer_revealed": true, "dealer": {"cards": ["J♥", "6♦", "2♠"], "total": 18, "soft": false, "blackjack": false, "bust": false}, "events": ["莊家明牌為 A 或 10 點,進行暗牌確認", "確認結果:莊家沒有 Blackjack", "莊家補牌 2♠ → 18 點", "莊家最終 18 點"], "players": [{"cards": ["K♦", "K♠"], "bet": 10, "doubled": false, "total": 20, "soft": false, "bust": false, "blackjack": false, "steps": [{"type": "action", "cards": ["K♦", "K♠"], "total": 20, "soft": false, "dealer_up": "J♥", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "stand", "probabilities": {"hit": 0.20113812988182753, "stand": 0.7988618701181724}, "confidence": 0.5977237402363449}, "risk": {"type": "score", "score": 0.9617831874646433, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.37266878093983297, "1": 0.3419774875432344, "2": 0.2362554946293891, "3": 0.04909823688754356}, "confidence": 0.03821681253535669}, "latency_ms": 2807, "drawn": null, "total_after": 20}], "player_id": "a", "result": "win", "net": 10, "bet_decision": {"decision": {"type": "choice", "choice": "min", "probabilities": {"min": 0.705457858991765, "standard": 0.19844058997151406, "max": 0.09610155103672088}, "confidence": 0.5581867884876476}, "bet": 10, "confidence": {"type": "score", "score": 1.1786459226702077, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.2098777886585959, "1": 0.4725124514370032, "2": 0.24669580847999806, "3": 0.07091395142440278}, "confidence": 0.40159850001260045}, "latency_ms": 2281}, "bankroll_after": 950, "id": "a", "name": "阿保", "style": "保守算牌派"}, {"cards": ["2♦", "3♥", "2♥", "A♠"], "bet": 50, "doubled": false, "total": 18, "soft": true, "bust": false, "blackjack": false, "steps": [{"type": "action", "cards": ["2♦", "3♥"], "total": 5, "soft": false, "dealer_up": "J♥", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "hit", "probabilities": {"hit": 0.8339648493225332, "stand": 0.1660351506774667}, "confidence": 0.6679296986450665}, "risk": {"type": "score", "score": 1.9632358033511101, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.08778014756208807, "1": 0.17939112699816237, "2": 0.414641499966301, "3": 0.3181872254734486}, "confidence": 0.3268613524042129}, "latency_ms": 3984, "drawn": "2♥", "total_after": 7}, {"type": "action", "cards": ["2♦", "3♥", "2♥"], "total": 7, "soft": false, "dealer_up": "J♥", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "hit", "probabilities": {"hit": 0.8254946752313272, "stand": 0.17450532476867275}, "confidence": 0.6509893504626545}, "risk": {"type": "score", "score": 1.9199601944797384, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.08961070661335929, "1": 0.1944316247075459, "2": 0.4223444362650919, "3": 0.2936132324140029}, "confidence": 0.33273372965173253}, "latency_ms": 3647, "drawn": "A♠", "total_after": 18}, {"type": "action", "cards": ["2♦", "3♥", "2♥", "A♠"], "total": 18, "soft": true, "dealer_up": "J♥", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "stand", "probabilities": {"hit": 0.4516507728170757, "stand": 0.5483492271829243}, "confidence": 0.09669845436584867}, "risk": {"type": "score", "score": 1.3633877251471496, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.1898920049001741, "1": 0.35446905502554144, "2": 0.3579981501012455, "3": 0.09764078997303899}, "confidence": 0.16810614520107148}, "latency_ms": 3469, "drawn": null, "total_after": 18}], "player_id": "b", "result": "push", "net": 0, "bet_decision": {"decision": {"type": "choice", "choice": "max", "probabilities": {"min": 0.17659368546509877, "standard": 0.38463171221217046, "max": 0.43877460232273074}, "confidence": 0.15816190348409612}, "bet": 50, "confidence": {"type": "score", "score": 1.6844293372148722, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.12644015955206472, "1": 0.27508981392517495, "2": 0.3860705562785838, "3": 0.21239947024417657}, "confidence": 0.259630396726519}, "latency_ms": 2718}, "bankroll_after": 950, "id": "b", "name": "阿衝", "style": "激進壓迫派"}], "shoe_after": {"decks": 6, "remaining_cards": 224, "penetration_pct": 28, "cards_left_by_rank": {"A": 12, "2": 15, "3": 13, "4": 22, "5": 18, "6": 19, "7": 18, "8": 18, "9": 17, "10": 17, "J": 22, "Q": 20, "K": 13}, "hi_lo_running_count": -3, "true_count": -0.7}}, {"n": 12, "shoe_before": {"decks": 6, "remaining_cards": 218, "penetration_pct": 30, "cards_left_by_rank": {"A": 12, "2": 15, "3": 13, "4": 22, "5": 18, "6": 18, "7": 16, "8": 17, "9": 17, "10": 17, "J": 22, "Q": 19, "K": 12}, "hi_lo_running_count": -4, "true_count": -0.95}, "dealer_up": "K♦", "dealer_hole": "8♦", "dealer_revealed": true, "dealer": {"cards": ["K♦", "8♦"], "total": 18, "soft": false, "blackjack": false, "bust": false}, "events": ["莊家明牌為 A 或 10 點,進行暗牌確認", "確認結果:莊家沒有 Blackjack", "莊家最終 18 點"], "players": [{"cards": ["Q♦", "6♣"], "bet": 10, "doubled": false, "total": 16, "soft": false, "bust": false, "blackjack": false, "steps": [{"type": "action", "cards": ["Q♦", "6♣"], "total": 16, "soft": false, "dealer_up": "K♦", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "stand", "probabilities": {"hit": 0.39016492109703543, "stand": 0.6098350789029646}, "confidence": 0.21967015780592924}, "risk": {"type": "score", "score": 1.7370799154639605, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.09344393533096118, "1": 0.2685085201031415, "2": 0.4455712383368731, "3": 0.19247630622902423}, "confidence": 0.3521273030059119}, "latency_ms": 2460, "drawn": null, "total_after": 16}], "player_id": "a", "result": "lose", "net": -10, "bet_decision": {"decision": {"type": "choice", "choice": "min", "probabilities": {"min": 0.7338713056072282, "standard": 0.171097765938789, "max": 0.09503092845398282}, "confidence": 0.6008069584108423}, "bet": 10, "confidence": {"type": "score", "score": 1.2594716391475735, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.16968632812925138, "1": 0.4794955805426685, "2": 0.2724782153793357, "3": 0.07833987594874454}, "confidence": 0.4011557045939238}, "latency_ms": 1995}, "bankroll_after": 940, "id": "a", "name": "阿保", "style": "保守算牌派"}, {"cards": ["7♥", "7♦", "3♥"], "bet": 50, "doubled": false, "total": 17, "soft": false, "bust": false, "blackjack": false, "steps": [{"type": "action", "cards": ["7♥", "7♦"], "total": 14, "soft": false, "dealer_up": "K♦", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "hit", "probabilities": {"hit": 0.608349823613437, "stand": 0.3916501763865629}, "confidence": 0.21669964722687407}, "risk": {"type": "score", "score": 1.4682163416498657, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.17040055353150113, "1": 0.3169814206595017, "2": 0.38661915643662725, "3": 0.12599886937236981}, "confidence": 0.21621860290512618}, "latency_ms": 3602, "drawn": "3♥", "total_after": 17}, {"type": "action", "cards": ["7♥", "7♦", "3♥"], "total": 17, "soft": false, "dealer_up": "K♦", "allowed": ["hit", "stand"], "decision": {"type": "choice", "choice": "stand", "probabilities": {"hit": 0.4373052794431983, "stand": 0.5626947205568017}, "confidence": 0.1253894411136034}, "risk": {"type": "score", "score": 1.27082220318929, "legend": {"0": "非常有利", "1": "稍微有利", "2": "有點不利", "3": "非常不利"}, "probabilities": {"0": 0.21799232108026073, "1": 0.36294600367568364, "2": 0.34930882621856046, "3": 0.06975284902549515}, "confidence": 0.29319315465018847}, "latency_ms": 3726, "drawn": null, "total_after": 17}], "player_id": "b", "result": "lose", "net": -50, "bet_decision": {"decision": {"type": "choice", "choice": "max", "probabilities": {"min": 0.18420429014082615, "standard": 0.39700684030663047, "max": 0.4187888695525434}, "confidence": 0.1281833043288151}, "bet": 50, "confidence": {"type": "score", "score": 1.6458985137494473, "legend": {"0": "沒信心", "1": "普通", "2": "有信心", "3": "非常有信心"}, "probabilities": {"0": 0.1260372937960504, "1": 0.2951391749084489, "2": 0.3857112550455043, "3": 0.19311227624999663}, "confidence": 0.25967396124945374}, "latency_ms": 3039}, "bankroll_after": 900, "id": "b", "name": "阿衝", "style": "激進壓迫派"}], "shoe_after": {"decks": 6, "remaining_cards": 217, "penetration_pct": 30, "cards_left_by_rank": {"A": 12, "2": 15, "3": 12, "4": 22, "5": 18, "6": 18, "7": 16, "8": 17, "9": 17, "10": 17, "J": 22, "Q": 19, "K": 12}, "hi_lo_running_count": -3, "true_count": -0.72}}]};
|
| 254 |
+
const SUIT_RED = new Set(["♥","♦"]);
|
| 255 |
+
const LABEL = {hit:"補牌", stand:"停牌", double:"加倍", min:"最小注", standard:"標準注", max:"重注"};
|
| 256 |
+
const VALUE = {A:11,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,"10":10,J:10,Q:10,K:10};
|
| 257 |
+
const RESULT_LABEL = {win:"勝", lose:"負", push:"和局", blackjack:"Blackjack"};
|
| 258 |
+
const $ = (s,el=document)=>el.querySelector(s);
|
| 259 |
+
let DATA=null, roundIdx=0, autoTimer=null, dealerOpen=true;
|
| 260 |
+
|
| 261 |
+
function money(n){ return (n>0?"+":"") + n.toLocaleString("en-US"); }
|
| 262 |
+
function cls(n){ return n>0?"pos":(n<0?"neg":"neutral"); }
|
| 263 |
+
function pct(x){ return (Math.max(0,Math.min(1,x||0))*100).toFixed(0)+"%"; }
|
| 264 |
+
function cardHTML(card, hidden){
|
| 265 |
+
if(hidden) return '<div class="card down">?</div>';
|
| 266 |
+
const r=card.slice(0,-1), s=card.slice(-1);
|
| 267 |
+
return '<div class="card '+(SUIT_RED.has(s)?"red":"")+'"><span class="r">'+r+'</span><span class="s">'+s+'</span></div>';
|
| 268 |
+
}
|
| 269 |
+
function cardsHTML(cards, opts={}){
|
| 270 |
+
return '<div class="cards">'+cards.map((c,i)=>cardHTML(c, opts.hideIndex===i)).join("")+'</div>';
|
| 271 |
+
}
|
| 272 |
+
function agentColor(id){ return id==="a" ? "var(--green)" : "var(--blue)"; }
|
| 273 |
+
function agentMeta(id){ return DATA.meta.agents.find(a=>a.id===id) || {name:id,style:""}; }
|
| 274 |
+
|
| 275 |
+
function totalsFor(round){
|
| 276 |
+
const out={};
|
| 277 |
+
for(const p of round.players){
|
| 278 |
+
out[p.id]=out[p.id]||{bet:0,net:0};
|
| 279 |
+
out[p.id].bet+=p.bet; out[p.id].net+=p.net;
|
| 280 |
+
}
|
| 281 |
+
return out;
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
function init(){
|
| 285 |
+
DATA = window.BATTLE_DATA;
|
| 286 |
+
if(!DATA){ $("#app").innerHTML='<div class="err">找不到 battle.json,請用 HTTP 伺服器開啟此頁。</div>'; return; }
|
| 287 |
+
document.title = DATA.meta.title + " · d1demo";
|
| 288 |
+
$("#app").innerHTML = `
|
| 289 |
+
<section class="view active" id="view-battle">
|
| 290 |
+
<div class="hero">
|
| 291 |
+
<h1>${DATA.meta.title.split(" ")[0]} <em>×</em> d1:21 點決策對戰</h1>
|
| 292 |
+
<p>${DATA.meta.subtitle}。每一注、每一次補牌/停牌/加倍,都是模型即時輸出的機率分佈,不是寫死的策略表。</p>
|
| 293 |
+
<div class="chips" id="metaChips"></div>
|
| 294 |
+
</div>
|
| 295 |
+
<div class="scoreboard" id="scoreboard"></div>
|
| 296 |
+
<div class="panel table-panel">
|
| 297 |
+
<div class="round-toolbar">
|
| 298 |
+
<button class="icon-btn" id="prevRound">← 上一局</button>
|
| 299 |
+
<div class="round-pills" id="roundPills"></div>
|
| 300 |
+
<button class="icon-btn" id="nextRound">下一局 →</button>
|
| 301 |
+
<button class="icon-btn" id="playBtn">▶ 自動播放</button>
|
| 302 |
+
<span class="round-label" id="roundLabel"></span>
|
| 303 |
+
</div>
|
| 304 |
+
<div id="roundView"></div>
|
| 305 |
+
<div class="arena-note">莊家由固定規則操作(S17),兩個玩家各自連到一個獨立的 llama.cpp server;模型只知道牌面、剩餘牌組成、真數與資金,不知道對手的手牌。</div>
|
| 306 |
+
</div>
|
| 307 |
+
</section>
|
| 308 |
+
|
| 309 |
+
<section class="view" id="view-stats"></section>
|
| 310 |
+
<section class="view" id="view-code">
|
| 311 |
+
<h2 class="section-title">整理程式</h2>
|
| 312 |
+
<div class="data-box">
|
| 313 |
+
<p>這一頁不是手寫的靜態稿:<b>blackjack.py</b> 負責發牌、呼叫兩個 d1 server 做決策並記錄每一步;<b>build_site.py</b> 把 battle.json 編譯成你現在看到的網站。原始碼同步放在這個 Space 的 <code>source/</code> 目錄。</p>
|
| 314 |
+
</div>
|
| 315 |
+
<div class="code-tabs" id="codeTabs"></div>
|
| 316 |
+
<div class="code-bar">
|
| 317 |
+
<span id="codeName">blackjack.py</span>
|
| 318 |
+
<a class="btn ghost" id="codeDownload" href="source/blackjack.py" download>下載此檔</a>
|
| 319 |
+
<button class="icon-btn" id="copyCode">複製</button>
|
| 320 |
+
</div>
|
| 321 |
+
<pre class="code"><code id="codeBody">載入中…</code></pre>
|
| 322 |
+
</section>
|
| 323 |
+
|
| 324 |
+
<section class="view" id="view-data">
|
| 325 |
+
<h2 class="section-title">原始資料</h2>
|
| 326 |
+
<div class="data-box">
|
| 327 |
+
<h3>battle.json</h3>
|
| 328 |
+
<p>完整對戰紀錄:每一局的牌、注碼、決策機率、信心度、風險評分、延遲、莊家補牌與資金變化。</p>
|
| 329 |
+
<p><a class="btn" href="battle.json" download>下載 battle.json</a>
|
| 330 |
+
<a class="btn ghost" href="battle.json" target="_blank" rel="noopener">在瀏覽器開啟</a></p>
|
| 331 |
+
</div>
|
| 332 |
+
<div class="data-box">
|
| 333 |
+
<h3>重現方式</h3>
|
| 334 |
+
<p>需要 llama.cpp b11786 以上版本、LiquidAI/d1-3B-GGUF(Q8_0)與 Python 3。</p>
|
| 335 |
+
<pre class="code"><code id="repro"></code></pre>
|
| 336 |
+
</div>
|
| 337 |
+
<div class="data-box">
|
| 338 |
+
<h3>本次執行摘要</h3>
|
| 339 |
+
<pre class="code"><code id="metaJson"></code></pre>
|
| 340 |
+
</div>
|
| 341 |
+
</section>
|
| 342 |
+
<footer style="border:0;padding-bottom:0"></footer>
|
| 343 |
+
`;
|
| 344 |
+
renderChips();
|
| 345 |
+
renderScoreboard();
|
| 346 |
+
renderPills();
|
| 347 |
+
renderRound();
|
| 348 |
+
renderStats();
|
| 349 |
+
renderCodeTabs();
|
| 350 |
+
bind();
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
function renderChips(){
|
| 354 |
+
const m=DATA.meta;
|
| 355 |
+
const chips=[
|
| 356 |
+
["模型",m.model],["量化",m.quant],["llama.cpp",m.llama_build],
|
| 357 |
+
["局數",m.rounds_played],["隨機種子",m.seed],["耗時",m.duration_sec+"s"],
|
| 358 |
+
["莊家","S17"],["BJ 賠率","3:2"]
|
| 359 |
+
];
|
| 360 |
+
$("#metaChips").innerHTML=chips.map(([k,v])=>`<span class="chip">${k} <b>${v}</b></span>`).join("");
|
| 361 |
+
}
|
| 362 |
+
|
| 363 |
+
function renderScoreboard(){
|
| 364 |
+
const agents=DATA.meta.agents;
|
| 365 |
+
$("#scoreboard").innerHTML=agents.map(a=>{
|
| 366 |
+
const last=DATA.rounds[DATA.rounds.length-1];
|
| 367 |
+
const p=last.players.find(x=>x.id===a.id);
|
| 368 |
+
const profit=a.final_bankroll-DATA.meta.start_bankroll;
|
| 369 |
+
const color=agentColor(a.id);
|
| 370 |
+
return `<div class="score" style="--accent:${color}">
|
| 371 |
+
<div class="id">PLAYER ${a.id.toUpperCase()}</div>
|
| 372 |
+
<div class="name">${a.name}</div>
|
| 373 |
+
<div class="style">${a.style}</div>
|
| 374 |
+
<div class="bank" style="color:${color}">${a.final_bankroll.toLocaleString("en-US")}</div>
|
| 375 |
+
<div class="sub">起始 ${DATA.meta.start_bankroll} · 損益 <span class="big-delta ${cls(profit)}">${money(profit)}</span></div>
|
| 376 |
+
<div class="sub">最終一局:${p?(RESULT_LABEL[p.result]||p.result):""} ${p?money(p.net):""}</div>
|
| 377 |
+
</div>`;
|
| 378 |
+
}).join("");
|
| 379 |
+
}
|
| 380 |
+
|
| 381 |
+
function roundTone(r){
|
| 382 |
+
const res=r.players.map(p=>p.result);
|
| 383 |
+
const wins=res.filter(x=>x==="win"||x==="blackjack").length;
|
| 384 |
+
const loses=res.filter(x=>x==="lose").length;
|
| 385 |
+
if(wins===res.length) return "win";
|
| 386 |
+
if(loses===res.length) return "lose";
|
| 387 |
+
if(res.every(x=>x==="push")) return "push";
|
| 388 |
+
return "mixed";
|
| 389 |
+
}
|
| 390 |
+
|
| 391 |
+
function renderPills(){
|
| 392 |
+
$("#roundPills").innerHTML=DATA.rounds.map((r,i)=>{
|
| 393 |
+
return `<button class="pill ${roundTone(r)} ${i===roundIdx?"active":""}" data-round="${i}" title="第 ${r.n} 局">
|
| 394 |
+
<span class="dot"></span>${r.n}</button>`;
|
| 395 |
+
}).join("");
|
| 396 |
+
$("#roundPills").querySelectorAll(".pill").forEach(b=>b.onclick=()=>{stopAuto();roundIdx=+b.dataset.round;dealerOpen=true;renderRound();renderPills();});
|
| 397 |
+
}
|
| 398 |
+
|
| 399 |
+
function stepHTML(step, i){
|
| 400 |
+
const d=step.decision||{};
|
| 401 |
+
const probs=d.probabilities||{};
|
| 402 |
+
const keys=Object.keys(probs).sort((a,b)=>probs[b]-probs[a]);
|
| 403 |
+
const risk=step.risk;
|
| 404 |
+
const riskText=risk ? `風險評分 <b>${risk.score.toFixed(2)}</b>/3 · 信心 ${pct(risk.confidence)}` : "";
|
| 405 |
+
const drawn=step.drawn ? `補到 <b>${step.drawn}</b> → <b>${step.total_after}</b> 點` : (d.choice==="stand"?"維持 <b>"+step.total+"</b> 點":`→ <b>${step.total_after}</b> 點`);
|
| 406 |
+
return `<li class="step">
|
| 407 |
+
<div class="step-head">
|
| 408 |
+
<span class="step-no">#${i+1}</span>
|
| 409 |
+
<span class="mini">${step.cards.join(" ")}</span>
|
| 410 |
+
<span class="mini" style="color:var(--muted)">(${step.total}${step.soft?" 軟":""})</span>
|
| 411 |
+
<span class="action ${LABEL[d.choice]?d.choice:"unknown"}">${LABEL[d.choice]||d.choice||"?"}</span>
|
| 412 |
+
<span class="meta">信心 ${pct(d.confidence)} · ${((step.latency_ms||0)/1000).toFixed(2)}s</span>
|
| 413 |
+
</div>
|
| 414 |
+
<div class="probs">
|
| 415 |
+
${keys.map(k=>`<span>${LABEL[k]||k}</span><span class="bar"><i style="--c:var(--${k==="hit"?"hit":k==="stand"?"stand":"double"});width:${(probs[k]*100).toFixed(1)}%"></i></span><span class="pct">${pct(probs[k])}</span>`).join("")}
|
| 416 |
+
</div>
|
| 417 |
+
<div class="step-foot"><span>${drawn}</span>${riskText?`<span>${riskText}</span>`:""}${d.fallback?`<span style="color:var(--red)">伺服器無回應,改用規則備援</span>`:""}</div>
|
| 418 |
+
</li>`;
|
| 419 |
+
}
|
| 420 |
+
|
| 421 |
+
function playerHTML(p){
|
| 422 |
+
const steps=(p.steps||[]).map(stepHTML).join("");
|
| 423 |
+
const bd=p.bet_decision||{};
|
| 424 |
+
const betDecision=bd.decision||{};
|
| 425 |
+
const betProbs=betDecision.probabilities||{};
|
| 426 |
+
const betLabel=LABEL[betDecision.choice]||betDecision.choice||"";
|
| 427 |
+
return `<article class="player ${p.result} ${p.blackjack?"blackjack":""}" style="--accent:${agentColor(p.id)}">
|
| 428 |
+
<div class="p-head">
|
| 429 |
+
<div><h3>${p.name}</h3><div class="style">${p.style||""}</div></div>
|
| 430 |
+
<div class="bet">注碼 <b>${p.bet}</b>${p.doubled?" · 已加倍":""}</div>
|
| 431 |
+
</div>
|
| 432 |
+
<div class="hand">
|
| 433 |
+
${cardsHTML(p.cards)}
|
| 434 |
+
<div class="total">${p.total}<small>${p.bust?"BUST":p.soft?"軟牌":p.blackjack?"BJ":"硬牌"}</small></div>
|
| 435 |
+
<span class="result ${p.result}">${RESULT_LABEL[p.result]||p.result}</span>
|
| 436 |
+
</div>
|
| 437 |
+
<div class="bet-line">
|
| 438 |
+
<span class="tag">注碼決策 ${betLabel}</span>
|
| 439 |
+
${p.rebuy?`<span class="tag" style="color:var(--amber)">補充籌碼 +${p.rebuy}</span>`:""}
|
| 440 |
+
<span>信心 ${pct(betDecision.confidence)}</span>
|
| 441 |
+
${Object.keys(betProbs).length?`<span>${Object.keys(betProbs).sort((a,b)=>betProbs[b]-betProbs[a]).map(k=>`${LABEL[k]||k} ${pct(betProbs[k])}`).join(" · ")}</span>`:""}
|
| 442 |
+
</div>
|
| 443 |
+
<details><summary>看模型怎麼決定注碼</summary>
|
| 444 |
+
<div class="body">狀態包含資金、剩余牌、Hi-Lo 真數與規則;模型在 min / standard / max 三種注碼中挑一個,並另外給出信心評分。</div>
|
| 445 |
+
</details>
|
| 446 |
+
${steps?`<ol class="steps">${steps}</ol>`:`<div class="step-foot" style="margin-top:12px">莊家直接 Blackjack,玩家沒有動作機會。</div>`}
|
| 447 |
+
<div class="net"><span>本局結果</span><span class="amt ${cls(p.net)}">${money(p.net)} <small style="font-size:12px;color:var(--muted)">→ ${p.bankroll_after}</small></span></div>
|
| 448 |
+
</article>`;
|
| 449 |
+
}
|
| 450 |
+
|
| 451 |
+
function renderRound(){
|
| 452 |
+
const r=DATA.rounds[roundIdx];
|
| 453 |
+
if(!r){ return; }
|
| 454 |
+
const d=r.dealer||{};
|
| 455 |
+
const hideHole=!dealerOpen && r.dealer_revealed;
|
| 456 |
+
const dealerCards=(d.cards||[]).map((c,i)=>cardHTML(c, hideHole && i===1)).join("");
|
| 457 |
+
const totals=totalsFor(r);
|
| 458 |
+
const pillTone=roundTone(r);
|
| 459 |
+
$("#roundLabel").textContent=`第 ${r.n} / ${DATA.rounds.length} 局 · 剩餘 ${r.shoe_before.remaining_cards} 張 · 真數 ${r.shoe_before.true_count}`;
|
| 460 |
+
$("#roundView").innerHTML=`
|
| 461 |
+
<div class="round-head">
|
| 462 |
+
<h2>第 ${r.n} 局</h2>
|
| 463 |
+
<span class="shoe">洗牌前剩餘 ${r.shoe_before.remaining_cards} 張(穿透率 ${r.shoe_before.penetration_pct}%) · Hi-Lo 真數 ${r.shoe_before.true_count}</span>
|
| 464 |
+
</div>
|
| 465 |
+
<div class="dealer">
|
| 466 |
+
<div>
|
| 467 |
+
<div class="label">DEALER · 莊家</div>
|
| 468 |
+
<div class="total-badge">${hideHole?"?":d.total}${d.bust?' <span style="font-size:12px;color:var(--red)">BUST</span>':""}${d.blackjack?' <span style="font-size:12px;color:var(--gold)">BJ</span>':""}</div>
|
| 469 |
+
</div>
|
| 470 |
+
<div class="cards">${dealerCards}</div>
|
| 471 |
+
<div class="note">${r.events.join(" · ")||"莊家依規則補牌"}</div>
|
| 472 |
+
${r.dealer_revealed?`<button class="icon-btn" id="revealBtn">${dealerOpen?"蓋回暗牌":"揭開暗牌"}</button>`:""}
|
| 473 |
+
</div>
|
| 474 |
+
<div class="players">${r.players.map(playerHTML).join("")}</div>
|
| 475 |
+
<div class="events"><b>牌桌事件</b><ul>${r.events.map(e=>`<li>${e}</li>`).join("")}</ul></div>
|
| 476 |
+
`;
|
| 477 |
+
const rb=$("#revealBtn");
|
| 478 |
+
if(rb) rb.onclick=()=>{dealerOpen=!dealerOpen;renderRound();};
|
| 479 |
+
$("#prevRound").disabled=roundIdx===0;
|
| 480 |
+
$("#nextRound").disabled=roundIdx===DATA.rounds.length-1;
|
| 481 |
+
document.querySelectorAll(".pill").forEach((b,i)=>b.classList.toggle("active",i===roundIdx));
|
| 482 |
+
}
|
| 483 |
+
|
| 484 |
+
function bind(){
|
| 485 |
+
$("#prevRound").onclick=()=>{stopAuto(); if(roundIdx>0){roundIdx--;dealerOpen=true;renderRound();}};
|
| 486 |
+
$("#nextRound").onclick=()=>{stopAuto(); if(roundIdx<DATA.rounds.length-1){roundIdx++;dealerOpen=true;renderRound();}};
|
| 487 |
+
$("#playBtn").onclick=()=>{
|
| 488 |
+
if(autoTimer){stopAuto();return;}
|
| 489 |
+
roundIdx=0;dealerOpen=true;renderRound();
|
| 490 |
+
$("#playBtn").textContent="⏸ 暫停";
|
| 491 |
+
autoTimer=setInterval(()=>{
|
| 492 |
+
if(roundIdx>=DATA.rounds.length-1){stopAuto();return;}
|
| 493 |
+
roundIdx++;dealerOpen=true;renderRound();
|
| 494 |
+
},4000);
|
| 495 |
+
};
|
| 496 |
+
document.querySelectorAll("nav button").forEach(b=>{
|
| 497 |
+
b.onclick=()=>{
|
| 498 |
+
document.querySelectorAll("nav button").forEach(x=>x.classList.toggle("active",x===b));
|
| 499 |
+
document.querySelectorAll(".view").forEach(v=>v.classList.toggle("active",v.id==="view-"+b.dataset.view));
|
| 500 |
+
window.scrollTo({top:0,behavior:"smooth"});
|
| 501 |
+
};
|
| 502 |
+
});
|
| 503 |
+
}
|
| 504 |
+
function stopAuto(){ if(autoTimer){clearInterval(autoTimer);autoTimer=null;} const b=$("#playBtn"); if(b)b.textContent="▶ 自動播放"; }
|
| 505 |
+
|
| 506 |
+
function statsFor(id){
|
| 507 |
+
const s={rounds:0,win:0,lose:0,push:0,bj:0,bust:0,double:0,net:0,wagered:0,steps:0,conf:0,lat:0,actions:{hit:0,stand:0,double:0},firstActions:[]};
|
| 508 |
+
for(const r of DATA.rounds){
|
| 509 |
+
const p=r.players.find(x=>x.id===id); if(!p) continue;
|
| 510 |
+
s.rounds++;
|
| 511 |
+
if(p.result==="win")s.win++;
|
| 512 |
+
else if(p.result==="lose")s.lose++;
|
| 513 |
+
else if(p.result==="push")s.push++;
|
| 514 |
+
else if(p.result==="blackjack")s.win++;
|
| 515 |
+
if(p.blackjack)s.bj++;
|
| 516 |
+
if(p.bust)s.bust++;
|
| 517 |
+
if(p.doubled)s.double++;
|
| 518 |
+
s.net+=p.net; s.wagered+=p.bet;
|
| 519 |
+
(p.steps||[]).forEach(st=>{
|
| 520 |
+
s.steps++; s.conf+=st.decision.confidence||0; s.lat+=st.latency_ms||0;
|
| 521 |
+
s.actions[st.decision.choice]=(s.actions[st.decision.choice]||0)+1;
|
| 522 |
+
});
|
| 523 |
+
const p1=(p.steps||[])[0];
|
| 524 |
+
if(p1) s.firstActions.push({round:r.n,action:p1.decision.choice});
|
| 525 |
+
}
|
| 526 |
+
s.avgConf=s.steps?s.conf/s.steps:0;
|
| 527 |
+
s.avgLat=s.steps?s.lat/s.steps:0;
|
| 528 |
+
return s;
|
| 529 |
+
}
|
| 530 |
+
|
| 531 |
+
function renderStats(){
|
| 532 |
+
const a=statsFor("a"), b=statsFor("b");
|
| 533 |
+
const agree=a.firstActions.filter(x=>{
|
| 534 |
+
const y=b.firstActions.find(z=>z.round===x.round);
|
| 535 |
+
return y && y.action===x.action;
|
| 536 |
+
}).length;
|
| 537 |
+
const compared=a.firstActions.filter(x=>b.firstActions.some(z=>z.round===x.round)).length;
|
| 538 |
+
const dealerBust=DATA.rounds.filter(r=>r.dealer&&r.dealer.bust).length;
|
| 539 |
+
const dealerBj=DATA.rounds.filter(r=>r.dealer&&r.dealer.blackjack).length;
|
| 540 |
+
const dealerAvg=DATA.rounds.reduce((sum,r)=>sum+((r.dealer||{}).total||0),0)/Math.max(DATA.rounds.length,1);
|
| 541 |
+
const totalNet=a.net+b.net;
|
| 542 |
+
const houseEdge=totalNet;
|
| 543 |
+
|
| 544 |
+
function statCard(id,s){
|
| 545 |
+
const m=agentMeta(id);
|
| 546 |
+
const totalActions=Math.max(s.steps,1);
|
| 547 |
+
return `<div class="stat-card" style="--accent:${agentColor(id)}">
|
| 548 |
+
<h3>${m.name}</h3><div class="style">${m.style}</div>
|
| 549 |
+
<div class="kv">
|
| 550 |
+
<div><div class="k">損益</div><div class="v ${cls(s.net)}">${money(s.net)}</div></div>
|
| 551 |
+
<div><div class="k">勝 / 負 / 和</div><div class="v">${s.win} / ${s.lose} / ${s.push}</div></div>
|
| 552 |
+
<div><div class="k">Blackjack</div><div class="v">${s.bj}</div></div>
|
| 553 |
+
<div><div class="k">爆牌</div><div class="v">${s.bust}</div></div>
|
| 554 |
+
<div><div class="k">加倍次數</div><div class="v">${s.double}</div></div>
|
| 555 |
+
<div><div class="k">總下注</div><div class="v">${s.wagered}</div></div>
|
| 556 |
+
<div><div class="k">平均信心</div><div class="v">${pct(s.avgConf)}</div></div>
|
| 557 |
+
<div><div class="k">平均推理</div><div class="v">${(s.avgLat/1000).toFixed(2)}s</div></div>
|
| 558 |
+
</div>
|
| 559 |
+
<div class="dist">
|
| 560 |
+
<div class="mini-title">動作分佈(${s.steps} 次)</div>
|
| 561 |
+
${["hit","stand","double"].map(k=>`<div class="row"><span>${LABEL[k]}</span><span class="bar"><i style="--c:var(--${k});width:${(100*s.actions[k]/totalActions).toFixed(1)}%"></i></span><span class="pct">${s.actions[k]}</span></div>`).join("")}
|
| 562 |
+
</div>
|
| 563 |
+
</div>`;
|
| 564 |
+
}
|
| 565 |
+
|
| 566 |
+
const chart=bankrollChart();
|
| 567 |
+
$("#view-stats").innerHTML=`
|
| 568 |
+
<h2 class="section-title">決策統計</h2>
|
| 569 |
+
<div class="stats-grid">${statCard("a",a)}${statCard("b",b)}</div>
|
| 570 |
+
<div class="chart-wrap">
|
| 571 |
+
<div class="chart-legend">
|
| 572 |
+
<span><i style="background:var(--green)"></i>${agentMeta("a").name}</span>
|
| 573 |
+
<span><i style="background:var(--blue)"></i>${agentMeta("b").name}</span>
|
| 574 |
+
<span>起始資金 ${DATA.meta.start_bankroll}</span>
|
| 575 |
+
</div>
|
| 576 |
+
${chart}
|
| 577 |
+
</div>
|
| 578 |
+
<div class="h2h">
|
| 579 |
+
<h3 style="margin:0 0 6px">兩個 d1 有多像?</h3>
|
| 580 |
+
<p style="color:var(--muted);margin:4px 0 10px">比較每一局雙方第一次動作(同一副牌、同一個莊家明牌、相同的剩餘牌資訊)。</p>
|
| 581 |
+
<div class="big">${compared?Math.round(100*agree/compared):0}%</div>
|
| 582 |
+
<div style="color:var(--muted);font-size:13px">首個動作一致率 · 共比較 ${compared} 局</div>
|
| 583 |
+
<div class="kv" style="grid-template-columns:repeat(3,1fr);margin-top:16px">
|
| 584 |
+
<div><div class="k">莊家爆牌率</div><div class="v">${Math.round(100*dealerBust/DATA.rounds.length)}%</div></div>
|
| 585 |
+
<div><div class="k">莊家 Blackjack</div><div class="v">${dealerBj} 局</div></div>
|
| 586 |
+
<div><div class="k">莊家平均點數</div><div class="v">${dealerAvg.toFixed(1)}</div></div>
|
| 587 |
+
</div>
|
| 588 |
+
</div>
|
| 589 |
+
<div class="data-box" style="margin-top:14px">
|
| 590 |
+
<h3>兩個模型合計對抗莊家</h3>
|
| 591 |
+
<p style="font-size:15px">總損益 <b class="${cls(totalNet)}" style="font-size:24px">${money(totalNet)}</b> · 每位玩家起始 ${DATA.meta.start_bankroll},合計起始 ${DATA.meta.start_bankroll*2}</p>
|
| 592 |
+
</div>
|
| 593 |
+
`;
|
| 594 |
+
}
|
| 595 |
+
|
| 596 |
+
function bankrollChart(){
|
| 597 |
+
const W=760,H=230,pad={l:52,r:14,t:14,b:26};
|
| 598 |
+
const agents=DATA.meta.agents;
|
| 599 |
+
const series=agents.map(a=>{
|
| 600 |
+
const vals=[DATA.meta.start_bankroll];
|
| 601 |
+
for(const r of DATA.rounds){
|
| 602 |
+
const p=r.players.find(x=>x.id===a.id);
|
| 603 |
+
vals.push(p?p.bankroll_after:DATA.meta.start_bankroll);
|
| 604 |
+
}
|
| 605 |
+
return {a,vals,color:a.id==="a"?"#39d98a":"#5cc8ff"};
|
| 606 |
+
});
|
| 607 |
+
const all=series.flatMap(s=>s.vals);
|
| 608 |
+
let min=Math.min(...all),max=Math.max(...all);
|
| 609 |
+
const span=Math.max(max-min,100);
|
| 610 |
+
min=Math.floor((min-span*0.1)/50)*50; max=Math.ceil((max+span*0.1)/50)*50;
|
| 611 |
+
const X=i=>pad.l+(W-pad.l-pad.r)*(i/(DATA.rounds.length||1));
|
| 612 |
+
const Y=v=>H-pad.b-(H-pad.t-pad.b)*((v-min)/(max-min||1));
|
| 613 |
+
const step=Math.max(50,Math.ceil((max-min)/4/50)*50);
|
| 614 |
+
min=Math.floor(min/step)*step; max=Math.ceil(max/step)*step;
|
| 615 |
+
const ticks=Math.round((max-min)/step);
|
| 616 |
+
let grid="";
|
| 617 |
+
for(let i=0;i<=ticks;i++){
|
| 618 |
+
const v=min+(max-min)*i/ticks, y=Y(v);
|
| 619 |
+
grid+=`<line x1="${pad.l}" y1="${y}" x2="${W-pad.r}" y2="${y}" stroke="rgba(255,255,255,.07)"/>`;
|
| 620 |
+
grid+=`<text x="${pad.l-8}" y="${y+4}" fill="#8fae9f" font-size="10" text-anchor="end">${Math.round(v)}</text>`;
|
| 621 |
+
}
|
| 622 |
+
const paths=series.map(s=>{
|
| 623 |
+
const d=s.vals.map((v,i)=>`${i?"L":"M"}${X(i).toFixed(1)},${Y(v).toFixed(1)}`).join(" ");
|
| 624 |
+
const dots=s.vals.map((v,i)=>`<circle cx="${X(i)}" cy="${Y(v)}" r="2.6" fill="${s.color}"><title>第 ${i} 局後:${v}</title></circle>`).join("");
|
| 625 |
+
return `<path d="${d}" fill="none" stroke="${s.color}" stroke-width="2.4" stroke-linejoin="round"/>${dots}`;
|
| 626 |
+
}).join("");
|
| 627 |
+
return `<svg viewBox="0 0 ${W} ${H}" width="100%" role="img" aria-label="資金曲線">${grid}${paths}</svg>`;
|
| 628 |
+
}
|
| 629 |
+
|
| 630 |
+
const CODE_FILES=[
|
| 631 |
+
{file:"blackjack.py",label:"blackjack.py · 對戰與記錄"},
|
| 632 |
+
{file:"build_site.py",label:"build_site.py · 網頁編譯"},
|
| 633 |
+
{file:"run_demo.sh",label:"run_demo.sh · 一鍵執行"},
|
| 634 |
+
];
|
| 635 |
+
function renderCodeTabs(){
|
| 636 |
+
$("#codeTabs").innerHTML=CODE_FILES.map((f,i)=>`<button data-file="${f.file}" class="${i===0?"active":""}">${f.label}</button>`).join("");
|
| 637 |
+
$("#codeTabs").querySelectorAll("button").forEach(b=>b.onclick=()=>{
|
| 638 |
+
$("#codeTabs").querySelectorAll("button").forEach(x=>x.classList.toggle("active",x===b));
|
| 639 |
+
loadCode(b.dataset.file);
|
| 640 |
+
});
|
| 641 |
+
loadCode("blackjack.py");
|
| 642 |
+
$("#copyCode").onclick=async()=>{
|
| 643 |
+
try{await navigator.clipboard.writeText($("#codeBody").textContent);$("#copyCode").textContent="已複製";setTimeout(()=>$("#copyCode").textContent="複製",1200);}catch(e){}
|
| 644 |
+
};
|
| 645 |
+
$("#repro").textContent=`# 1) 下載模型與 llama.cpp(CPU 版)
|
| 646 |
+
llama-server -m d1-3B-Q8_0.gguf --port 8081 -c 2048 -np 1 --no-webui &
|
| 647 |
+
llama-server -m d1-3B-Q8_0.gguf --port 8082 -c 2048 -np 1 --no-webui &
|
| 648 |
+
|
| 649 |
+
# 2) 讓兩個 d1 打 12 局,並把每一步寫成 JSON
|
| 650 |
+
python3 blackjack.py --rounds 12 --seed ${DATA.meta.seed} --out battle.json
|
| 651 |
+
|
| 652 |
+
# 3) 把 JSON 編譯成這個靜態網站
|
| 653 |
+
python3 build_site.py --battle battle.json --out site
|
| 654 |
+
|
| 655 |
+
# 4) 上傳 static Space
|
| 656 |
+
python3 - <<'PY'
|
| 657 |
+
from huggingface_hub import HfApi
|
| 658 |
+
HfApi().upload_folder(folder_path="site", repo_id="HelloSun/d1demo", repo_type="space")
|
| 659 |
+
PY`;
|
| 660 |
+
$("#metaJson").textContent=JSON.stringify(DATA.meta,null,2);
|
| 661 |
+
}
|
| 662 |
+
function loadCode(file){
|
| 663 |
+
$("#codeName").textContent=file;
|
| 664 |
+
$("#codeDownload").href="source/"+file;
|
| 665 |
+
$("#codeBody").textContent="載入中…";
|
| 666 |
+
fetch("source/"+file).then(r=>{if(!r.ok)throw new Error(r.status);return r.text();})
|
| 667 |
+
.then(t=>{$("#codeBody").textContent=t;})
|
| 668 |
+
.catch(()=>{$("#codeBody").textContent="(無法載入 "+file+",請確認 source/ 目錄存在)";});
|
| 669 |
+
}
|
| 670 |
+
|
| 671 |
+
init();
|
| 672 |
+
</script>
|
| 673 |
+
</body>
|
| 674 |
</html>
|
source/blackjack.py
ADDED
|
@@ -0,0 +1,535 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""d1 vs d1 blackjack battle.
|
| 3 |
+
|
| 4 |
+
Two llama.cpp servers, each hosting a LiquidAI d1-3B decision model, play
|
| 5 |
+
blackjack against a rule-based dealer. Every decision (bet size + hit / stand /
|
| 6 |
+
double) is a /v1/systemone query. The complete battle is written as JSON and
|
| 7 |
+
rendered into a static web page by build_site.py.
|
| 8 |
+
|
| 9 |
+
Usage:
|
| 10 |
+
python3 blackjack.py --rounds 12 --out battle.json
|
| 11 |
+
"""
|
| 12 |
+
import argparse
|
| 13 |
+
import json
|
| 14 |
+
import random
|
| 15 |
+
import time
|
| 16 |
+
import urllib.error
|
| 17 |
+
import urllib.request
|
| 18 |
+
from datetime import datetime, timezone
|
| 19 |
+
from pathlib import Path
|
| 20 |
+
|
| 21 |
+
SUITS = ["♠", "♥", "♦", "♣"]
|
| 22 |
+
RANKS = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
|
| 23 |
+
RANK_VALUE = {r: (11 if r == "A" else 10 if r in ("10", "J", "Q", "K") else int(r)) for r in RANKS}
|
| 24 |
+
|
| 25 |
+
DECKS = 6
|
| 26 |
+
BASE_BET = 10
|
| 27 |
+
START_BANKROLL = 1000
|
| 28 |
+
|
| 29 |
+
AGENTS = [
|
| 30 |
+
{
|
| 31 |
+
"id": "a",
|
| 32 |
+
"name": "阿保",
|
| 33 |
+
"style": "保守算牌派",
|
| 34 |
+
"description": "以保住本金為第一優先,只有在牌面明顯有利時才加注,遇到風險寧可停牌。",
|
| 35 |
+
"url": "http://127.0.0.1:8081",
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
"id": "b",
|
| 39 |
+
"name": "阿衝",
|
| 40 |
+
"style": "激進壓迫派",
|
| 41 |
+
"description": "相信進攻是最好的防守,喜歡加倍與大注,願意為了期望值承擔波動。",
|
| 42 |
+
"url": "http://127.0.0.1:8082",
|
| 43 |
+
},
|
| 44 |
+
]
|
| 45 |
+
|
| 46 |
+
RULES = {
|
| 47 |
+
"decks": DECKS,
|
| 48 |
+
"dealer": "莊家軟 17 停牌 (S17)",
|
| 49 |
+
"blackjack_pays": "3:2",
|
| 50 |
+
"double": "僅限硬 9–11 或軟 13–18 時加倍,加倍後只補一張",
|
| 51 |
+
"split": "本局不開放分牌",
|
| 52 |
+
"insurance": "不開放保險",
|
| 53 |
+
"reshuffle": "剩餘牌數低於 25% 時重洗",
|
| 54 |
+
"start_bankroll": START_BANKROLL,
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def make_shoe(rng):
|
| 59 |
+
shoe = [(r, s) for _ in range(DECKS) for s in SUITS for r in RANKS]
|
| 60 |
+
rng.shuffle(shoe)
|
| 61 |
+
return shoe
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def card_str(card):
|
| 65 |
+
return f"{card[0]}{card[1]}"
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def hand_value(cards):
|
| 69 |
+
total = sum(RANK_VALUE[r] for r, _ in cards)
|
| 70 |
+
soft_aces = sum(1 for r, _ in cards if r == "A")
|
| 71 |
+
while total > 21 and soft_aces:
|
| 72 |
+
total -= 10
|
| 73 |
+
soft_aces -= 1
|
| 74 |
+
return total, soft_aces > 0
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def is_blackjack(cards):
|
| 78 |
+
return len(cards) == 2 and hand_value(cards)[0] == 21
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def hi_lo(card):
|
| 82 |
+
r = card[0]
|
| 83 |
+
if r in ("2", "3", "4", "5", "6"):
|
| 84 |
+
return 1
|
| 85 |
+
if r in ("10", "J", "Q", "K", "A"):
|
| 86 |
+
return -1
|
| 87 |
+
return 0
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def shoe_state(shoe, running_count):
|
| 91 |
+
remaining = len(shoe)
|
| 92 |
+
decks_left = max(remaining / 52.0, 0.15)
|
| 93 |
+
counts = {r: 0 for r in RANKS}
|
| 94 |
+
for r, _ in shoe:
|
| 95 |
+
counts[r] += 1
|
| 96 |
+
return {
|
| 97 |
+
"decks": DECKS,
|
| 98 |
+
"remaining_cards": remaining,
|
| 99 |
+
"penetration_pct": round(100 * (1 - remaining / (DECKS * 52))),
|
| 100 |
+
"cards_left_by_rank": counts,
|
| 101 |
+
"hi_lo_running_count": running_count,
|
| 102 |
+
"true_count": round(running_count / decks_left, 2),
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
class D1Client:
|
| 107 |
+
def __init__(self, agent):
|
| 108 |
+
self.agent = agent
|
| 109 |
+
self.url = agent["url"].rstrip("/")
|
| 110 |
+
self.calls = 0
|
| 111 |
+
self.total_ms = 0
|
| 112 |
+
|
| 113 |
+
def ask(self, state, questions, retries=2):
|
| 114 |
+
payload = json.dumps({"state": state, "questions": questions}).encode()
|
| 115 |
+
last_err = None
|
| 116 |
+
for _ in range(retries + 1):
|
| 117 |
+
started = time.time()
|
| 118 |
+
req = urllib.request.Request(
|
| 119 |
+
f"{self.url}/v1/systemone",
|
| 120 |
+
data=payload,
|
| 121 |
+
headers={"Content-Type": "application/json"},
|
| 122 |
+
)
|
| 123 |
+
try:
|
| 124 |
+
with urllib.request.urlopen(req, timeout=120) as resp:
|
| 125 |
+
body = json.loads(resp.read())
|
| 126 |
+
elapsed = int((time.time() - started) * 1000)
|
| 127 |
+
self.calls += 1
|
| 128 |
+
self.total_ms += elapsed
|
| 129 |
+
return body["answers"], elapsed, None
|
| 130 |
+
except Exception as exc: # noqa: BLE001
|
| 131 |
+
last_err = f"{type(exc).__name__}: {exc}"
|
| 132 |
+
time.sleep(0.4)
|
| 133 |
+
return None, 0, last_err
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
def pick(answers, key, valid):
|
| 137 |
+
ans = (answers or {}).get(key) or {}
|
| 138 |
+
choice = ans.get("choice")
|
| 139 |
+
if choice not in valid:
|
| 140 |
+
return None
|
| 141 |
+
return ans
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
def draw(shoe, count):
|
| 145 |
+
cards = [shoe.pop() for _ in range(min(count, len(shoe)))]
|
| 146 |
+
return cards
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def fallback_action(total, soft, allowed):
|
| 150 |
+
if "double" in allowed and total in (10, 11):
|
| 151 |
+
return "double"
|
| 152 |
+
if soft and total >= 19:
|
| 153 |
+
return "stand"
|
| 154 |
+
if total >= 17:
|
| 155 |
+
return "stand"
|
| 156 |
+
return "hit"
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def play_hand(client, shoe, player, hand, dealer_up, running_count, rng):
|
| 160 |
+
steps = []
|
| 161 |
+
while True:
|
| 162 |
+
total, soft = hand_value(hand["cards"])
|
| 163 |
+
if total >= 21:
|
| 164 |
+
break
|
| 165 |
+
allowed = ["hit", "stand"]
|
| 166 |
+
double_spot = (not soft and 9 <= total <= 11) or (soft and 13 <= total <= 18)
|
| 167 |
+
if len(hand["cards"]) == 2 and player["bankroll"] >= hand["bet"] and double_spot:
|
| 168 |
+
allowed.append("double")
|
| 169 |
+
|
| 170 |
+
state = {
|
| 171 |
+
"game": "blackjack",
|
| 172 |
+
"you": {
|
| 173 |
+
"name": client.agent["name"],
|
| 174 |
+
"style": client.agent["style"],
|
| 175 |
+
"personality": client.agent["description"],
|
| 176 |
+
"bankroll": player["bankroll"],
|
| 177 |
+
},
|
| 178 |
+
"hand": {
|
| 179 |
+
"cards": [card_str(c) for c in hand["cards"]],
|
| 180 |
+
"total": total,
|
| 181 |
+
"soft": soft,
|
| 182 |
+
"is_pair": len(hand["cards"]) == 2 and hand["cards"][0][0] == hand["cards"][1][0],
|
| 183 |
+
},
|
| 184 |
+
"dealer_upcard": card_str(dealer_up),
|
| 185 |
+
"bet": hand["bet"],
|
| 186 |
+
"allowed_actions": allowed,
|
| 187 |
+
"shoe": shoe_state(shoe, running_count),
|
| 188 |
+
"rules": RULES,
|
| 189 |
+
}
|
| 190 |
+
criteria = {
|
| 191 |
+
"hit": "再補一張牌:點數偏低、需要往 17 點以上推進時使用",
|
| 192 |
+
"stand": "停牌:點數已接近 21,或再補牌爆牌風險過高時使用",
|
| 193 |
+
}
|
| 194 |
+
if "double" in allowed:
|
| 195 |
+
criteria["double"] = "加倍下注:再多押一倍且只補一張,通常用在 9、10、11 點這種有把握的局面"
|
| 196 |
+
cards_text = " ".join(card_str(c) for c in hand["cards"])
|
| 197 |
+
questions = {
|
| 198 |
+
"action": {
|
| 199 |
+
"type": "choice",
|
| 200 |
+
"instructions": (
|
| 201 |
+
f"你是 {client.agent['name']},風格「{client.agent['style']}」:"
|
| 202 |
+
f"{client.agent['description']} "
|
| 203 |
+
"你在玩 21 點,目標是讓手牌點數盡量接近 21 但不超過,並贏過莊家。"
|
| 204 |
+
f"你的手牌是 {cards_text},合計 {total} 點"
|
| 205 |
+
f"({'軟牌,A 可算 11 或 1' if soft else '硬牌'})。"
|
| 206 |
+
f"莊家明牌是 {card_str(dealer_up)}。現在只能選一個動作,請選長期期望值最高的。"
|
| 207 |
+
),
|
| 208 |
+
"criteria": criteria,
|
| 209 |
+
},
|
| 210 |
+
"risk": {
|
| 211 |
+
"type": "score",
|
| 212 |
+
"instructions": "以你的風格判斷,現在這個局面對你有多不利?",
|
| 213 |
+
"criteria": ["非常有利", "稍微有利", "有點不利", "非常不利"],
|
| 214 |
+
},
|
| 215 |
+
}
|
| 216 |
+
answers, latency, err = client.ask(state, questions)
|
| 217 |
+
decision = pick(answers, "action", allowed)
|
| 218 |
+
if decision is None:
|
| 219 |
+
if not allowed:
|
| 220 |
+
break
|
| 221 |
+
action = fallback_action(total, soft, allowed)
|
| 222 |
+
decision = {
|
| 223 |
+
"type": "choice",
|
| 224 |
+
"choice": action,
|
| 225 |
+
"probabilities": {a: (1.0 if a == action else 0.0) for a in allowed},
|
| 226 |
+
"confidence": 0.0,
|
| 227 |
+
"fallback": True,
|
| 228 |
+
"error": err,
|
| 229 |
+
}
|
| 230 |
+
action = decision["choice"]
|
| 231 |
+
step = {
|
| 232 |
+
"type": "action",
|
| 233 |
+
"cards": [card_str(c) for c in hand["cards"]],
|
| 234 |
+
"total": total,
|
| 235 |
+
"soft": soft,
|
| 236 |
+
"dealer_up": card_str(dealer_up),
|
| 237 |
+
"allowed": allowed,
|
| 238 |
+
"decision": decision,
|
| 239 |
+
"risk": (answers or {}).get("risk"),
|
| 240 |
+
"latency_ms": latency,
|
| 241 |
+
"drawn": None,
|
| 242 |
+
"total_after": total,
|
| 243 |
+
}
|
| 244 |
+
steps.append(step)
|
| 245 |
+
|
| 246 |
+
if action == "stand":
|
| 247 |
+
break
|
| 248 |
+
if action == "double":
|
| 249 |
+
player["bankroll"] -= hand["bet"]
|
| 250 |
+
hand["bet"] *= 2
|
| 251 |
+
hand["doubled"] = True
|
| 252 |
+
new_card = draw(shoe, 1)[0]
|
| 253 |
+
running_count += hi_lo(new_card)
|
| 254 |
+
step["drawn"] = card_str(new_card)
|
| 255 |
+
hand["cards"].append(new_card)
|
| 256 |
+
hand["total"], hand["soft"] = hand_value(hand["cards"])
|
| 257 |
+
step["total_after"] = hand["total"]
|
| 258 |
+
break
|
| 259 |
+
new_card = draw(shoe, 1)[0]
|
| 260 |
+
running_count += hi_lo(new_card)
|
| 261 |
+
step["drawn"] = card_str(new_card)
|
| 262 |
+
hand["cards"].append(new_card)
|
| 263 |
+
hand["total"], hand["soft"] = hand_value(hand["cards"])
|
| 264 |
+
step["total_after"] = hand["total"]
|
| 265 |
+
if hand["total"] > 21:
|
| 266 |
+
break
|
| 267 |
+
|
| 268 |
+
hand["total"], hand["soft"] = hand_value(hand["cards"])
|
| 269 |
+
hand["bust"] = hand["total"] > 21
|
| 270 |
+
hand["blackjack"] = is_blackjack(hand["cards"]) and not hand["doubled"]
|
| 271 |
+
return running_count, steps
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
def settle(hand, dealer_total, dealer_blackjack, dealer_bust):
|
| 275 |
+
if hand["bust"]:
|
| 276 |
+
return "lose", -hand["bet"]
|
| 277 |
+
if hand["blackjack"] and not dealer_blackjack:
|
| 278 |
+
return "blackjack", int(hand["bet"] * 1.5)
|
| 279 |
+
if dealer_blackjack and not hand["blackjack"]:
|
| 280 |
+
return "lose", -hand["bet"]
|
| 281 |
+
if dealer_bust:
|
| 282 |
+
return "win", hand["bet"]
|
| 283 |
+
if hand["total"] > dealer_total:
|
| 284 |
+
return "win", hand["bet"]
|
| 285 |
+
if hand["total"] < dealer_total:
|
| 286 |
+
return "lose", -hand["bet"]
|
| 287 |
+
return "push", 0
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
def ask_bet(client, player, dealer_up_hint, shoe, running_count):
|
| 291 |
+
state = {
|
| 292 |
+
"game": "blackjack",
|
| 293 |
+
"you": {
|
| 294 |
+
"name": client.agent["name"],
|
| 295 |
+
"style": client.agent["style"],
|
| 296 |
+
"personality": client.agent["description"],
|
| 297 |
+
"bankroll": player["bankroll"],
|
| 298 |
+
},
|
| 299 |
+
"phase": "betting",
|
| 300 |
+
"base_bet": BASE_BET,
|
| 301 |
+
"shoe": shoe_state(shoe, running_count),
|
| 302 |
+
"rules": RULES,
|
| 303 |
+
}
|
| 304 |
+
questions = {
|
| 305 |
+
"bet": {
|
| 306 |
+
"type": "choice",
|
| 307 |
+
"instructions": (
|
| 308 |
+
f"你是 {client.agent['name']},風格是「{client.agent['style']}」:"
|
| 309 |
+
f"{client.agent['description']} 新的一局要下注了,"
|
| 310 |
+
"請依資金、剩餘牌組成與真數決定注碼。"
|
| 311 |
+
),
|
| 312 |
+
"criteria": {
|
| 313 |
+
"min": f"最小注 {BASE_BET}",
|
| 314 |
+
"standard": f"標準注 {BASE_BET * 2}",
|
| 315 |
+
"max": f"重注 {BASE_BET * 5}",
|
| 316 |
+
},
|
| 317 |
+
},
|
| 318 |
+
"confidence": {
|
| 319 |
+
"type": "score",
|
| 320 |
+
"instructions": "你對這一注的信心有多高?",
|
| 321 |
+
"criteria": ["沒信心", "普通", "有信心", "非常有信心"],
|
| 322 |
+
},
|
| 323 |
+
}
|
| 324 |
+
answers, latency, err = client.ask(state, questions)
|
| 325 |
+
decision = pick(answers, "bet", ["min", "standard", "max"])
|
| 326 |
+
if decision is None:
|
| 327 |
+
decision = {
|
| 328 |
+
"choice": "min",
|
| 329 |
+
"probabilities": {"min": 1.0, "standard": 0.0, "max": 0.0},
|
| 330 |
+
"confidence": 0.0,
|
| 331 |
+
"fallback": True,
|
| 332 |
+
"error": err,
|
| 333 |
+
}
|
| 334 |
+
amounts = {"min": BASE_BET, "standard": BASE_BET * 2, "max": BASE_BET * 5}
|
| 335 |
+
bet = min(amounts[decision["choice"]], player["bankroll"])
|
| 336 |
+
if bet <= 0:
|
| 337 |
+
bet = min(BASE_BET, player["bankroll"])
|
| 338 |
+
return {
|
| 339 |
+
"decision": decision,
|
| 340 |
+
"bet": max(bet, 0),
|
| 341 |
+
"confidence": (answers or {}).get("confidence"),
|
| 342 |
+
"latency_ms": latency,
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
def run(rounds, seed, out_path):
|
| 347 |
+
rng = random.Random(seed)
|
| 348 |
+
shoe = make_shoe(rng)
|
| 349 |
+
running_count = 0
|
| 350 |
+
players = [
|
| 351 |
+
{"id": a["id"], "name": a["name"], "style": a["style"], "bankroll": START_BANKROLL}
|
| 352 |
+
for a in AGENTS
|
| 353 |
+
]
|
| 354 |
+
clients = {a["id"]: D1Client(a) for a in AGENTS}
|
| 355 |
+
started = time.time()
|
| 356 |
+
history = []
|
| 357 |
+
|
| 358 |
+
for n in range(1, rounds + 1):
|
| 359 |
+
if len(shoe) < DECKS * 52 * 0.25:
|
| 360 |
+
shoe = make_shoe(rng)
|
| 361 |
+
running_count = 0
|
| 362 |
+
|
| 363 |
+
rebuys = {}
|
| 364 |
+
for a in AGENTS:
|
| 365 |
+
player = next(p for p in players if p["id"] == a["id"])
|
| 366 |
+
if player["bankroll"] < BASE_BET:
|
| 367 |
+
rebuys[a["id"]] = 500
|
| 368 |
+
player["bankroll"] += 500
|
| 369 |
+
|
| 370 |
+
bets = {}
|
| 371 |
+
for a in AGENTS:
|
| 372 |
+
player = next(p for p in players if p["id"] == a["id"])
|
| 373 |
+
bets[a["id"]] = ask_bet(clients[a["id"]], player, None, shoe, running_count)
|
| 374 |
+
|
| 375 |
+
hands = {}
|
| 376 |
+
for a in AGENTS:
|
| 377 |
+
player = next(p for p in players if p["id"] == a["id"])
|
| 378 |
+
hands[a["id"]] = {
|
| 379 |
+
"cards": draw(shoe, 2),
|
| 380 |
+
"bet": bets[a["id"]]["bet"],
|
| 381 |
+
"doubled": False,
|
| 382 |
+
}
|
| 383 |
+
running_count += sum(hi_lo(c) for c in hands[a["id"]]["cards"])
|
| 384 |
+
player["bankroll"] -= hands[a["id"]]["bet"]
|
| 385 |
+
|
| 386 |
+
dealer_cards = draw(shoe, 2)
|
| 387 |
+
running_count += sum(hi_lo(c) for c in dealer_cards)
|
| 388 |
+
dealer_up, dealer_hole = dealer_cards[0], dealer_cards[1]
|
| 389 |
+
dealer_blackjack = is_blackjack(dealer_cards)
|
| 390 |
+
peek = RANK_VALUE[dealer_up[0]] in (10, 11)
|
| 391 |
+
events = []
|
| 392 |
+
for aid, amount in rebuys.items():
|
| 393 |
+
events.append(f"{next(p for p in players if p['id'] == aid)['name']} 籌碼不足,補充 {amount}")
|
| 394 |
+
|
| 395 |
+
round_rec = {
|
| 396 |
+
"n": n,
|
| 397 |
+
"shoe_before": shoe_state(shoe, running_count),
|
| 398 |
+
"dealer_up": card_str(dealer_up),
|
| 399 |
+
"dealer_hole": card_str(dealer_hole),
|
| 400 |
+
"dealer_revealed": False,
|
| 401 |
+
"dealer": None,
|
| 402 |
+
"events": events,
|
| 403 |
+
"players": [],
|
| 404 |
+
}
|
| 405 |
+
if peek:
|
| 406 |
+
events.append("莊家明牌為 A 或 10 點,進行暗牌確認")
|
| 407 |
+
|
| 408 |
+
if dealer_blackjack:
|
| 409 |
+
events.append("莊家直接拿到 Blackjack,本局立即結束")
|
| 410 |
+
round_rec["dealer_revealed"] = True
|
| 411 |
+
round_rec["dealer"] = {
|
| 412 |
+
"cards": [card_str(c) for c in dealer_cards],
|
| 413 |
+
"total": 21,
|
| 414 |
+
"soft": hand_value(dealer_cards)[1],
|
| 415 |
+
"blackjack": True,
|
| 416 |
+
"bust": False,
|
| 417 |
+
}
|
| 418 |
+
else:
|
| 419 |
+
for a in AGENTS:
|
| 420 |
+
hand = hands[a["id"]]
|
| 421 |
+
player = next(p for p in players if p["id"] == a["id"])
|
| 422 |
+
running_count, steps = play_hand(
|
| 423 |
+
clients[a["id"]], shoe, player, hand, dealer_up, running_count, rng
|
| 424 |
+
)
|
| 425 |
+
hand["steps"] = steps
|
| 426 |
+
hand["player_id"] = a["id"]
|
| 427 |
+
|
| 428 |
+
if not dealer_blackjack:
|
| 429 |
+
dealer_total, dealer_soft = hand_value(dealer_cards)
|
| 430 |
+
if any(not h["bust"] for h in hands.values()):
|
| 431 |
+
if peek:
|
| 432 |
+
events.append("確認結果:莊家沒有 Blackjack")
|
| 433 |
+
while dealer_total < 17:
|
| 434 |
+
card = draw(shoe, 1)[0]
|
| 435 |
+
running_count += hi_lo(card)
|
| 436 |
+
dealer_cards.append(card)
|
| 437 |
+
dealer_total, dealer_soft = hand_value(dealer_cards)
|
| 438 |
+
events.append(f"莊家補牌 {card_str(card)} → {dealer_total} 點")
|
| 439 |
+
events.append(f"莊家最終 {dealer_total} 點" + (",爆牌" if dealer_total > 21 else ""))
|
| 440 |
+
round_rec["dealer_revealed"] = True
|
| 441 |
+
round_rec["dealer"] = {
|
| 442 |
+
"cards": [card_str(c) for c in dealer_cards],
|
| 443 |
+
"total": dealer_total,
|
| 444 |
+
"soft": dealer_soft,
|
| 445 |
+
"blackjack": False,
|
| 446 |
+
"bust": dealer_total > 21,
|
| 447 |
+
}
|
| 448 |
+
|
| 449 |
+
dealer_total = round_rec["dealer"]["total"]
|
| 450 |
+
dealer_bust = round_rec["dealer"]["bust"]
|
| 451 |
+
|
| 452 |
+
for a in AGENTS:
|
| 453 |
+
player = next(p for p in players if p["id"] == a["id"])
|
| 454 |
+
hand = hands[a["id"]]
|
| 455 |
+
hand["blackjack"] = is_blackjack(hand["cards"]) and not hand["doubled"]
|
| 456 |
+
hand["bust"] = hand_value(hand["cards"])[0] > 21
|
| 457 |
+
result, net = settle(hand, dealer_total, dealer_blackjack, dealer_bust)
|
| 458 |
+
hand.update(
|
| 459 |
+
{
|
| 460 |
+
"cards": [card_str(c) for c in hand["cards"]],
|
| 461 |
+
"total": hand_value(hand["cards"])[0],
|
| 462 |
+
"soft": hand_value(hand["cards"])[1],
|
| 463 |
+
"blackjack": is_blackjack(hand["cards"]) and not hand["doubled"],
|
| 464 |
+
"bust": hand_value(hand["cards"])[0] > 21,
|
| 465 |
+
"result": result,
|
| 466 |
+
"net": net,
|
| 467 |
+
"bet_decision": bets[a["id"]],
|
| 468 |
+
}
|
| 469 |
+
)
|
| 470 |
+
player["bankroll"] += hand["bet"] + net
|
| 471 |
+
hand["bankroll_after"] = player["bankroll"]
|
| 472 |
+
if a["id"] in rebuys:
|
| 473 |
+
hand["rebuy"] = rebuys[a["id"]]
|
| 474 |
+
hand["id"] = a["id"]
|
| 475 |
+
hand["name"] = a["name"]
|
| 476 |
+
hand["style"] = a["style"]
|
| 477 |
+
round_rec["players"].append(hand)
|
| 478 |
+
|
| 479 |
+
round_rec["shoe_after"] = shoe_state(shoe, running_count)
|
| 480 |
+
history.append(round_rec)
|
| 481 |
+
print(
|
| 482 |
+
f"round {n:02d} | dealer {dealer_total} | "
|
| 483 |
+
+ " | ".join(
|
| 484 |
+
f"{h['name']} {h['total']} {h['result']} net {h['net']:+d} bank {h['bankroll_after']}"
|
| 485 |
+
for h in round_rec["players"]
|
| 486 |
+
),
|
| 487 |
+
flush=True,
|
| 488 |
+
)
|
| 489 |
+
|
| 490 |
+
meta = {
|
| 491 |
+
"title": "d1 × d1 21 點對戰實錄",
|
| 492 |
+
"subtitle": "兩個 LiquidAI d1-3B 決策模型,透過 llama.cpp 在同一個牌桌上對打",
|
| 493 |
+
"created_at": datetime.now(timezone.utc).astimezone().isoformat(timespec="seconds"),
|
| 494 |
+
"seed": seed,
|
| 495 |
+
"model": "LiquidAI/d1-3B-GGUF",
|
| 496 |
+
"quant": "Q8_0",
|
| 497 |
+
"llama_build": "b11786 (0.7.0-dev)",
|
| 498 |
+
"rules": RULES,
|
| 499 |
+
"start_bankroll": START_BANKROLL,
|
| 500 |
+
"agents": [
|
| 501 |
+
{
|
| 502 |
+
**a,
|
| 503 |
+
"final_bankroll": next(p for p in players if p["id"] == a["id"])["bankroll"],
|
| 504 |
+
}
|
| 505 |
+
for a in AGENTS
|
| 506 |
+
],
|
| 507 |
+
"rounds_played": len(history),
|
| 508 |
+
"duration_sec": round(time.time() - started, 2),
|
| 509 |
+
"inference": {
|
| 510 |
+
a["id"]: {
|
| 511 |
+
"calls": clients[a["id"]].calls,
|
| 512 |
+
"avg_latency_ms": round(clients[a["id"]].total_ms / max(clients[a["id"]].calls, 1)),
|
| 513 |
+
}
|
| 514 |
+
for a in AGENTS
|
| 515 |
+
},
|
| 516 |
+
}
|
| 517 |
+
data = {"meta": meta, "rounds": history}
|
| 518 |
+
out = Path(out_path)
|
| 519 |
+
out.parent.mkdir(parents=True, exist_ok=True)
|
| 520 |
+
out.write_text(json.dumps(data, ensure_ascii=False, indent=1), encoding="utf-8")
|
| 521 |
+
print(f"wrote {out} ({out.stat().st_size / 1024:.1f} KiB)")
|
| 522 |
+
return data
|
| 523 |
+
|
| 524 |
+
|
| 525 |
+
def main():
|
| 526 |
+
parser = argparse.ArgumentParser()
|
| 527 |
+
parser.add_argument("--rounds", type=int, default=12)
|
| 528 |
+
parser.add_argument("--seed", type=int, default=20261021)
|
| 529 |
+
parser.add_argument("--out", default="battle.json")
|
| 530 |
+
args = parser.parse_args()
|
| 531 |
+
run(args.rounds, args.seed, args.out)
|
| 532 |
+
|
| 533 |
+
|
| 534 |
+
if __name__ == "__main__":
|
| 535 |
+
main()
|
source/build_site.py
ADDED
|
@@ -0,0 +1,753 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Turn a battle.json produced by blackjack.py into a static website.
|
| 3 |
+
|
| 4 |
+
Usage:
|
| 5 |
+
python3 build_site.py --battle battle.json --out site
|
| 6 |
+
"""
|
| 7 |
+
import argparse
|
| 8 |
+
import json
|
| 9 |
+
import shutil
|
| 10 |
+
from datetime import datetime, timezone
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
|
| 13 |
+
ROOT = Path(__file__).resolve().parent
|
| 14 |
+
|
| 15 |
+
HTML = r"""<!doctype html>
|
| 16 |
+
<html lang="zh-Hant">
|
| 17 |
+
<head>
|
| 18 |
+
<meta charset="utf-8">
|
| 19 |
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 20 |
+
<title>__TITLE__</title>
|
| 21 |
+
<meta name="description" content="__SUBTITLE__">
|
| 22 |
+
<style>
|
| 23 |
+
:root{
|
| 24 |
+
--bg:#07120e; --bg2:#0b1c16; --panel:#0e241c; --panel2:#123026;
|
| 25 |
+
--line:rgba(255,255,255,.09); --text:#e8f3ed; --muted:#8fae9f;
|
| 26 |
+
--gold:#f6c453; --green:#39d98a; --red:#ff6b6b; --blue:#5cc8ff; --amber:#ffb020;
|
| 27 |
+
--hit:#ff6b6b; --stand:#5cc8ff; --double:#f6c453;
|
| 28 |
+
--shadow:0 18px 50px rgba(0,0,0,.45);
|
| 29 |
+
}
|
| 30 |
+
*{box-sizing:border-box}
|
| 31 |
+
html,body{margin:0;padding:0}
|
| 32 |
+
body{
|
| 33 |
+
background:
|
| 34 |
+
radial-gradient(1100px 700px at 12% -10%, rgba(57,217,138,.13), transparent 60%),
|
| 35 |
+
radial-gradient(900px 600px at 92% 0%, rgba(246,196,83,.10), transparent 55%),
|
| 36 |
+
var(--bg);
|
| 37 |
+
color:var(--text);
|
| 38 |
+
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans TC","PingFang TC","Microsoft JhengHei",sans-serif;
|
| 39 |
+
line-height:1.6; min-height:100vh;
|
| 40 |
+
}
|
| 41 |
+
a{color:var(--gold)}
|
| 42 |
+
button{font:inherit;color:inherit;cursor:pointer}
|
| 43 |
+
.topbar{
|
| 44 |
+
position:sticky;top:0;z-index:50;backdrop-filter:blur(14px);
|
| 45 |
+
background:rgba(7,18,14,.8);border-bottom:1px solid var(--line);
|
| 46 |
+
}
|
| 47 |
+
.topbar-inner{max-width:1180px;margin:0 auto;padding:12px 20px;display:flex;gap:18px;align-items:center;flex-wrap:wrap}
|
| 48 |
+
.brand{display:flex;gap:10px;align-items:center;font-weight:800;letter-spacing:.02em}
|
| 49 |
+
.brand .logo{
|
| 50 |
+
width:30px;height:30px;border-radius:9px;display:grid;place-items:center;
|
| 51 |
+
background:linear-gradient(145deg,#f6c453,#b8860b);color:#1b1200;font-weight:900;
|
| 52 |
+
}
|
| 53 |
+
nav{display:flex;gap:4px;margin-left:auto;flex-wrap:wrap}
|
| 54 |
+
nav button{
|
| 55 |
+
border:1px solid transparent;background:transparent;padding:8px 14px;border-radius:999px;
|
| 56 |
+
color:var(--muted);font-weight:600;
|
| 57 |
+
}
|
| 58 |
+
nav button:hover{color:var(--text);background:rgba(255,255,255,.05)}
|
| 59 |
+
nav button.active{color:#1b1200;background:var(--gold);border-color:var(--gold)}
|
| 60 |
+
main{max-width:1180px;margin:0 auto;padding:28px 20px 60px}
|
| 61 |
+
.view{display:none}
|
| 62 |
+
.view.active{display:block;animation:fade .25s ease}
|
| 63 |
+
@keyframes fade{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
|
| 64 |
+
.hero{padding:18px 0 6px}
|
| 65 |
+
.hero h1{font-size:clamp(30px,4vw,50px);line-height:1.15;margin:0 0 10px;letter-spacing:-.01em}
|
| 66 |
+
.hero h1 em{font-style:normal;color:var(--gold)}
|
| 67 |
+
.hero p{color:var(--muted);margin:0;max-width:760px;font-size:16px}
|
| 68 |
+
.chips{display:flex;gap:8px;flex-wrap:wrap;margin-top:18px}
|
| 69 |
+
.chip{
|
| 70 |
+
font-size:12.5px;color:#cfe4d9;background:rgba(255,255,255,.045);
|
| 71 |
+
border:1px solid var(--line);border-radius:999px;padding:5px 11px;
|
| 72 |
+
}
|
| 73 |
+
.chip b{color:var(--gold)}
|
| 74 |
+
.scoreboard{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;margin:22px 0 24px}
|
| 75 |
+
.score{
|
| 76 |
+
position:relative;overflow:hidden;border:1px solid var(--line);border-radius:18px;
|
| 77 |
+
padding:18px 18px 16px;background:linear-gradient(160deg,var(--panel2),var(--panel));
|
| 78 |
+
box-shadow:var(--shadow);
|
| 79 |
+
}
|
| 80 |
+
.score:before{content:"";position:absolute;inset:0 auto 0 0;width:4px;background:var(--accent,var(--gold))}
|
| 81 |
+
.score .id{font-size:12px;letter-spacing:.16em;text-transform:uppercase;color:var(--muted)}
|
| 82 |
+
.score .name{font-size:22px;font-weight:800;margin:2px 0 0}
|
| 83 |
+
.score .style{font-size:13px;color:var(--accent,var(--gold))}
|
| 84 |
+
.score .bank{font-size:34px;font-weight:900;letter-spacing:-.02em;margin-top:10px;font-variant-numeric:tabular-nums}
|
| 85 |
+
.score .sub{font-size:13px;color:var(--muted)}
|
| 86 |
+
.big-delta{font-weight:800}
|
| 87 |
+
.pos{color:var(--green)}.neg{color:var(--red)}.neutral{color:var(--muted)}
|
| 88 |
+
.panel{
|
| 89 |
+
border:1px solid var(--line);border-radius:20px;background:linear-gradient(180deg,rgba(255,255,255,.025),rgba(0,0,0,.12));
|
| 90 |
+
box-shadow:var(--shadow);
|
| 91 |
+
}
|
| 92 |
+
.table-panel{padding:16px}
|
| 93 |
+
.round-toolbar{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:14px}
|
| 94 |
+
.icon-btn{
|
| 95 |
+
border:1px solid var(--line);background:rgba(255,255,255,.05);border-radius:10px;
|
| 96 |
+
padding:8px 13px;font-weight:700;color:var(--text);
|
| 97 |
+
}
|
| 98 |
+
.icon-btn:hover{background:rgba(255,255,255,.1)}
|
| 99 |
+
.icon-btn:disabled{opacity:.35;cursor:not-allowed}
|
| 100 |
+
.round-pills{display:flex;gap:6px;overflow-x:auto;padding:2px;max-width:100%;scrollbar-width:thin}
|
| 101 |
+
.pill{
|
| 102 |
+
flex:0 0 auto;min-width:38px;height:34px;border-radius:9px;border:1px solid var(--line);
|
| 103 |
+
background:rgba(255,255,255,.04);font-weight:800;font-size:13px;color:var(--muted);
|
| 104 |
+
display:grid;place-items:center;padding:0 8px;
|
| 105 |
+
}
|
| 106 |
+
.pill:hover{border-color:rgba(246,196,83,.5);color:var(--text)}
|
| 107 |
+
.pill.active{background:var(--gold);border-color:var(--gold);color:#1b1200}
|
| 108 |
+
.pill .dot{width:6px;height:6px;border-radius:50%;display:block;margin:0 auto 2px}
|
| 109 |
+
.pill.win .dot{background:var(--green)}
|
| 110 |
+
.pill.lose .dot{background:var(--red)}
|
| 111 |
+
.pill.mixed .dot{background:var(--amber)}
|
| 112 |
+
.pill.push .dot{background:#93a89d}
|
| 113 |
+
.round-label{margin-left:auto;color:var(--muted);font-size:13px;font-variant-numeric:tabular-nums}
|
| 114 |
+
.round-head{display:flex;align-items:baseline;gap:12px;flex-wrap:wrap;margin-bottom:10px}
|
| 115 |
+
.round-head h2{margin:0;font-size:20px}
|
| 116 |
+
.round-head .shoe{color:var(--muted);font-size:12.5px}
|
| 117 |
+
.dealer{
|
| 118 |
+
border:1px solid rgba(246,196,83,.25);border-radius:16px;padding:14px 16px;margin-bottom:14px;
|
| 119 |
+
background:radial-gradient(700px 200px at 20% 0%,rgba(246,196,83,.1),transparent 70%),rgba(0,0,0,.18);
|
| 120 |
+
display:flex;gap:16px;align-items:center;flex-wrap:wrap;
|
| 121 |
+
}
|
| 122 |
+
.dealer .label{font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--gold);font-weight:800}
|
| 123 |
+
.dealer .total-badge{font-size:26px;font-weight:900;font-variant-numeric:tabular-nums}
|
| 124 |
+
.dealer .note{color:var(--muted);font-size:13px;flex:1;min-width:180px}
|
| 125 |
+
.players{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
|
| 126 |
+
.player{
|
| 127 |
+
border:1px solid var(--line);border-radius:16px;padding:15px;background:rgba(255,255,255,.025);
|
| 128 |
+
}
|
| 129 |
+
.player.win{border-color:rgba(57,217,138,.45);box-shadow:inset 0 0 0 1px rgba(57,217,138,.12)}
|
| 130 |
+
.player.lose{border-color:rgba(255,107,107,.35)}
|
| 131 |
+
.player.blackjack{border-color:rgba(246,196,83,.6)}
|
| 132 |
+
.p-head{display:flex;justify-content:space-between;gap:10px;align-items:flex-start;flex-wrap:wrap}
|
| 133 |
+
.p-head h3{margin:0;font-size:18px}
|
| 134 |
+
.p-head .style{font-size:12.5px;color:var(--accent,var(--gold))}
|
| 135 |
+
.p-head .bet{
|
| 136 |
+
font-size:12.5px;color:var(--muted);border:1px solid var(--line);border-radius:999px;padding:4px 10px;
|
| 137 |
+
background:rgba(0,0,0,.2);white-space:nowrap;
|
| 138 |
+
}
|
| 139 |
+
.p-head .bet b{color:var(--gold);font-size:15px}
|
| 140 |
+
.hand{display:flex;gap:14px;align-items:center;margin:12px 0 8px;flex-wrap:wrap}
|
| 141 |
+
.cards{display:flex;gap:6px;flex-wrap:wrap}
|
| 142 |
+
.card{
|
| 143 |
+
width:52px;height:72px;border-radius:8px;background:#f7f7f2;color:#141414;
|
| 144 |
+
display:flex;flex-direction:column;align-items:center;justify-content:center;
|
| 145 |
+
font-weight:900;box-shadow:0 6px 14px rgba(0,0,0,.35);border:1px solid rgba(0,0,0,.15);
|
| 146 |
+
line-height:1;user-select:none;
|
| 147 |
+
}
|
| 148 |
+
.card .r{font-size:21px}
|
| 149 |
+
.card .s{font-size:17px;margin-top:2px}
|
| 150 |
+
.card.red{color:#c0182b}
|
| 151 |
+
.card.down{background:repeating-linear-gradient(45deg,#123c6b,#123c6b 5px,#0b2a4d 5px,#0b2a4d 10px);color:transparent;border-color:#0a2340}
|
| 152 |
+
.total{
|
| 153 |
+
font-size:30px;font-weight:900;font-variant-numeric:tabular-nums;display:flex;align-items:baseline;gap:6px;
|
| 154 |
+
}
|
| 155 |
+
.total small{font-size:11px;font-weight:700;color:var(--muted);border:1px solid var(--line);border-radius:5px;padding:1px 5px}
|
| 156 |
+
.result{
|
| 157 |
+
display:inline-flex;align-items:center;gap:6px;font-size:12.5px;font-weight:800;
|
| 158 |
+
padding:3px 10px;border-radius:999px;border:1px solid var(--line);
|
| 159 |
+
}
|
| 160 |
+
.result.win{color:var(--green);border-color:rgba(57,217,138,.4);background:rgba(57,217,138,.1)}
|
| 161 |
+
.result.lose{color:var(--red);border-color:rgba(255,107,107,.4);background:rgba(255,107,107,.08)}
|
| 162 |
+
.result.push{color:#b9c9c0;background:rgba(255,255,255,.05)}
|
| 163 |
+
.result.blackjack{color:var(--gold);border-color:rgba(246,196,83,.5);background:rgba(246,196,83,.1)}
|
| 164 |
+
.bet-line{display:flex;gap:8px;align-items:center;flex-wrap:wrap;font-size:12.5px;color:var(--muted);margin:6px 0 2px}
|
| 165 |
+
.bet-line .tag{border:1px solid var(--line);border-radius:6px;padding:1px 7px;color:#cfe4d9}
|
| 166 |
+
details{margin-top:8px}
|
| 167 |
+
summary{cursor:pointer;color:var(--muted);font-size:12.5px;font-weight:700}
|
| 168 |
+
summary:hover{color:var(--text)}
|
| 169 |
+
details .body{padding:8px 0 0;color:var(--muted);font-size:13px}
|
| 170 |
+
.steps{list-style:none;margin:12px 0 0;padding:0;display:flex;flex-direction:column;gap:8px}
|
| 171 |
+
.step{
|
| 172 |
+
border:1px solid var(--line);border-radius:11px;padding:9px 11px;background:rgba(0,0,0,.2);
|
| 173 |
+
}
|
| 174 |
+
.step.enter{animation:slide .3s ease}
|
| 175 |
+
@keyframes slide{from{opacity:0;transform:translateX(-6px)}to{opacity:1;transform:none}}
|
| 176 |
+
.step-head{display:flex;gap:8px;align-items:center;flex-wrap:wrap;font-size:12.5px}
|
| 177 |
+
.step-no{color:var(--muted);font-weight:800;font-variant-numeric:tabular-nums}
|
| 178 |
+
.mini{font-weight:800;font-variant-numeric:tabular-nums}
|
| 179 |
+
.action{
|
| 180 |
+
font-weight:900;font-size:12px;letter-spacing:.04em;padding:2px 9px;border-radius:6px;color:#0b0f0d;
|
| 181 |
+
}
|
| 182 |
+
.action.hit{background:var(--hit)}.action.stand{background:var(--stand)}.action.double{background:var(--double)}
|
| 183 |
+
.action.unknown{background:#7a8a81}
|
| 184 |
+
.step .meta{margin-left:auto;color:var(--muted);font-size:11.5px;display:flex;gap:8px;flex-wrap:wrap}
|
| 185 |
+
.probs{display:grid;grid-template-columns:64px 1fr 46px;gap:6px;align-items:center;margin-top:7px;font-size:11.5px;color:var(--muted)}
|
| 186 |
+
.bar{height:7px;border-radius:4px;background:rgba(255,255,255,.07);overflow:hidden}
|
| 187 |
+
.bar>i{display:block;height:100%;border-radius:4px;background:var(--c,var(--gold));width:0;transition:width .5s ease}
|
| 188 |
+
.probs .pct{text-align:right;font-variant-numeric:tabular-nums;color:#d6e7de}
|
| 189 |
+
.step-foot{display:flex;gap:10px;flex-wrap:wrap;color:var(--muted);font-size:11.5px;margin-top:7px}
|
| 190 |
+
.step-foot b{color:#d6e7de}
|
| 191 |
+
.net{margin-top:11px;padding-top:10px;border-top:1px solid var(--line);display:flex;justify-content:space-between;align-items:center;font-size:13px}
|
| 192 |
+
.net .amt{font-size:19px;font-weight:900;font-variant-numeric:tabular-nums}
|
| 193 |
+
.events{margin-top:14px;border-top:1px dashed var(--line);padding-top:10px;color:var(--muted);font-size:12.5px}
|
| 194 |
+
.events ul{margin:6px 0 0;padding-left:18px}
|
| 195 |
+
.events li{margin:2px 0}
|
| 196 |
+
.arena-note{margin-top:14px;color:var(--muted);font-size:12px}
|
| 197 |
+
.section-title{font-size:22px;margin:26px 0 12px}
|
| 198 |
+
.stats-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
|
| 199 |
+
.stat-card{border:1px solid var(--line);border-radius:16px;padding:16px;background:rgba(255,255,255,.025)}
|
| 200 |
+
.stat-card h3{margin:0 0 4px;font-size:17px}
|
| 201 |
+
.stat-card .style{font-size:12.5px;color:var(--accent,var(--gold))}
|
| 202 |
+
.kv{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;margin-top:12px}
|
| 203 |
+
.kv .k{font-size:11.5px;color:var(--muted)}
|
| 204 |
+
.kv .v{font-size:19px;font-weight:800;font-variant-numeric:tabular-nums}
|
| 205 |
+
.dist{margin-top:12px}
|
| 206 |
+
.dist .row{display:grid;grid-template-columns:66px 1fr 44px;gap:7px;align-items:center;font-size:12px;margin:5px 0;color:var(--muted)}
|
| 207 |
+
.mini-title{font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin-top:14px}
|
| 208 |
+
.chart-wrap{margin-top:14px;border:1px solid var(--line);border-radius:16px;padding:14px;background:rgba(0,0,0,.18)}
|
| 209 |
+
.chart-legend{display:flex;gap:14px;font-size:12px;color:var(--muted);flex-wrap:wrap;margin-bottom:6px}
|
| 210 |
+
.chart-legend i{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:5px}
|
| 211 |
+
.h2h{margin-top:14px;border:1px solid var(--line);border-radius:16px;padding:16px;background:rgba(255,255,255,.025)}
|
| 212 |
+
.h2h .big{font-size:30px;font-weight:900;color:var(--gold)}
|
| 213 |
+
.code-tabs{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px}
|
| 214 |
+
.code-tabs button{border:1px solid var(--line);background:rgba(255,255,255,.04);border-radius:8px;padding:6px 12px;font-size:12.5px;color:var(--muted)}
|
| 215 |
+
.code-tabs button.active{background:var(--gold);color:#1b1200;border-color:var(--gold);font-weight:800}
|
| 216 |
+
.code-bar{display:flex;gap:10px;align-items:center;margin-bottom:8px;color:var(--muted);font-size:12.5px;flex-wrap:wrap}
|
| 217 |
+
pre.code{
|
| 218 |
+
margin:0;border:1px solid var(--line);border-radius:14px;background:#04100c;padding:16px;
|
| 219 |
+
overflow:auto;max-height:70vh;font-size:12.5px;line-height:1.55;
|
| 220 |
+
}
|
| 221 |
+
pre.code code{font-family:"SFMono-Regular",Consolas,"Liberation Mono",Menlo,monospace}
|
| 222 |
+
.data-box{border:1px solid var(--line);border-radius:16px;padding:16px;background:rgba(255,255,255,.025);margin-bottom:14px}
|
| 223 |
+
.data-box h3{margin:0 0 8px;font-size:17px}
|
| 224 |
+
.data-box p{color:var(--muted);margin:6px 0;font-size:14px}
|
| 225 |
+
.btn{
|
| 226 |
+
display:inline-flex;align-items:center;gap:7px;border:1px solid rgba(246,196,83,.45);
|
| 227 |
+
background:rgba(246,196,83,.1);color:var(--gold);padding:7px 13px;border-radius:9px;
|
| 228 |
+
text-decoration:none;font-weight:700;font-size:13px;
|
| 229 |
+
}
|
| 230 |
+
.btn:hover{background:rgba(246,196,83,.18)}
|
| 231 |
+
.btn.ghost{border-color:var(--line);background:rgba(255,255,255,.04);color:var(--text)}
|
| 232 |
+
footer{border-top:1px solid var(--line);margin-top:30px;padding:22px 20px 40px;color:var(--muted);font-size:12.5px;text-align:center}
|
| 233 |
+
.loading{padding:40px;text-align:center;color:var(--muted)}
|
| 234 |
+
.err{border:1px solid rgba(255,107,107,.4);background:rgba(255,107,107,.08);padding:16px;border-radius:12px;color:#ffd4d4}
|
| 235 |
+
@media (max-width:760px){
|
| 236 |
+
.scoreboard,.stats-grid,.players{grid-template-columns:1fr}
|
| 237 |
+
.card{width:44px;height:62px}
|
| 238 |
+
.card .r{font-size:18px}
|
| 239 |
+
.total{font-size:25px}
|
| 240 |
+
.round-label{margin-left:0}
|
| 241 |
+
}
|
| 242 |
+
</style>
|
| 243 |
+
</head>
|
| 244 |
+
<body>
|
| 245 |
+
<header class="topbar">
|
| 246 |
+
<div class="topbar-inner">
|
| 247 |
+
<div class="brand"><span class="logo">21</span><span>d1 × d1 對戰實驗室</span></div>
|
| 248 |
+
<nav>
|
| 249 |
+
<button data-view="battle" class="active">對戰</button>
|
| 250 |
+
<button data-view="stats">統計</button>
|
| 251 |
+
<button data-view="code">整理程式</button>
|
| 252 |
+
<button data-view="data">原始資料</button>
|
| 253 |
+
</nav>
|
| 254 |
+
</div>
|
| 255 |
+
</header>
|
| 256 |
+
|
| 257 |
+
<main>
|
| 258 |
+
<div id="app"><div class="loading">載入對戰資料中…</div></div>
|
| 259 |
+
</main>
|
| 260 |
+
|
| 261 |
+
<footer>
|
| 262 |
+
<div>由 llama.cpp 上的兩個 <b>LiquidAI d1-3B</b> 決策模型自動產生 · 頁面由 build_site.py 靜態編譯</div>
|
| 263 |
+
<div style="margin-top:6px">Hosted on Hugging Face Static Space · <a href="https://huggingface.co/spaces/HelloSun/d1demo">HelloSun/d1demo</a></div>
|
| 264 |
+
</footer>
|
| 265 |
+
|
| 266 |
+
<script>
|
| 267 |
+
const SUIT_RED = new Set(["♥","♦"]);
|
| 268 |
+
const LABEL = {hit:"補牌", stand:"停牌", double:"加倍", min:"最小注", standard:"標準注", max:"重注"};
|
| 269 |
+
const VALUE = {A:11,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,"10":10,J:10,Q:10,K:10};
|
| 270 |
+
const RESULT_LABEL = {win:"勝", lose:"負", push:"和局", blackjack:"Blackjack"};
|
| 271 |
+
const $ = (s,el=document)=>el.querySelector(s);
|
| 272 |
+
let DATA=null, roundIdx=0, autoTimer=null, dealerOpen=true;
|
| 273 |
+
|
| 274 |
+
function money(n){ return (n>0?"+":"") + n.toLocaleString("en-US"); }
|
| 275 |
+
function cls(n){ return n>0?"pos":(n<0?"neg":"neutral"); }
|
| 276 |
+
function pct(x){ return (Math.max(0,Math.min(1,x||0))*100).toFixed(0)+"%"; }
|
| 277 |
+
function cardHTML(card, hidden){
|
| 278 |
+
if(hidden) return '<div class="card down">?</div>';
|
| 279 |
+
const r=card.slice(0,-1), s=card.slice(-1);
|
| 280 |
+
return '<div class="card '+(SUIT_RED.has(s)?"red":"")+'"><span class="r">'+r+'</span><span class="s">'+s+'</span></div>';
|
| 281 |
+
}
|
| 282 |
+
function cardsHTML(cards, opts={}){
|
| 283 |
+
return '<div class="cards">'+cards.map((c,i)=>cardHTML(c, opts.hideIndex===i)).join("")+'</div>';
|
| 284 |
+
}
|
| 285 |
+
function agentColor(id){ return id==="a" ? "var(--green)" : "var(--blue)"; }
|
| 286 |
+
function agentMeta(id){ return DATA.meta.agents.find(a=>a.id===id) || {name:id,style:""}; }
|
| 287 |
+
|
| 288 |
+
function totalsFor(round){
|
| 289 |
+
const out={};
|
| 290 |
+
for(const p of round.players){
|
| 291 |
+
out[p.id]=out[p.id]||{bet:0,net:0};
|
| 292 |
+
out[p.id].bet+=p.bet; out[p.id].net+=p.net;
|
| 293 |
+
}
|
| 294 |
+
return out;
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
+
function init(){
|
| 298 |
+
DATA = window.BATTLE_DATA;
|
| 299 |
+
if(!DATA){ $("#app").innerHTML='<div class="err">找不到 battle.json,請用 HTTP 伺服器開啟此頁。</div>'; return; }
|
| 300 |
+
document.title = DATA.meta.title + " · d1demo";
|
| 301 |
+
$("#app").innerHTML = `
|
| 302 |
+
<section class="view active" id="view-battle">
|
| 303 |
+
<div class="hero">
|
| 304 |
+
<h1>${DATA.meta.title.split(" ")[0]} <em>×</em> d1:21 點決策對戰</h1>
|
| 305 |
+
<p>${DATA.meta.subtitle}。每一注、每一次補牌/停牌/加倍,都是模型即時輸出的機率分佈,不是寫死的策略表。</p>
|
| 306 |
+
<div class="chips" id="metaChips"></div>
|
| 307 |
+
</div>
|
| 308 |
+
<div class="scoreboard" id="scoreboard"></div>
|
| 309 |
+
<div class="panel table-panel">
|
| 310 |
+
<div class="round-toolbar">
|
| 311 |
+
<button class="icon-btn" id="prevRound">← 上一局</button>
|
| 312 |
+
<div class="round-pills" id="roundPills"></div>
|
| 313 |
+
<button class="icon-btn" id="nextRound">下一局 →</button>
|
| 314 |
+
<button class="icon-btn" id="playBtn">▶ 自動播放</button>
|
| 315 |
+
<span class="round-label" id="roundLabel"></span>
|
| 316 |
+
</div>
|
| 317 |
+
<div id="roundView"></div>
|
| 318 |
+
<div class="arena-note">莊家由固定規則操作(S17),兩個玩家各自連到一個獨立的 llama.cpp server;模型只知道牌面、剩餘牌組成、真數與資金,不知道對手的手牌。</div>
|
| 319 |
+
</div>
|
| 320 |
+
</section>
|
| 321 |
+
|
| 322 |
+
<section class="view" id="view-stats"></section>
|
| 323 |
+
<section class="view" id="view-code">
|
| 324 |
+
<h2 class="section-title">整理程式</h2>
|
| 325 |
+
<div class="data-box">
|
| 326 |
+
<p>這一頁不是手寫的靜態稿:<b>blackjack.py</b> 負責發牌、呼叫兩個 d1 server 做決策並記錄每一步;<b>build_site.py</b> 把 battle.json 編譯成你現在看到的網站。原始碼同步放在這個 Space 的 <code>source/</code> 目錄。</p>
|
| 327 |
+
</div>
|
| 328 |
+
<div class="code-tabs" id="codeTabs"></div>
|
| 329 |
+
<div class="code-bar">
|
| 330 |
+
<span id="codeName">blackjack.py</span>
|
| 331 |
+
<a class="btn ghost" id="codeDownload" href="source/blackjack.py" download>下載此檔</a>
|
| 332 |
+
<button class="icon-btn" id="copyCode">複製</button>
|
| 333 |
+
</div>
|
| 334 |
+
<pre class="code"><code id="codeBody">載入中…</code></pre>
|
| 335 |
+
</section>
|
| 336 |
+
|
| 337 |
+
<section class="view" id="view-data">
|
| 338 |
+
<h2 class="section-title">原始資料</h2>
|
| 339 |
+
<div class="data-box">
|
| 340 |
+
<h3>battle.json</h3>
|
| 341 |
+
<p>完整對戰紀錄:每一局的牌、注碼、決策機率、信心度、風險評分、延遲、莊家補牌與資金變化。</p>
|
| 342 |
+
<p><a class="btn" href="battle.json" download>下載 battle.json</a>
|
| 343 |
+
<a class="btn ghost" href="battle.json" target="_blank" rel="noopener">在瀏覽器開啟</a></p>
|
| 344 |
+
</div>
|
| 345 |
+
<div class="data-box">
|
| 346 |
+
<h3>重現方式</h3>
|
| 347 |
+
<p>需要 llama.cpp b11786 以上版本、LiquidAI/d1-3B-GGUF(Q8_0)與 Python 3。</p>
|
| 348 |
+
<pre class="code"><code id="repro"></code></pre>
|
| 349 |
+
</div>
|
| 350 |
+
<div class="data-box">
|
| 351 |
+
<h3>本次執行摘要</h3>
|
| 352 |
+
<pre class="code"><code id="metaJson"></code></pre>
|
| 353 |
+
</div>
|
| 354 |
+
</section>
|
| 355 |
+
<footer style="border:0;padding-bottom:0"></footer>
|
| 356 |
+
`;
|
| 357 |
+
renderChips();
|
| 358 |
+
renderScoreboard();
|
| 359 |
+
renderPills();
|
| 360 |
+
renderRound();
|
| 361 |
+
renderStats();
|
| 362 |
+
renderCodeTabs();
|
| 363 |
+
bind();
|
| 364 |
+
}
|
| 365 |
+
|
| 366 |
+
function renderChips(){
|
| 367 |
+
const m=DATA.meta;
|
| 368 |
+
const chips=[
|
| 369 |
+
["模型",m.model],["量化",m.quant],["llama.cpp",m.llama_build],
|
| 370 |
+
["局數",m.rounds_played],["隨機種子",m.seed],["耗時",m.duration_sec+"s"],
|
| 371 |
+
["莊家","S17"],["BJ 賠率","3:2"]
|
| 372 |
+
];
|
| 373 |
+
$("#metaChips").innerHTML=chips.map(([k,v])=>`<span class="chip">${k} <b>${v}</b></span>`).join("");
|
| 374 |
+
}
|
| 375 |
+
|
| 376 |
+
function renderScoreboard(){
|
| 377 |
+
const agents=DATA.meta.agents;
|
| 378 |
+
$("#scoreboard").innerHTML=agents.map(a=>{
|
| 379 |
+
const last=DATA.rounds[DATA.rounds.length-1];
|
| 380 |
+
const p=last.players.find(x=>x.id===a.id);
|
| 381 |
+
const profit=a.final_bankroll-DATA.meta.start_bankroll;
|
| 382 |
+
const color=agentColor(a.id);
|
| 383 |
+
return `<div class="score" style="--accent:${color}">
|
| 384 |
+
<div class="id">PLAYER ${a.id.toUpperCase()}</div>
|
| 385 |
+
<div class="name">${a.name}</div>
|
| 386 |
+
<div class="style">${a.style}</div>
|
| 387 |
+
<div class="bank" style="color:${color}">${a.final_bankroll.toLocaleString("en-US")}</div>
|
| 388 |
+
<div class="sub">起始 ${DATA.meta.start_bankroll} · 損益 <span class="big-delta ${cls(profit)}">${money(profit)}</span></div>
|
| 389 |
+
<div class="sub">最終一局:${p?(RESULT_LABEL[p.result]||p.result):""} ${p?money(p.net):""}</div>
|
| 390 |
+
</div>`;
|
| 391 |
+
}).join("");
|
| 392 |
+
}
|
| 393 |
+
|
| 394 |
+
function roundTone(r){
|
| 395 |
+
const res=r.players.map(p=>p.result);
|
| 396 |
+
const wins=res.filter(x=>x==="win"||x==="blackjack").length;
|
| 397 |
+
const loses=res.filter(x=>x==="lose").length;
|
| 398 |
+
if(wins===res.length) return "win";
|
| 399 |
+
if(loses===res.length) return "lose";
|
| 400 |
+
if(res.every(x=>x==="push")) return "push";
|
| 401 |
+
return "mixed";
|
| 402 |
+
}
|
| 403 |
+
|
| 404 |
+
function renderPills(){
|
| 405 |
+
$("#roundPills").innerHTML=DATA.rounds.map((r,i)=>{
|
| 406 |
+
return `<button class="pill ${roundTone(r)} ${i===roundIdx?"active":""}" data-round="${i}" title="第 ${r.n} 局">
|
| 407 |
+
<span class="dot"></span>${r.n}</button>`;
|
| 408 |
+
}).join("");
|
| 409 |
+
$("#roundPills").querySelectorAll(".pill").forEach(b=>b.onclick=()=>{stopAuto();roundIdx=+b.dataset.round;dealerOpen=true;renderRound();renderPills();});
|
| 410 |
+
}
|
| 411 |
+
|
| 412 |
+
function stepHTML(step, i){
|
| 413 |
+
const d=step.decision||{};
|
| 414 |
+
const probs=d.probabilities||{};
|
| 415 |
+
const keys=Object.keys(probs).sort((a,b)=>probs[b]-probs[a]);
|
| 416 |
+
const risk=step.risk;
|
| 417 |
+
const riskText=risk ? `風險評分 <b>${risk.score.toFixed(2)}</b>/3 · 信心 ${pct(risk.confidence)}` : "";
|
| 418 |
+
const drawn=step.drawn ? `補到 <b>${step.drawn}</b> → <b>${step.total_after}</b> 點` : (d.choice==="stand"?"維持 <b>"+step.total+"</b> 點":`→ <b>${step.total_after}</b> 點`);
|
| 419 |
+
return `<li class="step">
|
| 420 |
+
<div class="step-head">
|
| 421 |
+
<span class="step-no">#${i+1}</span>
|
| 422 |
+
<span class="mini">${step.cards.join(" ")}</span>
|
| 423 |
+
<span class="mini" style="color:var(--muted)">(${step.total}${step.soft?" 軟":""})</span>
|
| 424 |
+
<span class="action ${LABEL[d.choice]?d.choice:"unknown"}">${LABEL[d.choice]||d.choice||"?"}</span>
|
| 425 |
+
<span class="meta">信心 ${pct(d.confidence)} · ${((step.latency_ms||0)/1000).toFixed(2)}s</span>
|
| 426 |
+
</div>
|
| 427 |
+
<div class="probs">
|
| 428 |
+
${keys.map(k=>`<span>${LABEL[k]||k}</span><span class="bar"><i style="--c:var(--${k==="hit"?"hit":k==="stand"?"stand":"double"});width:${(probs[k]*100).toFixed(1)}%"></i></span><span class="pct">${pct(probs[k])}</span>`).join("")}
|
| 429 |
+
</div>
|
| 430 |
+
<div class="step-foot"><span>${drawn}</span>${riskText?`<span>${riskText}</span>`:""}${d.fallback?`<span style="color:var(--red)">伺服器無回應,改用規則備援</span>`:""}</div>
|
| 431 |
+
</li>`;
|
| 432 |
+
}
|
| 433 |
+
|
| 434 |
+
function playerHTML(p){
|
| 435 |
+
const steps=(p.steps||[]).map(stepHTML).join("");
|
| 436 |
+
const bd=p.bet_decision||{};
|
| 437 |
+
const betDecision=bd.decision||{};
|
| 438 |
+
const betProbs=betDecision.probabilities||{};
|
| 439 |
+
const betLabel=LABEL[betDecision.choice]||betDecision.choice||"";
|
| 440 |
+
return `<article class="player ${p.result} ${p.blackjack?"blackjack":""}" style="--accent:${agentColor(p.id)}">
|
| 441 |
+
<div class="p-head">
|
| 442 |
+
<div><h3>${p.name}</h3><div class="style">${p.style||""}</div></div>
|
| 443 |
+
<div class="bet">注碼 <b>${p.bet}</b>${p.doubled?" · 已加倍":""}</div>
|
| 444 |
+
</div>
|
| 445 |
+
<div class="hand">
|
| 446 |
+
${cardsHTML(p.cards)}
|
| 447 |
+
<div class="total">${p.total}<small>${p.bust?"BUST":p.soft?"軟牌":p.blackjack?"BJ":"硬牌"}</small></div>
|
| 448 |
+
<span class="result ${p.result}">${RESULT_LABEL[p.result]||p.result}</span>
|
| 449 |
+
</div>
|
| 450 |
+
<div class="bet-line">
|
| 451 |
+
<span class="tag">注碼決策 ${betLabel}</span>
|
| 452 |
+
${p.rebuy?`<span class="tag" style="color:var(--amber)">補充籌碼 +${p.rebuy}</span>`:""}
|
| 453 |
+
<span>信心 ${pct(betDecision.confidence)}</span>
|
| 454 |
+
${Object.keys(betProbs).length?`<span>${Object.keys(betProbs).sort((a,b)=>betProbs[b]-betProbs[a]).map(k=>`${LABEL[k]||k} ${pct(betProbs[k])}`).join(" · ")}</span>`:""}
|
| 455 |
+
</div>
|
| 456 |
+
<details><summary>看模型怎麼決定注碼</summary>
|
| 457 |
+
<div class="body">狀態包含資金、剩余牌、Hi-Lo 真數與規則;模型在 min / standard / max 三種注碼中挑一個,並另外給出信心評分。</div>
|
| 458 |
+
</details>
|
| 459 |
+
${steps?`<ol class="steps">${steps}</ol>`:`<div class="step-foot" style="margin-top:12px">莊家直接 Blackjack,玩家沒有動作機會。</div>`}
|
| 460 |
+
<div class="net"><span>本局結果</span><span class="amt ${cls(p.net)}">${money(p.net)} <small style="font-size:12px;color:var(--muted)">→ ${p.bankroll_after}</small></span></div>
|
| 461 |
+
</article>`;
|
| 462 |
+
}
|
| 463 |
+
|
| 464 |
+
function renderRound(){
|
| 465 |
+
const r=DATA.rounds[roundIdx];
|
| 466 |
+
if(!r){ return; }
|
| 467 |
+
const d=r.dealer||{};
|
| 468 |
+
const hideHole=!dealerOpen && r.dealer_revealed;
|
| 469 |
+
const dealerCards=(d.cards||[]).map((c,i)=>cardHTML(c, hideHole && i===1)).join("");
|
| 470 |
+
const totals=totalsFor(r);
|
| 471 |
+
const pillTone=roundTone(r);
|
| 472 |
+
$("#roundLabel").textContent=`第 ${r.n} / ${DATA.rounds.length} 局 · 剩餘 ${r.shoe_before.remaining_cards} 張 · 真數 ${r.shoe_before.true_count}`;
|
| 473 |
+
$("#roundView").innerHTML=`
|
| 474 |
+
<div class="round-head">
|
| 475 |
+
<h2>第 ${r.n} 局</h2>
|
| 476 |
+
<span class="shoe">洗牌前剩餘 ${r.shoe_before.remaining_cards} 張(穿透率 ${r.shoe_before.penetration_pct}%) · Hi-Lo 真數 ${r.shoe_before.true_count}</span>
|
| 477 |
+
</div>
|
| 478 |
+
<div class="dealer">
|
| 479 |
+
<div>
|
| 480 |
+
<div class="label">DEALER · 莊家</div>
|
| 481 |
+
<div class="total-badge">${hideHole?"?":d.total}${d.bust?' <span style="font-size:12px;color:var(--red)">BUST</span>':""}${d.blackjack?' <span style="font-size:12px;color:var(--gold)">BJ</span>':""}</div>
|
| 482 |
+
</div>
|
| 483 |
+
<div class="cards">${dealerCards}</div>
|
| 484 |
+
<div class="note">${r.events.join(" · ")||"莊家依規則補牌"}</div>
|
| 485 |
+
${r.dealer_revealed?`<button class="icon-btn" id="revealBtn">${dealerOpen?"蓋回暗牌":"揭開暗牌"}</button>`:""}
|
| 486 |
+
</div>
|
| 487 |
+
<div class="players">${r.players.map(playerHTML).join("")}</div>
|
| 488 |
+
<div class="events"><b>牌桌事件</b><ul>${r.events.map(e=>`<li>${e}</li>`).join("")}</ul></div>
|
| 489 |
+
`;
|
| 490 |
+
const rb=$("#revealBtn");
|
| 491 |
+
if(rb) rb.onclick=()=>{dealerOpen=!dealerOpen;renderRound();};
|
| 492 |
+
$("#prevRound").disabled=roundIdx===0;
|
| 493 |
+
$("#nextRound").disabled=roundIdx===DATA.rounds.length-1;
|
| 494 |
+
document.querySelectorAll(".pill").forEach((b,i)=>b.classList.toggle("active",i===roundIdx));
|
| 495 |
+
}
|
| 496 |
+
|
| 497 |
+
function bind(){
|
| 498 |
+
$("#prevRound").onclick=()=>{stopAuto(); if(roundIdx>0){roundIdx--;dealerOpen=true;renderRound();}};
|
| 499 |
+
$("#nextRound").onclick=()=>{stopAuto(); if(roundIdx<DATA.rounds.length-1){roundIdx++;dealerOpen=true;renderRound();}};
|
| 500 |
+
$("#playBtn").onclick=()=>{
|
| 501 |
+
if(autoTimer){stopAuto();return;}
|
| 502 |
+
roundIdx=0;dealerOpen=true;renderRound();
|
| 503 |
+
$("#playBtn").textContent="⏸ 暫停";
|
| 504 |
+
autoTimer=setInterval(()=>{
|
| 505 |
+
if(roundIdx>=DATA.rounds.length-1){stopAuto();return;}
|
| 506 |
+
roundIdx++;dealerOpen=true;renderRound();
|
| 507 |
+
},4000);
|
| 508 |
+
};
|
| 509 |
+
document.querySelectorAll("nav button").forEach(b=>{
|
| 510 |
+
b.onclick=()=>{
|
| 511 |
+
document.querySelectorAll("nav button").forEach(x=>x.classList.toggle("active",x===b));
|
| 512 |
+
document.querySelectorAll(".view").forEach(v=>v.classList.toggle("active",v.id==="view-"+b.dataset.view));
|
| 513 |
+
window.scrollTo({top:0,behavior:"smooth"});
|
| 514 |
+
};
|
| 515 |
+
});
|
| 516 |
+
}
|
| 517 |
+
function stopAuto(){ if(autoTimer){clearInterval(autoTimer);autoTimer=null;} const b=$("#playBtn"); if(b)b.textContent="▶ 自動播放"; }
|
| 518 |
+
|
| 519 |
+
function statsFor(id){
|
| 520 |
+
const s={rounds:0,win:0,lose:0,push:0,bj:0,bust:0,double:0,net:0,wagered:0,steps:0,conf:0,lat:0,actions:{hit:0,stand:0,double:0},firstActions:[]};
|
| 521 |
+
for(const r of DATA.rounds){
|
| 522 |
+
const p=r.players.find(x=>x.id===id); if(!p) continue;
|
| 523 |
+
s.rounds++;
|
| 524 |
+
if(p.result==="win")s.win++;
|
| 525 |
+
else if(p.result==="lose")s.lose++;
|
| 526 |
+
else if(p.result==="push")s.push++;
|
| 527 |
+
else if(p.result==="blackjack")s.win++;
|
| 528 |
+
if(p.blackjack)s.bj++;
|
| 529 |
+
if(p.bust)s.bust++;
|
| 530 |
+
if(p.doubled)s.double++;
|
| 531 |
+
s.net+=p.net; s.wagered+=p.bet;
|
| 532 |
+
(p.steps||[]).forEach(st=>{
|
| 533 |
+
s.steps++; s.conf+=st.decision.confidence||0; s.lat+=st.latency_ms||0;
|
| 534 |
+
s.actions[st.decision.choice]=(s.actions[st.decision.choice]||0)+1;
|
| 535 |
+
});
|
| 536 |
+
const p1=(p.steps||[])[0];
|
| 537 |
+
if(p1) s.firstActions.push({round:r.n,action:p1.decision.choice});
|
| 538 |
+
}
|
| 539 |
+
s.avgConf=s.steps?s.conf/s.steps:0;
|
| 540 |
+
s.avgLat=s.steps?s.lat/s.steps:0;
|
| 541 |
+
return s;
|
| 542 |
+
}
|
| 543 |
+
|
| 544 |
+
function renderStats(){
|
| 545 |
+
const a=statsFor("a"), b=statsFor("b");
|
| 546 |
+
const agree=a.firstActions.filter(x=>{
|
| 547 |
+
const y=b.firstActions.find(z=>z.round===x.round);
|
| 548 |
+
return y && y.action===x.action;
|
| 549 |
+
}).length;
|
| 550 |
+
const compared=a.firstActions.filter(x=>b.firstActions.some(z=>z.round===x.round)).length;
|
| 551 |
+
const dealerBust=DATA.rounds.filter(r=>r.dealer&&r.dealer.bust).length;
|
| 552 |
+
const dealerBj=DATA.rounds.filter(r=>r.dealer&&r.dealer.blackjack).length;
|
| 553 |
+
const dealerAvg=DATA.rounds.reduce((sum,r)=>sum+((r.dealer||{}).total||0),0)/Math.max(DATA.rounds.length,1);
|
| 554 |
+
const totalNet=a.net+b.net;
|
| 555 |
+
const houseEdge=totalNet;
|
| 556 |
+
|
| 557 |
+
function statCard(id,s){
|
| 558 |
+
const m=agentMeta(id);
|
| 559 |
+
const totalActions=Math.max(s.steps,1);
|
| 560 |
+
return `<div class="stat-card" style="--accent:${agentColor(id)}">
|
| 561 |
+
<h3>${m.name}</h3><div class="style">${m.style}</div>
|
| 562 |
+
<div class="kv">
|
| 563 |
+
<div><div class="k">損益</div><div class="v ${cls(s.net)}">${money(s.net)}</div></div>
|
| 564 |
+
<div><div class="k">勝 / 負 / 和</div><div class="v">${s.win} / ${s.lose} / ${s.push}</div></div>
|
| 565 |
+
<div><div class="k">Blackjack</div><div class="v">${s.bj}</div></div>
|
| 566 |
+
<div><div class="k">爆牌</div><div class="v">${s.bust}</div></div>
|
| 567 |
+
<div><div class="k">加倍次數</div><div class="v">${s.double}</div></div>
|
| 568 |
+
<div><div class="k">總下注</div><div class="v">${s.wagered}</div></div>
|
| 569 |
+
<div><div class="k">平均信心</div><div class="v">${pct(s.avgConf)}</div></div>
|
| 570 |
+
<div><div class="k">平均推理</div><div class="v">${(s.avgLat/1000).toFixed(2)}s</div></div>
|
| 571 |
+
</div>
|
| 572 |
+
<div class="dist">
|
| 573 |
+
<div class="mini-title">動作分佈(${s.steps} 次)</div>
|
| 574 |
+
${["hit","stand","double"].map(k=>`<div class="row"><span>${LABEL[k]}</span><span class="bar"><i style="--c:var(--${k});width:${(100*s.actions[k]/totalActions).toFixed(1)}%"></i></span><span class="pct">${s.actions[k]}</span></div>`).join("")}
|
| 575 |
+
</div>
|
| 576 |
+
</div>`;
|
| 577 |
+
}
|
| 578 |
+
|
| 579 |
+
const chart=bankrollChart();
|
| 580 |
+
$("#view-stats").innerHTML=`
|
| 581 |
+
<h2 class="section-title">決策統計</h2>
|
| 582 |
+
<div class="stats-grid">${statCard("a",a)}${statCard("b",b)}</div>
|
| 583 |
+
<div class="chart-wrap">
|
| 584 |
+
<div class="chart-legend">
|
| 585 |
+
<span><i style="background:var(--green)"></i>${agentMeta("a").name}</span>
|
| 586 |
+
<span><i style="background:var(--blue)"></i>${agentMeta("b").name}</span>
|
| 587 |
+
<span>起始資金 ${DATA.meta.start_bankroll}</span>
|
| 588 |
+
</div>
|
| 589 |
+
${chart}
|
| 590 |
+
</div>
|
| 591 |
+
<div class="h2h">
|
| 592 |
+
<h3 style="margin:0 0 6px">兩個 d1 有多像?</h3>
|
| 593 |
+
<p style="color:var(--muted);margin:4px 0 10px">比較每一局雙方第一次動作(同一副牌、同一個莊家明牌、相同的剩餘牌資訊)。</p>
|
| 594 |
+
<div class="big">${compared?Math.round(100*agree/compared):0}%</div>
|
| 595 |
+
<div style="color:var(--muted);font-size:13px">首個動作一致率 · 共比較 ${compared} 局</div>
|
| 596 |
+
<div class="kv" style="grid-template-columns:repeat(3,1fr);margin-top:16px">
|
| 597 |
+
<div><div class="k">莊家爆牌率</div><div class="v">${Math.round(100*dealerBust/DATA.rounds.length)}%</div></div>
|
| 598 |
+
<div><div class="k">莊家 Blackjack</div><div class="v">${dealerBj} 局</div></div>
|
| 599 |
+
<div><div class="k">莊家平均點數</div><div class="v">${dealerAvg.toFixed(1)}</div></div>
|
| 600 |
+
</div>
|
| 601 |
+
</div>
|
| 602 |
+
<div class="data-box" style="margin-top:14px">
|
| 603 |
+
<h3>兩個模型合計對抗莊家</h3>
|
| 604 |
+
<p style="font-size:15px">總損益 <b class="${cls(totalNet)}" style="font-size:24px">${money(totalNet)}</b> · 每位玩家起始 ${DATA.meta.start_bankroll},合計起始 ${DATA.meta.start_bankroll*2}</p>
|
| 605 |
+
</div>
|
| 606 |
+
`;
|
| 607 |
+
}
|
| 608 |
+
|
| 609 |
+
function bankrollChart(){
|
| 610 |
+
const W=760,H=230,pad={l:52,r:14,t:14,b:26};
|
| 611 |
+
const agents=DATA.meta.agents;
|
| 612 |
+
const series=agents.map(a=>{
|
| 613 |
+
const vals=[DATA.meta.start_bankroll];
|
| 614 |
+
for(const r of DATA.rounds){
|
| 615 |
+
const p=r.players.find(x=>x.id===a.id);
|
| 616 |
+
vals.push(p?p.bankroll_after:DATA.meta.start_bankroll);
|
| 617 |
+
}
|
| 618 |
+
return {a,vals,color:a.id==="a"?"#39d98a":"#5cc8ff"};
|
| 619 |
+
});
|
| 620 |
+
const all=series.flatMap(s=>s.vals);
|
| 621 |
+
let min=Math.min(...all),max=Math.max(...all);
|
| 622 |
+
const span=Math.max(max-min,100);
|
| 623 |
+
min=Math.floor((min-span*0.1)/50)*50; max=Math.ceil((max+span*0.1)/50)*50;
|
| 624 |
+
const X=i=>pad.l+(W-pad.l-pad.r)*(i/(DATA.rounds.length||1));
|
| 625 |
+
const Y=v=>H-pad.b-(H-pad.t-pad.b)*((v-min)/(max-min||1));
|
| 626 |
+
const step=Math.max(50,Math.ceil((max-min)/4/50)*50);
|
| 627 |
+
min=Math.floor(min/step)*step; max=Math.ceil(max/step)*step;
|
| 628 |
+
const ticks=Math.round((max-min)/step);
|
| 629 |
+
let grid="";
|
| 630 |
+
for(let i=0;i<=ticks;i++){
|
| 631 |
+
const v=min+(max-min)*i/ticks, y=Y(v);
|
| 632 |
+
grid+=`<line x1="${pad.l}" y1="${y}" x2="${W-pad.r}" y2="${y}" stroke="rgba(255,255,255,.07)"/>`;
|
| 633 |
+
grid+=`<text x="${pad.l-8}" y="${y+4}" fill="#8fae9f" font-size="10" text-anchor="end">${Math.round(v)}</text>`;
|
| 634 |
+
}
|
| 635 |
+
const paths=series.map(s=>{
|
| 636 |
+
const d=s.vals.map((v,i)=>`${i?"L":"M"}${X(i).toFixed(1)},${Y(v).toFixed(1)}`).join(" ");
|
| 637 |
+
const dots=s.vals.map((v,i)=>`<circle cx="${X(i)}" cy="${Y(v)}" r="2.6" fill="${s.color}"><title>第 ${i} 局後:${v}</title></circle>`).join("");
|
| 638 |
+
return `<path d="${d}" fill="none" stroke="${s.color}" stroke-width="2.4" stroke-linejoin="round"/>${dots}`;
|
| 639 |
+
}).join("");
|
| 640 |
+
return `<svg viewBox="0 0 ${W} ${H}" width="100%" role="img" aria-label="資金曲線">${grid}${paths}</svg>`;
|
| 641 |
+
}
|
| 642 |
+
|
| 643 |
+
const CODE_FILES=[
|
| 644 |
+
{file:"blackjack.py",label:"blackjack.py · 對戰與記錄"},
|
| 645 |
+
{file:"build_site.py",label:"build_site.py · 網頁編譯"},
|
| 646 |
+
{file:"run_demo.sh",label:"run_demo.sh · 一鍵執行"},
|
| 647 |
+
];
|
| 648 |
+
function renderCodeTabs(){
|
| 649 |
+
$("#codeTabs").innerHTML=CODE_FILES.map((f,i)=>`<button data-file="${f.file}" class="${i===0?"active":""}">${f.label}</button>`).join("");
|
| 650 |
+
$("#codeTabs").querySelectorAll("button").forEach(b=>b.onclick=()=>{
|
| 651 |
+
$("#codeTabs").querySelectorAll("button").forEach(x=>x.classList.toggle("active",x===b));
|
| 652 |
+
loadCode(b.dataset.file);
|
| 653 |
+
});
|
| 654 |
+
loadCode("blackjack.py");
|
| 655 |
+
$("#copyCode").onclick=async()=>{
|
| 656 |
+
try{await navigator.clipboard.writeText($("#codeBody").textContent);$("#copyCode").textContent="已複製";setTimeout(()=>$("#copyCode").textContent="複製",1200);}catch(e){}
|
| 657 |
+
};
|
| 658 |
+
$("#repro").textContent=`# 1) 下載模型與 llama.cpp(CPU 版)
|
| 659 |
+
llama-server -m d1-3B-Q8_0.gguf --port 8081 -c 2048 -np 1 --no-webui &
|
| 660 |
+
llama-server -m d1-3B-Q8_0.gguf --port 8082 -c 2048 -np 1 --no-webui &
|
| 661 |
+
|
| 662 |
+
# 2) 讓兩個 d1 打 12 局,並把每一步寫成 JSON
|
| 663 |
+
python3 blackjack.py --rounds 12 --seed ${DATA.meta.seed} --out battle.json
|
| 664 |
+
|
| 665 |
+
# 3) 把 JSON 編譯成這個靜態網站
|
| 666 |
+
python3 build_site.py --battle battle.json --out site
|
| 667 |
+
|
| 668 |
+
# 4) 上傳 static Space
|
| 669 |
+
python3 - <<'PY'
|
| 670 |
+
from huggingface_hub import HfApi
|
| 671 |
+
HfApi().upload_folder(folder_path="site", repo_id="HelloSun/d1demo", repo_type="space")
|
| 672 |
+
PY`;
|
| 673 |
+
$("#metaJson").textContent=JSON.stringify(DATA.meta,null,2);
|
| 674 |
+
}
|
| 675 |
+
function loadCode(file){
|
| 676 |
+
$("#codeName").textContent=file;
|
| 677 |
+
$("#codeDownload").href="source/"+file;
|
| 678 |
+
$("#codeBody").textContent="載入中…";
|
| 679 |
+
fetch("source/"+file).then(r=>{if(!r.ok)throw new Error(r.status);return r.text();})
|
| 680 |
+
.then(t=>{$("#codeBody").textContent=t;})
|
| 681 |
+
.catch(()=>{$("#codeBody").textContent="(無法載入 "+file+",請確認 source/ 目錄存在)";});
|
| 682 |
+
}
|
| 683 |
+
|
| 684 |
+
init();
|
| 685 |
+
</script>
|
| 686 |
+
</body>
|
| 687 |
+
</html>
|
| 688 |
+
"""
|
| 689 |
+
|
| 690 |
+
README = """---
|
| 691 |
+
title: d1demo
|
| 692 |
+
emoji: 🃏
|
| 693 |
+
colorFrom: green
|
| 694 |
+
colorTo: yellow
|
| 695 |
+
sdk: static
|
| 696 |
+
pinned: false
|
| 697 |
+
license: mit
|
| 698 |
+
---
|
| 699 |
+
|
| 700 |
+
# d1 × d1 21 點對戰實錄
|
| 701 |
+
|
| 702 |
+
兩個 **LiquidAI d1-3B** 決策模型,各自跑在一個 llama.cpp server 上,在同一張 21 點牌桌上對打。每一次下注、補牌、停牌、加倍,都是模型輸出的機率分佈;這頁把每一步整理成可回放的視覺化紀錄。
|
| 703 |
+
|
| 704 |
+
- 模型:`LiquidAI/d1-3B-GGUF` (Q8_0),llama.cpp b11786
|
| 705 |
+
- API:`POST /v1/systemone`(d1 是 decision model,不是文字生成模型)
|
| 706 |
+
- 莊家:規則操作,S17,6 副牌,BJ 3:2
|
| 707 |
+
- 內容:對戰時間軸、決策機率、風險評分、信心度、延遲、資金曲線、動作統計
|
| 708 |
+
- 原始碼:`source/blackjack.py`(對戰 + 記錄)、`source/build_site.py`(靜態網頁編譯)
|
| 709 |
+
- 原始資料:`battle.json`
|
| 710 |
+
|
| 711 |
+
頁面是靜態的,所有資料都在 `battle.json`;`build_site.py` 可重新編譯整站。
|
| 712 |
+
"""
|
| 713 |
+
|
| 714 |
+
|
| 715 |
+
def build(battle_path, out_dir):
|
| 716 |
+
data = json.loads(Path(battle_path).read_text(encoding="utf-8"))
|
| 717 |
+
out = Path(out_dir)
|
| 718 |
+
out.mkdir(parents=True, exist_ok=True)
|
| 719 |
+
(out / "source").mkdir(exist_ok=True)
|
| 720 |
+
|
| 721 |
+
(out / "battle.json").write_text(
|
| 722 |
+
json.dumps(data, ensure_ascii=False, indent=1), encoding="utf-8"
|
| 723 |
+
)
|
| 724 |
+
shutil.copy(battle_path, out / "battle.json")
|
| 725 |
+
|
| 726 |
+
for name in ("blackjack.py", "build_site.py", "run_demo.sh"):
|
| 727 |
+
src = ROOT / name
|
| 728 |
+
if src.exists():
|
| 729 |
+
shutil.copy(src, out / "source" / name)
|
| 730 |
+
|
| 731 |
+
inline = "window.BATTLE_DATA = " + json.dumps(data, ensure_ascii=False) + ";"
|
| 732 |
+
html = HTML.replace("__TITLE__", data["meta"]["title"]).replace(
|
| 733 |
+
"__SUBTITLE__", data["meta"]["subtitle"]
|
| 734 |
+
)
|
| 735 |
+
html = html.replace('<script>\nconst SUIT_RED', '<script>\n' + inline + '\nconst SUIT_RED')
|
| 736 |
+
(out / "index.html").write_text(html, encoding="utf-8")
|
| 737 |
+
(out / "README.md").write_text(README, encoding="utf-8")
|
| 738 |
+
|
| 739 |
+
stamp = datetime.now(timezone.utc).astimezone().isoformat(timespec="seconds")
|
| 740 |
+
print(f"[{stamp}] built {out/'index.html'} ({len(html)/1024:.1f} KiB), "
|
| 741 |
+
f"battle.json {len(json.dumps(data, ensure_ascii=False).encode())/1024:.1f} KiB")
|
| 742 |
+
|
| 743 |
+
|
| 744 |
+
def main():
|
| 745 |
+
ap = argparse.ArgumentParser()
|
| 746 |
+
ap.add_argument("--battle", default="battle.json")
|
| 747 |
+
ap.add_argument("--out", default="site")
|
| 748 |
+
args = ap.parse_args()
|
| 749 |
+
build(args.battle, args.out)
|
| 750 |
+
|
| 751 |
+
|
| 752 |
+
if __name__ == "__main__":
|
| 753 |
+
main()
|
source/run_demo.sh
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# One-shot demo: two d1 servers -> blackjack battle -> static site.
|
| 3 |
+
set -euo pipefail
|
| 4 |
+
cd "$(dirname "$0")"
|
| 5 |
+
|
| 6 |
+
MODEL="${MODEL:-models/d1-3B-Q8_0.gguf}"
|
| 7 |
+
ROUNDS="${ROUNDS:-12}"
|
| 8 |
+
SEED="${SEED:-20261021}"
|
| 9 |
+
LLAMA="${LLAMA:-./llama/llama-server}"
|
| 10 |
+
|
| 11 |
+
wait_health() {
|
| 12 |
+
local port="$1"
|
| 13 |
+
for _ in $(seq 1 120); do
|
| 14 |
+
if curl -sf "http://127.0.0.1:${port}/health" >/dev/null 2>&1; then
|
| 15 |
+
return 0
|
| 16 |
+
fi
|
| 17 |
+
sleep 0.5
|
| 18 |
+
done
|
| 19 |
+
echo "server on :${port} did not become healthy" >&2
|
| 20 |
+
return 1
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
start_server() {
|
| 24 |
+
local port="$1" log="$2"
|
| 25 |
+
if curl -sf "http://127.0.0.1:${port}/health" >/dev/null 2>&1; then
|
| 26 |
+
echo "reuse existing server on :${port}"
|
| 27 |
+
return
|
| 28 |
+
fi
|
| 29 |
+
setsid nohup "$LLAMA" -m "$MODEL" --host 127.0.0.1 --port "$port" \
|
| 30 |
+
-t "${THREADS:-8}" -c 2048 -np 1 --no-webui </dev/null >"$log" 2>&1 &
|
| 31 |
+
echo $! >"/tmp/d1demo_${port}.pid"
|
| 32 |
+
wait_health "$port"
|
| 33 |
+
echo "d1 server ready on :${port}"
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
start_server 8081 logs_server1.txt
|
| 37 |
+
start_server 8082 logs_server2.txt
|
| 38 |
+
|
| 39 |
+
python3 blackjack.py --rounds "$ROUNDS" --seed "$SEED" --out battle.json
|
| 40 |
+
python3 build_site.py --battle battle.json --out site
|
| 41 |
+
|
| 42 |
+
echo "site ready: $(pwd)/site/index.html"
|