MarcosAutuori commited on
Commit
36e0f4e
·
verified ·
1 Parent(s): 8cfbd51

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +94 -84
app.py CHANGED
@@ -1,6 +1,6 @@
1
  """
2
  ================================================================================
3
- INTERACTIVE FACTORIZATION v1.4 EN
4
  ================================================================================
5
  Main Developer: Marcos Autuori | Author: Autuori
6
  ================================================================================
@@ -8,13 +8,10 @@ Main Developer: Marcos Autuori | Author: Autuori
8
 
9
  import gradio as gr
10
  import time
11
- import wave
12
- import struct
13
- import math
14
  import os
15
  import base64
16
 
17
- # --- DATA ---
18
  GRID_NUMBERS = [
19
  10, 12, 14, 15, 16, 18, 20, 21,
20
  24, 25, 27, 28, 30, 32, 35, 36,
@@ -22,143 +19,156 @@ GRID_NUMBERS = [
22
  63, 64, 70, 72, 80, 81, 90, 100
23
  ]
24
  KEYBOARD_KEYS = [2, 3, 4, 5, 6, 7, 8, 9, 10]
25
- MAX_TIME = 150
26
-
27
- # --- AUDIO GENERATION & ENCODING ---
28
- def create_audio_base64(freq, duration=0.15):
29
- """Gera o som e converte para Base64 para o JS tocar instantaneamente"""
30
- sample_rate = 44100
31
- n_samples = int(sample_rate * duration)
32
- buffer = []
33
- for i in range(n_samples):
34
- val = int(0.3 * 32767.0 * math.sin(2.0 * math.pi * freq * i / sample_rate))
35
- buffer.append(struct.pack('<h', val))
36
-
37
- # Criar um stream de bytes wave
38
- import io
39
- byte_io = io.BytesIO()
40
- with wave.open(byte_io, 'wb') as wav_file:
41
- wav_file.setnchannels(1)
42
- wav_file.setsampwidth(2)
43
- wav_file.setframerate(sample_rate)
44
- wav_file.writeframes(b"".join(buffer))
45
-
46
- return "data:audio/wav;base64," + base64.b64encode(byte_io.getvalue()).decode()
47
 
48
- # Pre-encodando os sons
49
- SOUND_DATA = {
50
- "click": create_audio_base64(440, 0.05),
51
- "correct": create_audio_base64(880, 0.2),
52
- "wrong": create_audio_base64(220, 0.3)
 
 
 
 
 
 
 
 
 
 
53
  }
54
 
55
- # --- JAVASCRIPT TRIGGERS ---
56
- # Esse script injetado toca o som sem latência de rede
57
  js_sound_trigger = """
58
  (tag) => {
59
  if (!tag) return;
60
- const sounds = {
61
- 'click': '""" + SOUND_DATA["click"] + """',
62
- 'correct': '""" + SOUND_DATA["correct"] + """',
63
- 'wrong': '""" + SOUND_DATA["wrong"] + """'
64
- };
65
- const audio = new Audio(sounds[tag]);
66
- audio.play().catch(e => console.log('Autoplay blocked or fast-click error'));
67
- return tag;
68
  }
