"""HTML/CSS rendering for Doodle Duel (human-draws mode only).""" from __future__ import annotations import html import random import config import game _LEADS = ["hmm...", "ooh!", "wait--", "is it...", "I think...", "let me guess...", "could be...", "aha,"] def _fmt_time(secs) -> str: t = int(secs) return f"{t // 60}:{t % 60:02d}" if t >= 60 else str(t) def _timer_ring(state) -> str: frac = max(0.0, min(1.0, state.time_left / config.ROUND_SECONDS)) R = 26 circ = 2 * 3.14159 * R off = circ * (1 - frac) color = "#46d39a" if frac > 0.5 else "#f4c04e" if frac > 0.2 else "#ef5d6c" return ( '' f'' f'' f'{_fmt_time(state.time_left)}' '' ) def render_topbar(state) -> str: if state.status == "idle": return ( '
' '🎨 Pick a difficulty below to get three words to draw!' '
' ) if state.status == "choosing": return ( '
' '✏️ Pick a word to draw' ' 👇  (the robot isn\'t peeking yet!)
' ) ring = _timer_ring(state) hint_tag = "" if state.hints_used: pat = game.masked(state.word, state.revealed).replace(" ", " / ") hint_tag = f' · 💡 {html.escape(pat)}' cat_badge = ( f'{html.escape(state.category)}' if state.category else "" ) diff_icon = {"easy": "🟢", "medium": "🟡", "hard": "🔴"}.get(state.difficulty, "") diff_badge = f'{diff_icon} {state.difficulty}' best_tag = ( f'🏆 {state.best}' if state.best > 0 else "" ) return ( f'
' f'
' f' round {state.round} · draw this' f' {html.escape(state.word.upper())}' f' {cat_badge} {diff_badge}
' f' {ring}' f'
' f' 🔥 streak {state.streak}{hint_tag}' f'
⭐ {state.score}{best_tag}
' f'
' f'
' ) def render_ai(state) -> str: face = "🤖" mood = "idle" if state.status == "won": face, mood = "🎉", "win" elif state.status == "drawing": mood = "think" if state.status == "idle": bubble = "👋 Ready when you are!" elif state.status == "choosing": face, mood = "🙈", "idle" bubble = "👀 Eyes closed — pick your word!" elif state.status == "won": bubble = f"🎉 Got it — {html.escape(state.last_caught)}!" elif state.status == "lost": bubble = f"😵 Argh! It was {html.escape(state.word)} — I never stood a chance!" elif state.guess_log: recent = state.guess_log[-4:] chips = "".join(f'{html.escape(g)}' for g in recent) bubble = f"{random.choice(_LEADS)} {chips}?" else: bubble = "🔍 Hmm, let me see what you\'re drawing..." return ( f'
' f'
{face}
' f'
{bubble}
' f'
' ) def render_thinking(state) -> str: if state.status in ("idle", "choosing"): body = ( '
The robot\'s reasoning will show up here ' 'line by line as it watches your doodle take shape... 👀
' ) elif state.status == "drawing" and not state.reason_log: body = ( '
' 'Analyzing your canvas...
' ) elif not state.reason_log: body = '
No thoughts recorded this round.
' else: body = "".join( f'
{html.escape(t)}
' for i, t in enumerate(reversed(state.reason_log)) ) guesses_seen = len(state.reason_log) subtitle = ( f'{guesses_seen} look{"s" if guesses_seen != 1 else ""}' if guesses_seen else "" ) return ( f'
' f'
🧠 Robot\'s thoughts {subtitle}
' f'
{body}
' f'
' ) def render_result(state) -> str: if state.status == "won": confetti = _make_confetti() streak_txt = ( f"  🔥 {state.streak} in a row!" if state.streak > 1 else "" ) best_txt = ( f"  🏆 New best: {state.best}!" if state.score == state.best and state.best > 0 else "" ) return ( f'
{confetti}' f'
The robot guessed it! 🎉
' f'
{html.escape(state.last_caught)}{streak_txt}{best_txt}' f' — press Next to keep the streak going.
' ) if state.status == "lost": best_streak_txt = ( f" Best streak: {state.best_streak}." if state.best_streak > 1 else "" ) return ( f'
' f'
⏱️ Time\'s up — the robot couldn\'t guess it!
' f'
It was {state.emoji} {html.escape(state.word)}. ' f'Streak reset.{best_streak_txt} Press Next to try again.
' ) return "" def _make_confetti() -> str: return '
' + "".join( f'' for i in range(36) ) + "
"