blackjack-theory-game/api.py
2026-06-11 20:10:52 +02:00

116 lines
3.6 KiB
Python

import uuid
from typing import Optional
from fastapi import FastAPI, Cookie, HTTPException
from fastapi.responses import FileResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
from blackjack_game import BlackjackGame
from game_logic import can_split, get_correct_decision, get_split_decision
app = FastAPI(title="Blackjack Theory Trainer")
sessions: dict[str, BlackjackGame] = {}
def get_or_create_session(session_id: Optional[str] = None) -> tuple[str, BlackjackGame, bool]:
if session_id and session_id in sessions:
return session_id, sessions[session_id], False
new_id = str(uuid.uuid4())
game = BlackjackGame()
sessions[new_id] = game
return new_id, game, True
class DecisionRequest(BaseModel):
action: str
@app.post("/api/start-round")
async def start_round(session_id: Optional[str] = Cookie(None)):
sid, game, is_new = get_or_create_session(session_id)
game.start_new_round()
data = {
"dealer_card": game.dealer_card,
"player_hand": game.player_hand,
"player_total": game.player_total,
"has_blackjack": game.has_blackjack,
"can_split": can_split(game.player_hand),
"current_streak": game.stats["current_streak"],
}
response = JSONResponse(content=data)
if is_new:
response.set_cookie(key="session_id", value=sid, path="/")
return response
@app.post("/api/check-decision")
async def check_decision(req: DecisionRequest, session_id: Optional[str] = Cookie(None)):
if not session_id or session_id not in sessions:
raise HTTPException(status_code=400, detail="No active session")
game = sessions[session_id]
scenario_type = "hard_totals"
if can_split(game.player_hand):
scenario_type = "pairs"
elif "A" in [c.split("_")[-1] for c in game.player_hand]:
scenario_type = "soft_totals"
if can_split(game.player_hand):
decision, feedback = get_split_decision(game.player_hand, game.dealer_card)
correct = req.action == decision
else:
decision, feedback = get_correct_decision(
game.player_hand, game.player_total, game.dealer_card
)
correct = req.action == decision
had_streak = game.stats["current_streak"]
game.update_stats(correct, scenario_type)
stats = game.get_stats()
streak_ended = None
if not correct and had_streak >= 3:
streak_ended = f"Streak ended at {had_streak}!"
return {
"correct": correct,
"decision": decision,
"feedback": feedback,
"scenario_type": scenario_type,
"stats": stats,
"streak_ended": streak_ended,
}
@app.get("/api/stats")
async def get_stats(session_id: Optional[str] = Cookie(None)):
if not session_id or session_id not in sessions:
raise HTTPException(status_code=400, detail="No active session")
return sessions[session_id].get_stats()
@app.get("/api/hint")
async def get_hint(session_id: Optional[str] = Cookie(None)):
if not session_id or session_id not in sessions:
raise HTTPException(status_code=400, detail="No active session")
game = sessions[session_id]
if can_split(game.player_hand):
decision, feedback = get_split_decision(game.player_hand, game.dealer_card)
else:
decision, feedback = get_correct_decision(
game.player_hand, game.player_total, game.dealer_card
)
return {"decision": decision, "feedback": feedback}
@app.get("/")
async def read_index():
return FileResponse("static/index.html")
app.mount("/images", StaticFiles(directory="images"), name="images")