from js import document, setInterval, clearInterval, alert from pyodide.ffi import create_proxy import random import time GRID_SIZE = 4 puzzle = [] move_count = 0 start_time = None timer_interval = None def create_solvable_puzzle(): numbers = list(range(1, GRID_SIZE * GRID_SIZE)) numbers.append(0) while True: random.shuffle(numbers) if is_solvable(numbers): return numbers def is_solvable(numbers): inv_count = 0 for i in range(len(numbers)): for j in range(i + 1, len(numbers)): if numbers[i] and numbers[j] and numbers[i] > numbers[j]: inv_count += 1 empty_row = GRID_SIZE - (numbers.index(0) // GRID_SIZE) if GRID_SIZE % 2 == 1: return inv_count % 2 == 0 else: if empty_row % 2 == 0: return inv_count % 2 == 1 else: return inv_count % 2 == 0 def draw(): container = document.getElementById("puzzle-container") container.innerHTML = "" for i, value in enumerate(puzzle): tile = document.createElement("div") if value == 0: tile.className = "tile empty" else: tile.className = "tile" tile.innerText = str(value) tile.addEventListener( "click", create_proxy(lambda event, idx=i: move_tile(idx)) ) container.appendChild(tile) def move_tile(index): global move_count empty_index = puzzle.index(0) row, col = divmod(index, GRID_SIZE) empty_row, empty_col = divmod(empty_index, GRID_SIZE) if (abs(row - empty_row) == 1 and col == empty_col) or \ (abs(col - empty_col) == 1 and row == empty_row): puzzle[empty_index], puzzle[index] = puzzle[index], puzzle[empty_index] move_count += 1 document.getElementById("moves").innerText = move_count draw() if is_winner(): stop_timer() alert(f"You won in {move_count} moves and {int(time.time() - start_time)} seconds!") def is_winner(): return puzzle == list(range(1, GRID_SIZE * GRID_SIZE)) + [0] def start_timer(): global start_time, timer_interval start_time = time.time() def update_timer(): elapsed = int(time.time() - start_time) document.getElementById("timer").innerText = elapsed timer_interval = setInterval(create_proxy(update_timer), 1000) def stop_timer(): global timer_interval if timer_interval: clearInterval(timer_interval) def restart_game(event=None): global puzzle, move_count stop_timer() puzzle = create_solvable_puzzle() move_count = 0 document.getElementById("moves").innerText = 0 document.getElementById("timer").innerText = 0 draw() start_timer() def set_level(event): alert("Level selection not implemented yet!") print("Level button clicked") def show_help(event): alert("Help not implemented yet!") # Bind restart button restart_button = document.getElementById("restart-btn") restart_button.addEventListener("click", create_proxy(restart_game)) # Bind level button level_button = document.getElementById("level-btn") level_button.addEventListener("click", create_proxy(set_level)) # Bind help button help_button = document.getElementById("help-btn") help_button.addEventListener("click", create_proxy(show_help)) # Initialize game restart_game()