192 lines
5.3 KiB
Python
192 lines
5.3 KiB
Python
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
|
||
)
|