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(""" """) # Inject JS to click End Game button on tab close gr.HTML(""" """) # 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