69
  """
70
 
71
- # --- LOGIC ---
72
  def get_empty_state():
73
  return {
74
- "solved": [False] * 32, "selected_idx": None, "current_factors": [],
75
- "start_time": time.time(), "game_over": False, "score": MAX_TIME
 
 
 
 
76
  }
77
 
78
  def handle_game_logic(state, action_type, value=None):
79
- if state["game_over"]: return state, "Game Over!", ""
80
-
81
- feedback = "Select a target..."
 
82
  sound_key = "click"
83
 
84
  if action_type == "select_target":
85
  state["selected_idx"] = value
86
  state["current_factors"] = []
87
  feedback = f"Factoring: {GRID_NUMBERS[value]}"
88
-
89
  elif action_type == "select_factor":
90
  if state["selected_idx"] is None:
91
- feedback = "⚠️ Select a number first!"
92
  sound_key = "wrong"
93
  else:
94
  state["current_factors"].append(int(value))
95
  if len(state["current_factors"]) == 2:
96
  a, b = state["current_factors"]
97
  target = GRID_NUMBERS[state["selected_idx"]]
 
98
  if a * b == target:
99
  state["solved"][state["selected_idx"]] = True
100
- feedback = f"✔️ Correct: {target}!"
101
  sound_key = "correct"
102
  state["selected_idx"] = None
103
  else:
104
- feedback = f"❌ Wrong for {target}!"
105
  sound_key = "wrong"
106
  state["current_factors"] = []
 
 
 
 
107
 
108
- state["score"] = max(0, MAX_TIME - int(time.time() - state["start_time"]))
109
  if all(state["solved"]):
110
  state["game_over"] = True
111
- feedback = "🏆 VICTORY!"
112
-
 
113
  return state, feedback, sound_key
114
 
115
- # --- UI ---
116
- custom_css = ".grid-btn { min-width: 80px !important; height: 80px !important; font-size: 20px !important; }"
 
 
 
117
 
118
  with gr.Blocks(title="Autuori - Factorization Game", css=custom_css) as demo:
119
  state = gr.State(get_empty_state())
120
 
121
- # Hidden Trigger for JS Sound
122
  sound_trigger = gr.Textbox(visible=False)
123
  sound_trigger.change(None, inputs=sound_trigger, js=js_sound_trigger)
124
 
125
- gr.Markdown("# 🎮 Interactive Factorization v1.4 EN\n**Developer: Marcos Autuori**")
126
 
127
  with gr.Row():
128
  with gr.Column(scale=4):
129
- grid_btns = []
130
  for r in range(4):
131
  with gr.Row():
132
  for c in range(8):
133
- idx = r*8+c
134
  btn = gr.Button(str(GRID_NUMBERS[idx]), elem_classes="grid-btn")
135
- grid_btns.append(btn)
136
 
137
  with gr.Column(scale=1):
138
- status = gr.Textbox(label="Status", value="Select a target", interactive=False)
139
- score = gr.Number(label="Score", value=MAX_TIME, interactive=False)
140
- kb_btns = [gr.Button(str(v), variant="secondary") for v in KEYBOARD_KEYS]
141
- reset = gr.Button("Restart", variant="stop")
 
 
 
 
 
 
142
 
143
  def update_ui(s):
144
- return [gr.update(value=f"✅ {GRID_NUMBERS[i]}" if s["solved"][i] else str(GRID_NUMBERS[i]),
145
- variant="success" if s["solved"][i] else ("primary" if s["selected_idx"]==i else "secondary"),
146
- interactive=not s["solved"][i]) for i in range(32)] + [s["score"]]
147
-
148
- # Event Mapping
149
- for i, b in enumerate(grid_btns):
150
- b.click(fn=lambda s, idx=i: handle_game_logic(s, "select_target", idx),
151
- inputs=[state], outputs=[state, status, sound_trigger]).then(
152
- fn=update_ui, inputs=[state], outputs=grid_btns + [score])
153
-
154
- for b in kb_btns:
155
- b.click(fn=lambda s, v=b.value: handle_game_logic(s, "select_factor", v),
156
- inputs=[state], outputs=[state, status, sound_trigger]).then(
157
- fn=update_ui, inputs=[state], outputs=grid_btns + [score])
158
-
159
- reset.click(fn=lambda: [get_empty_state(), "New Game!", ""],
160
- outputs=[state, status, sound_trigger]).then(
161
- fn=update_ui, inputs=[state], outputs=grid_buttons + [score] if 'grid_buttons' in locals() else grid_btns + [score])
 
 
 
 
 
 
 
 
162
 
163
  if __name__ == "__main__":
164
  demo.launch()
 
1
  """
2
  ================================================================================
3
+ INTERACTIVE FACTORIZATION v1.5 EN
4
  ================================================================================
5
  Main Developer: Marcos Autuori | Author: Autuori
6
  ================================================================================
 
8
 
9
  import gradio as gr
10
  import time
 
 
 
11
  import os
12
  import base64
13
 
14
+ # --- CONFIGURAÇÕES ---
15
  GRID_NUMBERS = [
16
  10, 12, 14, 15, 16, 18, 20, 21,
17
  24, 25, 27, 28, 30, 32, 35, 36,
 
19
  63, 64, 70, 72, 80, 81, 90, 100
20
  ]
21
  KEYBOARD_KEYS = [2, 3, 4, 5, 6, 7, 8, 9, 10]
22
+ MAX_TIME = 200 # Tempo aumentado conforme solicitado
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
+ # --- CARREGAMENTO DE ÁUDIO (BASE64) ---
25
+ def get_audio_base64(filename):
26
+ path = os.path.join("sounds", filename)
27
+ if os.path.exists(path):
28
+ with open(path, "rb") as f:
29
+ data = f.read()
30
+ return "data:audio/wav;base64," + base64.b64encode(data).decode()
31
+ return ""
32
+
33
+ # Pré-carrega os sons da pasta /sounds para o dicionário JS
34
+ SOUNDS = {
35
+ "click": get_audio_base64("click.wav"),
36
+ "correct": get_audio_base64("correct.wav"),
37
+ "wrong": get_audio_base64("wrong.wav"),
38
+ "complete": get_audio_base64("complete.wav")
39
  }
40
 
41
+ # --- JAVASCRIPT PARA PLAYBACK IMEDIATO ---
 
42
  js_sound_trigger = """
43
  (tag) => {
44
  if (!tag) return;
45
+ const soundData = """ + str(SOUNDS) + """;
46
+ if (soundData[tag]) {
47
+ const audio = new Audio(soundData[tag]);
48
+ audio.play().catch(e => console.log("Audio playback blocked"));
49
+ }
50
+ return tag;
 
 
51
  }
52
  """
53
 
54
+ # --- LÓGICA DO JOGO ---
55
  def get_empty_state():
56
  return {
57
+ "solved": [False] * 32,
58
+ "selected_idx": None,
59
+ "current_factors": [],
60
+ "start_time": time.time(),
61
+ "game_over": False,
62
+ "score": MAX_TIME
63
  }
64
 
65
  def handle_game_logic(state, action_type, value=None):
