import gradio as gr import random import math # ─── Swedish vocabulary ─────────────────────────────────────────────────────── NUMBERS_SV = { 1: "ett", 2: "två", 3: "tre", 4: "fyra", 5: "fem", 6: "sex", 7: "sju", 8: "åtta", 9: "nio", 10: "tio", 11: "elva", 12: "tolv" } # Only use "nice" times for children NICE_MINUTES = [0, 15, 30, 45] def get_swedish_time(hour: int, minute: int) -> str: h12 = hour % 12 or 12 next_h = (h12 % 12) + 1 if minute == 0: return f"Klockan är {NUMBERS_SV[h12]}" elif minute == 15: return f"Kvart över {NUMBERS_SV[h12]}" elif minute == 30: return f"Halv {NUMBERS_SV[next_h]}" elif minute == 45: return f"Kvart i {NUMBERS_SV[next_h]}" else: return f"Klockan är {NUMBERS_SV[h12]}" # ─── SVG Clock renderer ─────────────────────────────────────────────────────── def draw_clock(hour: int, minute: int) -> str: cx, cy, r = 150, 150, 130 # Angles (12 o'clock = -90°) min_angle = math.radians((minute / 60) * 360 - 90) hour_angle = math.radians(((hour % 12 + minute / 60) / 12) * 360 - 90) # Hand endpoints mx = cx + int(r * 0.85 * math.cos(min_angle)) my = cy + int(r * 0.85 * math.sin(min_angle)) hx = cx + int(r * 0.55 * math.cos(hour_angle)) hy = cy + int(r * 0.55 * math.sin(hour_angle)) # Hour markers ticks = "" for i in range(1, 13): a = math.radians(i * 30 - 90) x1 = cx + int((r - 10) * math.cos(a)) y1 = cy + int((r - 10) * math.sin(a)) x2 = cx + int(r * math.cos(a)) y2 = cy + int(r * math.sin(a)) ticks += f'' # Number labels (1-12) labels = "" for i in range(1, 13): a = math.radians(i * 30 - 90) lx = cx + int((r - 28) * math.cos(a)) ly = cy + int((r - 28) * math.sin(a)) labels += f'{i}' svg = f""" {ticks} {labels} """ return svg # ─── Game state helpers ─────────────────────────────────────────────────────── def new_question(): hour = random.randint(1, 12) minute = random.choice(NICE_MINUTES) correct = get_swedish_time(hour, minute) # Generate 3 wrong answers wrong_pool = set() while len(wrong_pool) < 3: wh = random.randint(1, 12) wm = random.choice(NICE_MINUTES) w = get_swedish_time(wh, wm) if w != correct: wrong_pool.add(w) options = list(wrong_pool) + [correct] random.shuffle(options) return hour, minute, correct, options # ─── UI builders ───────────────────────────────────────────────────────────── def render_question(score, total, hour, minute, options, feedback=""): clock_svg = draw_clock(hour, minute) stars = "⭐" * score feedback_html = "" if feedback: color = "#16a34a" if "✅" in feedback else "#dc2626" feedback_html = f'
{feedback}
' html = f"""

🕐 Lär dig klockan!

Hur mycket är klockan?

Poäng: {score}/{total} {stars if stars else "—"}
{clock_svg}
{feedback_html}
""" return html # ─── Gradio logic ───────────────────────────────────────────────────────────── def start_game(): score, total = 0, 0 hour, minute, correct, options = new_question() html = render_question(score, total, hour, minute, options) return ( html, gr.update(choices=options, value=None, visible=True), gr.update(visible=True), gr.update(visible=False), score, total, hour, minute, correct, options ) def check_answer(choice, score, total, hour, minute, correct, options): total += 1 if choice == correct: score += 1 feedback = "✅ Rätt svar! Bra jobbat! 🎉" else: feedback = f"❌ Fel! Rätt svar var: {correct}" html = render_question(score, total, hour, minute, options, feedback) return ( html, gr.update(choices=options, value=None, visible=False), gr.update(visible=False), gr.update(visible=True), score, total, hour, minute, correct, options ) def next_question(score, total): hour, minute, correct, options = new_question() html = render_question(score, total, hour, minute, options) return ( html, gr.update(choices=options, value=None, visible=True), gr.update(visible=True), gr.update(visible=False), score, total, hour, minute, correct, options ) # ─── Gradio UI ──────────────────────────────────────────────────────────────── with gr.Blocks( title="🕐 Lär dig klockan!", theme=gr.themes.Soft(primary_hue="blue", font=["Segoe UI", "Arial"]) ) as demo: # hidden state st_score = gr.State(0) st_total = gr.State(0) st_hour = gr.State(1) st_minute = gr.State(0) st_correct = gr.State("") st_options = gr.State([]) gr.HTML("""

🕐 Lär dig klockan!

Ett roligt spel för barn – lär dig svenska klockor!

""") display = gr.HTML(label="") radio = gr.Radio(choices=[], label="Välj rätt svar:", visible=False, interactive=True) btn_check = gr.Button("✅ Kontrollera svaret", variant="primary", visible=False) btn_next = gr.Button("➡️ Nästa fråga", variant="secondary", visible=False) btn_start = gr.Button("🎮 Starta spelet!", variant="primary", size="lg") # Events btn_start.click( start_game, [], [display, radio, btn_check, btn_next, st_score, st_total, st_hour, st_minute, st_correct, st_options] ) btn_check.click( check_answer, [radio, st_score, st_total, st_hour, st_minute, st_correct, st_options], [display, radio, btn_check, btn_next, st_score, st_total, st_hour, st_minute, st_correct, st_options] ) btn_next.click( next_question, [st_score, st_total], [display, radio, btn_check, btn_next, st_score, st_total, st_hour, st_minute, st_correct, st_options] ) if __name__ == "__main__": demo.launch()