MarcosAutuori's picture
Update app.py
fe0ac78 verified
Raw
History Blame Contribute Delete
6.55 kB
"""
================================================================================
INTERACTIVE FACTORIZATION v1.5 EN
================================================================================
Main Developer: Marcos Autuori | Author: Autuori
================================================================================
"""
import gradio as gr
import time
import os
import base64
# --- CONFIGURATION ---
GRID_NUMBERS = [
10, 12, 14, 15, 16, 18, 20, 21,
24, 25, 27, 28, 30, 32, 35, 36,
40, 42, 45, 48, 50, 54, 56, 60,
63, 64, 70, 72, 80, 81, 90, 100
]
KEYBOARD_KEYS = [2, 3, 4, 5, 6, 7, 8, 9, 10]
MAX_TIME = 350
# --- AUDIO LOADING (BASE64 for Zero Latency) ---
def get_audio_base64(filename):
path = os.path.join("sounds", filename)
if os.path.exists(path):
with open(path, "rb") as f:
data = f.read()
return "data:audio/wav;base64," + base64.b64encode(data).decode()
return ""
# Pre-load sounds from the /sounds folder
SOUNDS = {
"click": get_audio_base64("click.wav"),
"correct": get_audio_base64("correct.wav"),
"wrong": get_audio_base64("wrong.wav"),
"complete": get_audio_base64("complete.wav")
}
# --- JAVASCRIPT FOR INSTANT PLAYBACK ---
js_sound_trigger = """
(tag) => {
if (!tag) return;
const soundData = """ + str(SOUNDS) + """;
if (soundData[tag]) {
const audio = new Audio(soundData[tag]);
audio.play().catch(e => console.log("Audio blocked: Interact with the page first."));
}
return tag;
}
"""
# --- GAME LOGIC ---
def get_empty_state():
return {
"solved": [False] * 32,
"selected_idx": None,
"current_factors": [],
"start_time": time.time(),
"game_over": False,
"score": MAX_TIME
}
def handle_game_logic(state, action_type, value=None):
if state["game_over"]:
return state, "Game Over! Restart to play.", ""
feedback = "Select a target number..."
sound_key = "click"
if action_type == "select_target":
state["selected_idx"] = value
state["current_factors"] = []
feedback = f"Factoring: {GRID_NUMBERS[value]}"
elif action_type == "select_factor":
if state["selected_idx"] is None:
feedback = "⚠️ Select a grid number first!"
sound_key = "wrong"
else:
state["current_factors"].append(int(value))
if len(state["current_factors"]) == 2:
a, b = state["current_factors"]
target = GRID_NUMBERS[state["selected_idx"]]
if a * b == target:
state["solved"][state["selected_idx"]] = True
feedback = f"✔️ {a} x {b} = {target}! Correct!"
sound_key = "correct"
state["selected_idx"] = None
else:
feedback = f"❌ {a} x {b} = {a*b} (Wrong for {target})."
sound_key = "wrong"
state["current_factors"] = []
elapsed = int(time.time() - state["start_time"])
state["score"] = max(0, MAX_TIME - elapsed)
if all(state["solved"]):
state["game_over"] = True
feedback = "🏆 CONGRATULATIONS! ALL SOLVED!"
sound_key = "complete"
return state, feedback, sound_key
# --- STYLING ---
custom_css = """
.grid-btn { min-width: 80px !important; height: 80px !important; font-size: 20px !important; margin: 2px !important; }
.kb-btn { min-width: 60px !important; height: 60px !important; font-size: 18px !important; }
"""
with gr.Blocks(title="Autuori - Factorization Game", css=custom_css) as demo:
state = gr.State(get_empty_state())
# Hidden field to trigger JS sound
sound_trigger = gr.Textbox(visible=False)
sound_trigger.change(None, inputs=sound_trigger, js=js_sound_trigger)
gr.Markdown("# 🎮 Interactive Factorization v1.5 EN\n**Developed by Marcos Autuori**")
with gr.Row():
with gr.Column(scale=4):
# 4x8 Target Grid
grid_buttons = []
for r in range(4):
with gr.Row():
for c in range(8):
idx = r * 8 + c
btn = gr.Button(str(GRID_NUMBERS[idx]), elem_classes="grid-btn")
grid_buttons.append(btn)
with gr.Column(scale=1):
# Status & Info
status_box = gr.Textbox(label="Status", value="Select a target", interactive=False)
score_display = gr.Number(label="Score", value=MAX_TIME, interactive=False)
gr.Markdown("### Factor Keyboard")
keyboard_btns = []
# Keyboard 3x3 (Correct Layout)
for r_kb in range(3):
with gr.Row():
for c_kb in range(3):
val = KEYBOARD_KEYS[r_kb * 3 + c_kb]
kb_btn = gr.Button(str(val), variant="secondary", elem_classes="kb-btn")
keyboard_btns.append(kb_btn)
reset_btn = gr.Button("Restart Game", variant="stop")
def update_ui(s):
updates = []
for i in range(32):
label = f"✅ {GRID_NUMBERS[i]}" if s["solved"][i] else str(GRID_NUMBERS[i])
variant = "success" if s["solved"][i] else ("primary" if s["selected_idx"] == i else "secondary")
updates.append(gr.update(value=label, variant=variant, interactive=not s["solved"][i]))
return updates + [s["score"]]
# Events
for i, btn in enumerate(grid_buttons):
btn.click(
fn=lambda s, idx=i: handle_game_logic(s, "select_target", idx),
inputs=[state], outputs=[state, status_box, sound_trigger]
).then(fn=update_ui, inputs=[state], outputs=grid_buttons + [score_display])
for kb_btn in keyboard_btns:
kb_btn.click(
fn=lambda s, v=kb_btn.value: handle_game_logic(s, "select_factor", v),
inputs=[state], outputs=[state, status_box, sound_trigger]
).then(fn=update_ui, inputs=[state], outputs=grid_buttons + [score_display])
def reset_all():
s = get_empty_state()
u = [gr.update(value=str(n), variant="secondary", interactive=True) for n in GRID_NUMBERS]
return [s, "Game Restarted!", ""] + u + [MAX_TIME]
reset_btn.click(fn=reset_all, outputs=[state, status_box, sound_trigger] + grid_buttons + [score_display])
if __name__ == "__main__":
demo.launch()