import sys import os from pathlib import Path project_root = Path(__file__).resolve().parent.parent if str(project_root) not in sys.path: sys.path.insert(0, str(project_root)) from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from pydantic import BaseModel from typing import List, Optional, Dict, Any from shortdeck_server.persistence import append_game_history from shortdeck_server.arena_adapter import ArenaGame app = FastAPI(title="ShortDeck Poker Server", version="1.0.0") app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) GAME = ArenaGame() client_path = project_root / "client" if client_path.exists(): app.mount("/client", StaticFiles(directory=str(client_path)), name="client") class JoinRequest(BaseModel): name: str class JoinResponse(BaseModel): player_id: int name: str class ActionRequest(BaseModel): player_id: int action: str # "fold", "call", "raise", "check", "bet" amount: Optional[int] = None class ApiResponse(BaseModel): success: bool = True message: Optional[str] = None data: Optional[Dict[str, Any]] = None error: Optional[str] = None class GameInfo(BaseModel): game_id: str players: List[str] dealer_index: int current_turn: int stage: str total_pot: int side_pots: List[Dict[str, Any]] player_cards: List[str] board_cards: List[str] stacks: List[int] player_states: List[str] current_pot: List[int] actions: Dict[str, Any] class HandStrength(BaseModel): hand_type: str description: str strength: float cards: List[str] class Game1v1Response(BaseModel): success: bool message: str human_player_id: Optional[int] = None ai_player_id: Optional[int] = None game_id: Optional[str] = None @app.post("/reset_game") def reset_game(): global GAME try: GAME = ArenaGame() return {"success": True, "message": "游戏已重置"} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) from e @app.post("/join", response_model=JoinResponse) def join(req: JoinRequest): try: print(f"收到加入请求: {req.name}") player_id = GAME.join_game(req.name) print(f"玩家 {req.name} 成功加入,ID: {player_id}") return JoinResponse(player_id=player_id, name=req.name) except ValueError as e: print(f"加入游戏失败 - ValueError: {e}") raise HTTPException(status_code=400, detail=str(e)) from e except Exception as e: print(f"加入游戏失败 - Exception: {e}") import traceback traceback.print_exc() raise HTTPException(status_code=500, detail=str(e)) from e @app.get("/get_game_state") def get_game_state(player_id): try: state = GAME.info(player_id) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) from e return state @app.post("/apply_action", response_model=Dict[str, Any]) def apply_action(req: ActionRequest): try: result = GAME.apply_action(req.player_id, req.action, req.amount) append_game_history(GAME.game_id, {"history": GAME.history}) return result except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) from e @app.get("/valid_actions/{player_id}") def get_valid_actions(player_id: int): try: actions = GAME.get_valid_actions(player_id) return {"valid_actions": actions} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) from e @app.get("/hand_strength/{player_id}") def get_hand_strength(player_id: int): try: strength = GAME.get_hand_strength(player_id) return strength except Exception as e: raise HTTPException(status_code=500, detail=str(e)) from e @app.get("/winners") def get_winners(): try: winners = GAME.get_winners() return winners except Exception as e: raise HTTPException(status_code=500, detail=str(e)) from e @app.post("/reset") def reset_game(request: dict = None): try: global GAME keep_chips = False if request and isinstance(request, dict): keep_chips = request.get("keep_chips", False) if keep_chips and GAME and len(GAME.agents) >= 2: GAME.reset_hand_keep_chips() message = "游戏重置,筹码保持" else: GAME = ArenaGame() message = "游戏完全重置" return {"ok": True, "message": message} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) from e @app.get("/hand_complete") def check_hand_complete(): try: is_complete = GAME.check_hand_complete() return {"hand_complete": is_complete} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) from e if __name__ == "__main__": import uvicorn print(" Starting ShortDeck Poker Server...") print(" Server will run on http://localhost:8001") print(" API docs available at http://localhost:8001/docs") uvicorn.run( app, host="127.0.0.1", port=8001, reload=False )