RPSgamewithsave / app.py
rui3000's picture
Update app.py
5272060 verified
import gradio as gr
import uuid
import httpx
import os
# FastAPI imports
from fastapi import FastAPI, Request, Body
from fastapi.responses import RedirectResponse
from RockPaperScissor.routes import game_router
from RockPaperScissor.services.service_instance import game_service
# Import the LLM service and GPU function
from RockPaperScissor.services.LLM_service import LLMService, generate_text_with_gpu, llm_service_instance
# Import spaces after the GPU function is defined
try:
import spaces
from spaces import GPU
print("[APP] spaces.GPU imported successfully")
except ImportError:
print("[APP] spaces.GPU not available")
def GPU(f):
return f
# --- Game Constants and Types ---
from enum import Enum
class Move(Enum):
ROCK = "rock"
PAPER = "paper"
SCISSORS = "scissors"
class GameResult(Enum):
PLAYER_WIN = "player_win"
AI_WIN = "ai_win"
DRAW = "draw"
# --- Gradio Interface ---
class RockPaperScissorsUI:
def __init__(self, game_service):
self.game_service = game_service
self.session_id = None # Will be set from State
self.ai_models = list(self.game_service.ai_models.keys())
self.ai_descriptions = {
"random": "Random AI: Makes completely random moves.",
"adaptive_markov": "Adaptive Markov AI: Uses entropy-weighted Markov and frequency models to predict your next move."
}
self.last_move = None
async def reset_session(self, session_id: str):
# Use environment variable for base URL, fallback to localhost
base_url = os.getenv("HF_SPACE_URL", "http://localhost:7860")
async with httpx.AsyncClient() as client:
response = await client.post(
f"{base_url}/api/game/end",
json={"session_id": session_id}
)
print("[DEBUG] /api/game/end response:", response.text)
# Optionally, also clear the session in-memory (if needed)
await self.game_service.clear_session(session_id)
return {"status": "ok"}
def create_interface(self):
with gr.Blocks(theme=gr.themes.Soft(), title="Rock Paper Scissors ๐ŸŽฎ") as demo:
gr.Markdown("# ๐Ÿชจ๐Ÿ“„โœ‚๏ธ Rock Paper Scissors")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### ๐ŸŽฎ Game Setup")
ai_dropdown = gr.Dropdown(
choices=self.ai_models,
value=self.ai_models[0],
label="Select AI Opponent"
)
ai_description = gr.Markdown(self.ai_descriptions[self.ai_models[0]])
with gr.Row():
rock_btn = gr.Button("๐Ÿชจ Rock", variant="secondary", elem_classes=["move-btn"])
paper_btn = gr.Button("๐Ÿ“„ Paper", variant="secondary", elem_classes=["move-btn"])
scissors_btn = gr.Button("โœ‚๏ธ Scissors", variant="secondary", elem_classes=["move-btn"])
# Add End Game button
end_btn = gr.Button("End Game", variant="stop", elem_id="end-game-btn")
# Add Help button
help_btn = gr.Button("๐Ÿ’ก Get Help", variant="primary", elem_id="help-btn")
# Add a dedicated box for the help answer, initially empty
help_answer_box = gr.Markdown("", visible=True)
with gr.Column(scale=2):
gr.Markdown("### ๐Ÿ“Š Game Statistics")
stats_display = gr.Markdown()
result_display = gr.Markdown("Make your move!")
end_result_display = gr.Markdown(visible=False)
status_display = gr.Markdown(visible=True)
ai_dropdown.change(
fn=self.update_ai_description,
inputs=[ai_dropdown],
outputs=[ai_description]
)
# Use a Gradio State to store the session ID
move_state = gr.State("")
session_id_state = gr.State("")
async def play_rock(ai_type, session_id):
if not session_id:
session_id = f"session_{uuid.uuid4()}"
self.session_id = session_id
stats, result = await self.play_round(ai_type, "rock")
return stats, result, session_id
async def play_paper(ai_type, session_id):
if not session_id:
session_id = f"session_{uuid.uuid4()}"
self.session_id = session_id
stats, result = await self.play_round(ai_type, "paper")
return stats, result, session_id
async def play_scissors(ai_type, session_id):
if not session_id:
session_id = f"session_{uuid.uuid4()}"
self.session_id = session_id
stats, result = await self.play_round(ai_type, "scissors")
return stats, result, session_id
# Add a hidden HTML block with JS to auto-save on tab close
gr.HTML("""
<script>
// Store session ID in localStorage whenever it changes
window.setRpsSessionId = function(session_id) {
if (session_id) {
localStorage.setItem('rps_session_id', session_id);
}
};
// On tab close, send session end to backend
window.onbeforeunload = function() {
let session_id = localStorage.getItem('rps_session_id');
if (session_id) {
navigator.sendBeacon("/game/end", JSON.stringify({session_id: session_id}));
}
};
</script>
""")
# Inject JS to click End Game button on tab close
gr.HTML("""
<script>
window.onbeforeunload = function() {
var btn = document.getElementById('end-game-btn');
if (btn) {
btn.click();
}
};
</script>
""")
# After each move, update localStorage with the session ID
def update_session_id_js(session_id):
return f"window.setRpsSessionId('{session_id}');"
rock_btn.click(
fn=play_rock,
inputs=[ai_dropdown, session_id_state],
outputs=[stats_display, result_display, session_id_state],
js=update_session_id_js
)
paper_btn.click(
fn=play_paper,
inputs=[ai_dropdown, session_id_state],
outputs=[stats_display, result_display, session_id_state],
js=update_session_id_js
)
scissors_btn.click(
fn=play_scissors,
inputs=[ai_dropdown, session_id_state],
outputs=[stats_display, result_display, session_id_state],
js=update_session_id_js
)
# End Game button logic
async def end_game(session_id):
if not session_id:
return "No session to end. Play a round first!"
result = await self.reset_session(session_id)
return f"End Game: {result['status']} - {result.get('message', '')}"
end_btn.click(
fn=end_game,
inputs=[session_id_state],
outputs=[end_result_display],
)
end_result_display.visible = True
# Help button logic