# app.py # 3x3 Rubik's-style Color Password System Simulator # Hugging Face Spaces ready (Gradio) # # How it works (quick start): # 1) Choose a side (each has a fixed center color). # 2) Click "Randomize" to shuffle the 8 non-center tiles. # 3) Arrange the tiles by clicking them. Each click cycles color in this exact order: # Red → Yellow → Green → Blue → White → Orange → (repeat) # (Center tile is locked to the side's middle color.) # 4) Click "Set Code" to save the current pattern as the password for that side. # 5) Click "Randomize" again (or leave as-is), then try to re-create the saved pattern. # 6) Click "Unlock" to check your attempt. Success will be announced. # # Notes: # - Each of the 6 sides has its own center color and its own saved code. # - "Show Code" lets you peek at the saved code preview for the selected side. # - "Reset Side" resets only the selected side (grid and saved code). # - "Reset All" resets everything. # # Made for users with zero coding experience—just upload these files # to a Hugging Face Space (Python/Gradio), and it will run. import gradio as gr from PIL import Image, ImageDraw import random # ----- Color model ----- COLOR_ORDER = ["Red", "Yellow", "Green", "Blue", "White", "Orange"] COLOR_HEX = { "Red": "#e53935", "Yellow": "#fdd835", "Green": "#43a047", "Blue": "#1e88e5", "White": "#fafafa", "Orange": "#fb8c00", } # Six sides with fixed middle colors (index 4 in a 0..8 grid) SIDES = { "Side 1 (Red middle)": "Red", "Side 2 (Green middle)": "Green", "Side 3 (Orange middle)": "Orange", "Side 4 (Blue middle)": "Blue", "Side 5 (White middle)": "White", "Side 6 (Yellow middle)": "Yellow", } SIDE_NAMES = list(SIDES.keys()) TILE_SIZE = 120 # px GRID_SIZE = 3 IMG_SIZE = TILE_SIZE * GRID_SIZE def new_grid(center_color: str): """Create a new 3x3 grid with the center locked to center_color and others random.""" grid = [random.choice(COLOR_ORDER) for _ in range(9)] grid[4] = center_color return grid def render_grid(grid): """Return a PIL image of the 3x3 grid.""" img = Image.new("RGB", (IMG_SIZE, IMG_SIZE), color=(30, 30, 30)) draw = ImageDraw.Draw(img) for i, color_name in enumerate(grid): r = i // GRID_SIZE c = i % GRID_SIZE x0 = c * TILE_SIZE + 2 y0 = r * TILE_SIZE + 2 x1 = (c+1) * TILE_SIZE - 2 y1 = (r+1) * TILE_SIZE - 2 fill = COLOR_HEX[color_name] draw.rectangle([x0, y0, x1, y1], fill=fill, outline="#111111", width=4) # subtle "bevel" lines draw.line([x0, y0, x1, y0], fill="#000000", width=3) draw.line([x0, y0, x0, y1], fill="#000000", width=3) return img def grid_image_bytes(grid): return render_grid(grid) def cycle_color(color_name): idx = COLOR_ORDER.index(color_name) return COLOR_ORDER[(idx + 1) % len(COLOR_ORDER)] # ----- App State ----- # We keep per-side state: current grid + saved code (or None) def init_state(): state = {} for side, center in SIDES.items(): state[side] = { "center": center, "grid": new_grid(center), "code": None, # saved pattern (list of 9 color names) or None "unlocked": False, } return state # ----- Helpers for events ----- def side_to_image(state, side_name): return grid_image_bytes(state[side_name]["grid"]) def randomize_side(state, side_name): center = state[side_name]["center"] grid = state[side_name]["grid"] # randomize non-center tiles only for i in range(9): if i == 4: grid[i] = center else: grid[i] = random.choice(COLOR_ORDER) state[side_name]["unlocked"] = False return state, side_to_image(state, side_name), status_text(state, side_name), code_preview(state, side_name) def set_code(state, side_name): # Save current grid as the code state[side_name]["code"] = list(state[side_name]["grid"]) state[side_name]["unlocked"] = False return state, "Code saved for this side.", code_preview(state, side_name) def unlock(state, side_name): target = state[side_name]["code"] if target is None: return state, "No code saved yet for this side.", code_preview(state, side_name) if state[side_name]["grid"] == target: state[side_name]["unlocked"] = True return state, "✅ Unlocked! Pattern matches the saved code.", code_preview(state, side_name) else: state[side_name]["unlocked"] = False return state, "❌ Not yet. Keep trying!", code_preview(state, side_name) def status_text(state, side_name): u = state[side_name]["unlocked"] code = state[side_name]["code"] if u: return "Status: ✅ Unlocked" if code is None: return "Status: No code saved" return "Status: Locked (code saved, try to match & click Unlock)" def code_preview(state, side_name): """Return a small preview image for the saved code, or None if not set.""" code = state[side_name]["code"] if code is None: return None # Tiny rendering prev_tile = 40 size = prev_tile * GRID_SIZE img = Image.new("RGB", (size, size), color=(30,30,30)) draw = ImageDraw.Draw(img) for i, color_name in enumerate(code): r = i // GRID_SIZE c = i % GRID_SIZE x0 = c * prev_tile + 2 y0 = r * prev_tile + 2 x1 = (c+1) * prev_tile - 2 y1 = (r+1) * prev_tile - 2 fill = COLOR_HEX[color_name] draw.rectangle([x0, y0, x1, y1], fill=fill, outline="#111111", width=2) return img def reset_side(state, side_name): center = state[side_name]["center"] state[side_name] = { "center": center, "grid": new_grid(center), "code": None, "unlocked": False, } return state, side_to_image(state, side_name), status_text(state, side_name), None def reset_all(state): return init_state() # Handle tile clicks on the image: gr.Image.select gives (x, y) def on_click(state, side_name, evt: gr.SelectData): # Determine tile from coordinates x, y = evt.index[0], evt.index[1] col = min(int(x // TILE_SIZE), GRID_SIZE - 1) row = min(int(y // TILE_SIZE), GRID_SIZE - 1) idx = row * GRID_SIZE + col # Center is locked if idx == 4: return state, side_to_image(state, side_name) # cycle color grid = state[side_name]["grid"] grid[idx] = cycle_color(grid[idx]) state[side_name]["unlocked"] = False return state, side_to_image(state, side_name) # ----- Build UI ----- with gr.Blocks(title="3x3 Rubik's-Style Color Password") as demo: gr.Markdown( """ # 3×3 Rubik's‑Style Color Password System **Click tiles to cycle colors:** Red → Yellow → Green → Blue → White → Orange → (repeat). The center tile is locked to the side's middle color. """ ) app_state = gr.State(init_state()) with gr.Row(): side_dropdown = gr.Dropdown(choices=SIDE_NAMES, value=SIDE_NAMES[0], label="Choose Side") with gr.Row(): grid_image = gr.Image(type="pil", label="Current Grid", height=IMG_SIZE, width=IMG_SIZE, interactive=True) with gr.Column(): status = gr.Markdown("Status: No code saved") with gr.Row(): btn_random = gr.Button("🔀 Randomize") btn_set = gr.Button("💾 Set Code") with gr.Row(): btn_unlock = gr.Button("🔓 Unlock") btn_reset_side = gr.Button("♻️ Reset Side") with gr.Row(): btn_reset_all = gr.Button("🧹 Reset All (All Sides)") show_code = gr.Checkbox(label="Show Code Preview", value=True) code_image = gr.Image(type="pil", label="Saved Code (Preview)", height=120, width=120, interactive=False) def refresh_side(state, side_name, show_code_flag): # update main image, status, and code preview return ( side_to_image(state, side_name), status_text(state, side_name), code_preview(state, side_name) if show_code_flag else None, ) side_dropdown.change( refresh_side, [app_state, side_dropdown, show_code], [grid_image, status, code_image], ) show_code.change( lambda state, side, flag: code_preview(state, side) if flag else None, [app_state, side_dropdown, show_code], [code_image], ) btn_random.click( randomize_side, [app_state, side_dropdown], [app_state, grid_image, status, code_image], ) btn_set.click( set_code, [app_state, side_dropdown], [app_state, gr.Textbox(visible=False), code_image], # hidden toast text placeholder ) btn_unlock.click( unlock, [app_state, side_dropdown], [app_state, status, code_image], ) btn_reset_side.click( reset_side, [app_state, side_dropdown], [app_state, grid_image, status, code_image], ) btn_reset_all.click( lambda state: init_state(), [app_state], [app_state], ).then( refresh_side, [app_state, side_dropdown, show_code], [grid_image, status, code_image], ) grid_image.select( on_click, [app_state, side_dropdown], [app_state, grid_image], ) # Initialize the first view demo.load( refresh_side, [app_state, side_dropdown, show_code], [grid_image, status, code_image], ) if __name__ == "__main__": demo.launch()