Fix info leak in web game + add handover notes
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
"""Python 引擎冒煙測試(不連網、不呼叫真實 LLM)。
|
||||
|
||||
以「模擬 LLM」取代 API 呼叫,跑完整場騙子酒館遊戲,驗證
|
||||
game.py / player.py / game_record.py 主流程可正常完成並產生記錄。
|
||||
使用方式:
|
||||
.venv\\Scripts\\python.exe scripts\\smoke_test_python.py
|
||||
(若主控台遇到簡體中文編碼錯誤,請先設定 $env:PYTHONIOENCODING="utf-8")
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import random
|
||||
|
||||
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, REPO_ROOT)
|
||||
|
||||
from game import Game
|
||||
|
||||
|
||||
class MockChat:
|
||||
"""附著在某位玩家身上的假 LLM:依提示內容回傳格式正確的 JSON。
|
||||
注意:chat() 統一回傳 (content, reasoning_content) 二元素 tuple。
|
||||
"""
|
||||
|
||||
def __init__(self, player):
|
||||
self.player = player
|
||||
self.decision_count = 0
|
||||
|
||||
def chat(self, messages, model=None):
|
||||
prompt = messages[0]["content"] if messages else ""
|
||||
if "played_cards" in prompt:
|
||||
return self._play_reply(), ""
|
||||
if "was_challenged" in prompt:
|
||||
return self._challenge_reply(), ""
|
||||
return "mock opinion: \u8a72\u73a9\u5bb6\u76ee\u524d\u7121\u660e\u986f\u50be\u5411\u3002", ""
|
||||
|
||||
def _play_reply(self):
|
||||
"""\u7d66\u4e86\u81ea\u5df1\u7684\u624b\u724c\uff1a\u540c\u9ede\u6578 >=2 \u51fa\u6700\u591a 3 \u5f35\uff0c\u5426\u5247\u51fa 1 \u5f35\u3002"""
|
||||
hand = sorted(self.player.hand)
|
||||
cards = [hand[0]]
|
||||
for c in hand[1:3]:
|
||||
if c == cards[0]:
|
||||
cards.append(c)
|
||||
cards_json = ",".join('"%s"' % c for c in cards)
|
||||
return ('{"played_cards": [%s], "behavior": "\u7a69\u5065", '
|
||||
'"play_reason": "mock heuristic"}' % cards_json)
|
||||
|
||||
def _challenge_reply(self):
|
||||
"""\u6bcf 4 \u6b21\u8cea\u7591\u6c7a\u7b56\u5c31\u8cea\u7591\u4e00\u6b21\uff0c\u8986\u84cb\u6210\u529f/\u5931\u6557\u5169\u689d\u8def\u5f91\u3002"""
|
||||
self.decision_count += 1
|
||||
do_challenge = (self.decision_count % 4 == 0)
|
||||
return ('{"was_challenged": %s, "challenge_reason": "mock trigger"}' %
|
||||
("true" if do_challenge else "false"))
|
||||
|
||||
|
||||
def main():
|
||||
random.seed(20260803)
|
||||
player_configs = [
|
||||
{"name": "DeepSeek", "model": "mock"},
|
||||
{"name": "GPT", "model": "mock"},
|
||||
{"name": "Claude", "model": "mock"},
|
||||
{"name": "Gemini", "model": "mock"},
|
||||
]
|
||||
|
||||
game = Game(player_configs)
|
||||
for p in game.players:
|
||||
p.llm_client = MockChat(p)
|
||||
|
||||
max_plays = 400
|
||||
for step in range(max_plays):
|
||||
if game.game_over:
|
||||
break
|
||||
game.play_round()
|
||||
|
||||
if game.game_over:
|
||||
print("[OK] \u904a\u6232\u7d50\u675f\uff0c\u4e3b\u6d41\u7a0b\u53ef\u6b63\u5e38\u5b8c\u6210\u3002")
|
||||
else:
|
||||
print(f"[FAIL] \u8dd1\u4e86 {max_plays} \u6b65\u4ecd\u672a\u7d50\u675f\uff08\u53ef\u80fd\u89c4\u5247\u6709\u6b7b\u7d50\uff09\u3002 rounds={game.round_count}")
|
||||
return 1
|
||||
|
||||
rec = game.game_record
|
||||
rounds = len(rec.rounds)
|
||||
alive = [p.name for p in game.players if p.alive]
|
||||
print(f"[OK] \u7372\u52dd\u8005={rec.winner} | \u5b58\u6d3b\u73a9\u5bb6={alive}")
|
||||
print(f"[OK] \u8a18\u9304\u56de\u5408\u6578={rounds}")
|
||||
|
||||
import glob
|
||||
files = glob.glob(os.path.join(REPO_ROOT, "game_records", "*.json"))
|
||||
print(f"[OK] game_records \u7522\u51fa {len(files)} \u500b JSON \u8a18\u9304\u6a94")
|
||||
if files:
|
||||
newest = max(files, key=os.path.getmtime)
|
||||
print(f" \u6700\u65b0\u8a18\u9304: {os.path.basename(newest)}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user