File size: 5,968 Bytes
bd268da
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a2f6240
 
 
bd268da
a2f6240
 
 
 
 
 
 
 
 
 
bd268da
a2f6240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bd268da
a2f6240
bd268da
a2f6240
 
 
 
bd268da
a2f6240
 
 
bd268da
a2f6240
c79a44e
 
bd268da
a2f6240
 
 
 
 
 
 
bd268da
a2f6240
 
 
bd268da
a2f6240
 
bd268da
a2f6240
 
 
 
 
bd268da
a2f6240
 
 
bd268da
5095d5d
 
bd268da
 
 
5095d5d
 
 
bd268da
5095d5d
 
 
 
bd268da
a2f6240
 
bd268da
 
 
 
 
a2f6240
 
5095d5d
bd268da
a2f6240
 
 
 
 
 
 
 
 
bd268da
 
 
a2f6240
bd268da
a2f6240
5095d5d
 
 
 
 
 
a2f6240
bd268da
c79a44e
bd268da
c79a44e
 
 
 
bd268da
a2f6240
 
 
 
 
c79a44e
a2f6240
bd268da
c79a44e
 
 
a2f6240
 
c79a44e
a2f6240
bd268da
a2f6240
 
c79a44e
bd268da
a2f6240
5095d5d
a2f6240
 
 
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
"""
================================================================================
                    INTERACTIVE FACTORIZATION v1.4 EN
================================================================================
Main Developer:
    Marcos Autuori (MarcosAutuori)
    
Development Assistance:
    Gemini / GitHub Copilot

Technologies Used:
    Python 3.12, Gradio
================================================================================
"""

import gradio as gr
import time

# Data from the original v1.4 EN script
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 = 150

def get_empty_state():
    """Initializes game state"""
    return {
        "solved": [False] * 32,
        "selected_idx": None,
        "current_factors": [],
        "start_time": time.time(),
        "game_over": False,
        "score": MAX_TIME
    }

def format_grid_button(idx, solved_list, selected_idx):
    label = str(GRID_NUMBERS[idx])
    if solved_list[idx]:
        return gr.update(value=f"✅ {label}", variant="success", interactive=False)
    if selected_idx == idx:
        return gr.update(variant="primary", interactive=True)
    return gr.update(value=label, variant="secondary", interactive=True)

def handle_game_logic(state, action_type, value=None):
    if state["game_over"]:
        return state, "Game Over! Restart to play again."

    feedback = "Select a number from the grid..."
    
    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!"
        else:
            state["current_factors"].append(int(value))
            factors_str = " x ".join(map(str, state["current_factors"]))
            feedback = f"Current Factors: {factors_str}"
            
            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!"
                    state["selected_idx"] = None
                    state["current_factors"] = []
                else:
                    feedback = f"❌ {a} x {b} = {a*b} (Wrong for {target})."
                    state["current_factors"] = []

    # Update Score and Check Completion
    elapsed = int(time.time() - state["start_time"])
    state["score"] = max(0, MAX_TIME - elapsed)
    
    if all(state["solved"]):
        state["game_over"] = True
        feedback = f"🏆 COMPLETE! Final Score: {state['score']}"

    return state, feedback

# CSS for 4x8 uniform grid and consistent layout
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: 50px !important;
}
"""

with gr.Blocks(title="Autuori - Factorization Game", css=custom_css) as demo:
    state = gr.State(get_empty_state())
    
    with gr.Row():
        gr.Markdown("""
        # 🎮 Interactive Factorization v1.4 EN
        **Developed by Marcos Autuori** | Select a target and match its factors!
        """)
    
    with gr.Row():
        with gr.Column(scale=4):
            # 4x8 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):
            # Side Status Panel
            status_box = gr.Textbox(label="Status", value="Select a blue number", interactive=False)
            score_display = gr.Number(label="Score (Time)", value=MAX_TIME, interactive=False)
            
            gr.Markdown("### Keyboard")
            keyboard_btns = []
            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")

    # Helper function to refresh the whole UI
    def update_ui(s):
        updates = [format_grid_button(i, s["solved"], s["selected_idx"]) for i in range(32)]
        return updates + [s["score"]]

    # Grid 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]
        ).then(fn=update_ui, inputs=[state], outputs=grid_buttons + [score_display])

    # Keyboard Events
    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]
        ).then(fn=update_ui, inputs=[state], outputs=grid_buttons + [score_display])

    # Reset Event
    def reset_all():
        new_state = get_empty_state()
        updates = [gr.update(value=str(num), variant="secondary", interactive=True) for num in GRID_NUMBERS]
        return [new_state, "Game Restarted!"] + updates + [MAX_TIME]

    reset_btn.click(fn=reset_all, outputs=[state, status_box] + grid_buttons + [score_display])

if __name__ == "__main__":
    demo.launch()