File size: 6,550 Bytes
8acd6ee 36e0f4e 8acd6ee 3196102 8acd6ee fe0ac78 8acd6ee 3196102 36e0f4e 3196102 36e0f4e 8acd6ee 3196102 8acd6ee 36e0f4e 3196102 36e0f4e 8acd6ee 3196102 8acd6ee 36e0f4e 8acd6ee 36e0f4e 3196102 36e0f4e 8acd6ee 36e0f4e 8acd6ee 36e0f4e 8acd6ee 36e0f4e 8acd6ee 36e0f4e 8acd6ee 36e0f4e 8acd6ee 36e0f4e 8acd6ee 3196102 36e0f4e 8acd6ee 3196102 36e0f4e 3196102 36e0f4e 8acd6ee 3196102 8acd6ee 3196102 8acd6ee 3196102 36e0f4e 8acd6ee 36e0f4e 8acd6ee 36e0f4e 8acd6ee 3196102 36e0f4e 3196102 36e0f4e 3196102 36e0f4e 8acd6ee 36e0f4e 3196102 36e0f4e 8acd6ee | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | """
================================================================================
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() |