damla921's picture
Upload 3 files
9908e01 verified
# 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()