66
+ if state["game_over"]:
67
+ return state, "Game Over!", ""
68
+
69
+ feedback = "Select a target number..."
70
  sound_key = "click"
71
 
72
  if action_type == "select_target":
73
  state["selected_idx"] = value
74
  state["current_factors"] = []
75
  feedback = f"Factoring: {GRID_NUMBERS[value]}"
76
+
77
  elif action_type == "select_factor":
78
  if state["selected_idx"] is None:
79
+ feedback = "⚠️ Select a grid number first!"
80
  sound_key = "wrong"
81
  else:
82
  state["current_factors"].append(int(value))
83
  if len(state["current_factors"]) == 2:
84
  a, b = state["current_factors"]
85
  target = GRID_NUMBERS[state["selected_idx"]]
86
+
87
  if a * b == target:
88
  state["solved"][state["selected_idx"]] = True
89
+ feedback = f"✔️ {a} x {b} = {target}! Correct!"
90
  sound_key = "correct"
91
  state["selected_idx"] = None
92
  else:
93
+ feedback = f"❌ {a} x {b} = {a*b} (Wrong for {target})."
94
  sound_key = "wrong"
95
  state["current_factors"] = []
96
+
97
+ # Cálculo do Score
98
+ elapsed = int(time.time() - state["start_time"])
99
+ state["score"] = max(0, MAX_TIME - elapsed)
100
 
 
101
  if all(state["solved"]):
102
  state["game_over"] = True
103
+ feedback = f"🏆 VICTORY! Final Score: {state['score']}"
104
+ sound_key = "complete" # Som final solicitado
105
+
106
  return state, feedback, sound_key
107
 
108
+ # --- INTERFACE ---
109
+ custom_css = """
110
+ .grid-btn { min-width: 80px !important; height: 80px !important; font-size: 20px !important; margin: 2px !important; }
111
+ .kb-btn { min-width: 60px !important; height: 50px !important; }
112
+ """
113
 
114
  with gr.Blocks(title="Autuori - Factorization Game", css=custom_css) as demo:
115
  state = gr.State(get_empty_state())
116
 
117
+ # Campo invisível que dispara o som via JS
118
  sound_trigger = gr.Textbox(visible=False)
119
  sound_trigger.change(None, inputs=sound_trigger, js=js_sound_trigger)
120
 
121
+ gr.Markdown("# 🎮 Interactive Factorization v1.5 EN\n**Developer: Marcos Autuori**")
122
 
123
  with gr.Row():
124
  with gr.Column(scale=4):
125
+ grid_buttons = []
126
  for r in range(4):
127
  with gr.Row():
128
  for c in range(8):
129
+ idx = r * 8 + c
130
  btn = gr.Button(str(GRID_NUMBERS[idx]), elem_classes="grid-btn")
131
+ grid_buttons.append(btn)
132
 
133
  with gr.Column(scale=1):
134
+ status_box = gr.Textbox(label="Status", value="Select a blue target", interactive=False)
135
+ score_display = gr.Number(label="Score", value=MAX_TIME, interactive=False)
136
+
137
+ gr.Markdown("### Factors")
138
+ keyboard_btns = []
139
+ for val in KEYBOARD_KEYS:
140
+ kb_btn = gr.Button(str(val), variant="secondary", elem_classes="kb-btn")
141
+ keyboard_btns.append(kb_btn)
142
+
143
+ reset_btn = gr.Button("Restart Game", variant="stop")
144
 
145
  def update_ui(s):
146
+ updates = []
147
+ for i in range(32):
148
+ label = f"✅ {GRID_NUMBERS[i]}" if s["solved"][i] else str(GRID_NUMBERS[i])
149
+ variant = "success" if s["solved"][i] else ("primary" if s["selected_idx"] == i else "secondary")
150
+ updates.append(gr.update(value=label, variant=variant, interactive=not s["solved"][i]))
151
+ return updates + [s["score"]]
152
+
153
+ # Eventos
154
+ for i, btn in enumerate(grid_buttons):
155
+ btn.click(
156
+ fn=lambda s, idx=i: handle_game_logic(s, "select_target", idx),
157
+ inputs=[state], outputs=[state, status_box, sound_trigger]
158
+ ).then(fn=update_ui, inputs=[state], outputs=grid_buttons + [score_display])
159
+
160
+ for kb_btn in keyboard_btns:
161
+ kb_btn.click(
162
+ fn=lambda s, v=kb_btn.value: handle_game_logic(s, "select_factor", v),
163
+ inputs=[state], outputs=[state, status_box, sound_trigger]
164
+ ).then(fn=update_ui, inputs=[state], outputs=grid_buttons + [score_display])
165
+
166
+ def reset_all():
167
+ s = get_empty_state()
168
+ u = [gr.update(value=str(n), variant="secondary", interactive=True) for n in GRID_NUMBERS]
169
+ return [s, "Game Restarted!", ""] + u + [MAX_TIME]
170
+
171
+ reset_btn.click(fn=reset_all, outputs=[state, status_box, sound_trigger] + grid_buttons + [score_display])
172
 
173
  if __name__ == "__main__":
174
  demo.launch()