Prima Funzionante

This commit is contained in:
Flatlogic Bot 2025-09-28 22:38:17 +00:00
parent d83f1256da
commit 8554409a47
130 changed files with 953 additions and 124 deletions

298
admin.php Normal file
View File

@ -0,0 +1,298 @@
<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
session_start();
// Gatekeeper: redirect if not logged in
if (!isset($_SESSION['is_admin']) || $_SESSION['is_admin'] !== true) {
header('Location: login.php');
exit;
}
require_once 'db/config.php';
$pdo = db();
// --- IMAGE SLICING FUNCTION ---
function create_puzzle_pieces($source_image_path, $puzzle_id, $pieces_count) {
if (!function_exists('gd_info')) {
// GD library not available
error_log("GD Library is not installed or enabled.");
return false;
}
$image_info = getimagesize($source_image_path);
$source_width = $image_info[0];
$source_height = $image_info[1];
$mime_type = $image_info['mime'];
switch ($mime_type) {
case 'image/jpeg':
$source_image = imagecreatefromjpeg($source_image_path);
break;
case 'image/png':
$source_image = imagecreatefrompng($source_image_path);
break;
case 'image/gif':
$source_image = imagecreatefromgif($source_image_path);
break;
default:
error_log("Unsupported image type: " . $mime_type);
return false;
}
if (!$source_image) {
error_log("Failed to create image from source.");
return false;
}
// Grid dimensions
$cols = 0;
$rows = 0;
if ($pieces_count == 16) { $cols = 4; $rows = 4; }
elseif ($pieces_count == 32) { $cols = 8; $rows = 4; }
elseif ($pieces_count == 64) { $cols = 8; $rows = 8; }
else {
error_log("Invalid pieces count: " . $pieces_count);
return false;
}
$piece_width = floor($source_width / $cols);
$piece_height = floor($source_height / $rows);
$output_dir = __DIR__ . '/puzzles/' . $puzzle_id . '/' . $pieces_count;
if (!is_dir($output_dir)) {
mkdir($output_dir, 0777, true);
}
for ($r = 0; $r < $rows; $r++) {
for ($c = 0; $c < $cols; $c++) {
$piece = imagecreatetruecolor($piece_width, $piece_height);
imagecopy(
$piece,
$source_image,
0, 0, // dest x, y
$c * $piece_width, $r * $piece_height, // source x, y
$piece_width, $piece_height
);
$piece_path = $output_dir . '/piece_' . $r . '_' . $c . '.jpg';
imagejpeg($piece, $piece_path, 90); // Save as JPG with 90% quality
imagedestroy($piece);
}
}
imagedestroy($source_image);
return true;
}
// Handle new puzzle upload
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_puzzle'])) {
$puzzle_name = trim($_POST['puzzle_name'] ?? '');
$pieces = (int)($_POST['pieces'] ?? 16);
$is_public = isset($_POST['is_public']) ? 1 : 0;
$image_file = $_FILES['puzzle_image'] ?? null;
// Basic validation
if (!empty($puzzle_name) && $image_file && $image_file['error'] === UPLOAD_ERR_OK) {
$upload_dir = __DIR__ . '/uploads/';
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0777, true);
}
// Sanitize filename and create a unique name
$original_filename = basename($image_file['name']);
$image_extension = pathinfo($original_filename, PATHINFO_EXTENSION);
$safe_filename = 'puzzle_' . uniqid() . '.' . $image_extension;
$upload_path = $upload_dir . $safe_filename;
// Move the file
if (move_uploaded_file($image_file['tmp_name'], $upload_path)) {
// Insert into database
$stmt = $pdo->prepare('INSERT INTO puzzles (name, original_image, pieces, is_public, created_at) VALUES (?, ?, ?, ?, NOW())');
$stmt->execute([$puzzle_name, $safe_filename, $pieces, $is_public]);
$puzzle_id = $pdo->lastInsertId();
// Create puzzle pieces
create_puzzle_pieces($upload_path, $puzzle_id, $pieces);
header("Location: admin.php?success=1");
exit;
}
}
// Handle error case
header("Location: admin.php?error=1");
exit;
}
// Handle puzzle deletion
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_puzzle'])) {
$puzzle_id_to_delete = $_POST['puzzle_id'];
// First, get the original image filename to delete it
$stmt = $pdo->prepare('SELECT original_image FROM puzzles WHERE id = ?');
$stmt->execute([$puzzle_id_to_delete]);
$puzzle = $stmt->fetch(PDO::FETCH_ASSOC);
if ($puzzle) {
// Delete original image file
$original_image_path = __DIR__ . '/uploads/' . $puzzle['original_image'];
if (file_exists($original_image_path)) {
unlink($original_image_path);
}
}
// Delete from DB
$stmt = $pdo->prepare('DELETE FROM puzzles WHERE id = ?');
$stmt->execute([$puzzle_id_to_delete]);
// Delete puzzle pieces directory
$puzzle_dir = __DIR__ . '/puzzles/' . $puzzle_id_to_delete;
if (is_dir($puzzle_dir)) {
// A simple recursive delete function
function delete_directory($dir) {
if (!is_dir($dir)) return;
$files = array_diff(scandir($dir), array('.','..'));
foreach ($files as $file) {
(is_dir("$dir/$file")) ? delete_directory("$dir/$file") : unlink("$dir/$file");
}
rmdir($dir);
}
delete_directory($puzzle_dir);
}
header("Location: admin.php?deleted=1"); // Redirect to avoid re-posting
exit;
}
$puzzles = $pdo->query('SELECT id, name, original_image, pieces, is_public, created_at FROM puzzles ORDER BY created_at DESC')->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pannello di Amministrazione</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.upload-form {
background-color: #f8f9fa;
padding: 2rem;
border-radius: 0.5rem;
margin-bottom: 2rem;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="#">Admin Puzzle</a>
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="logout.php">Logout</a>
</li>
</ul>
</div>
</nav>
<div class="container mt-5">
<?php if (isset($_GET['success'])): ?>
<div class="alert alert-success">Puzzle aggiunto con successo!</div>
<?php endif; ?>
<?php if (isset($_GET['deleted'])): ?>
<div class="alert alert-info">Puzzle eliminato con successo.</div>
<?php endif; ?>
<?php if (isset($_GET['error'])): ?>
<div class="alert alert-danger">Errore durante l'upload. Controlla i dati e riprova.</div>
<?php endif; ?>
<!-- Upload Form -->
<div class="upload-form">
<h2 class="mb-4">Aggiungi Nuovo Puzzle</h2>
<form action="admin.php" method="POST" enctype="multipart/form-data">
<div class="mb-3">
<label for="puzzle_name" class="form-label">Nome del Puzzle</label>
<input type="text" class="form-control" id="puzzle_name" name="puzzle_name" required>
</div>
<div class="mb-3">
<label for="puzzle_image" class="form-label">Immagine</label>
<input type="file" class="form-control" id="puzzle_image" name="puzzle_image" accept="image/jpeg, image/png, image/gif" required>
</div>
<div class="mb-3">
<label for="pieces" class="form-label">Numero di Pezzi</label>
<select class="form-select" id="pieces" name="pieces">
<option value="16">16 (4x4)</option>
<option value="32">32 (8x4)</option>
<option value="64">64 (8x8)</option>
</select>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" id="is_public" name="is_public" value="1" checked>
<label class="form-check-label" for="is_public">
Rendi Pubblico
</label>
</div>
<button type="submit" name="add_puzzle" class="btn btn-primary">Aggiungi Puzzle</button>
</form>
</div>
<h1 class="mb-4">Gestione Puzzle</h1>
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Nome</th>
<th>Immagine</th>
<th>Pezzi</th>
<th>Pubblico</th>
<th>Creato il</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
<?php if (empty($puzzles)): ?>
<tr>
<td colspan="7" class="text-center">Nessun puzzle trovato.</td>
</tr>
<?php else: ?>
<?php foreach ($puzzles as $puzzle):
$image_path = __DIR__ . '/uploads/' . $puzzle['original_image'];
if (file_exists($image_path)) {
// Cache busting for images
$imageUrl = 'uploads/' . htmlspecialchars($puzzle['original_image']) . '?v=' . filemtime($image_path);
} else {
$imageUrl = 'assets/images/placeholder.png'; // A placeholder if image is missing
}
?>
<tr>
<td><?php echo htmlspecialchars($puzzle['id']); ?></td>
<td><a href="puzzle.php?id=<?php echo htmlspecialchars($puzzle['id']); ?>" target="_blank"><?php echo htmlspecialchars($puzzle['name']); ?></a></td>
<td><img src="<?php echo $imageUrl; ?>" alt="Puzzle" width="100"></td>
<td><?php echo htmlspecialchars($puzzle['pieces']); ?></td>
<td><?php echo $puzzle['is_public'] ? 'Sì' : 'No'; ?></td>
<td><?php echo htmlspecialchars($puzzle['created_at']); ?></td>
<td>
<form method="POST" action="admin.php" onsubmit="return confirm('Sei sicuro di voler eliminare questo puzzle? L'azione è irreversibile.');">
<input type="hidden" name="puzzle_id" value="<?php echo htmlspecialchars($puzzle['id']); ?>">
<button type="submit" name="delete_puzzle" class="btn btn-danger btn-sm">Elimina</button>
</form>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<footer class="text-center mt-5 py-3 bg-light">
<p>&copy; <?php echo date("Y"); ?> Puzzle Game</p>
</footer>
</body>
</html>

114
assets/css/custom.css Normal file
View File

@ -0,0 +1,114 @@
:root {
--bg-slate-900: #0F172A;
--bg-slate-800: #1E293B;
--text-cyan-400: #22D3EE;
--bg-cyan-500: #06B6D4;
--border-slate-700: #334155;
--text-slate-200: #E2E8F0;
--text-slate-400: #94A3B8;
}
body {
background-color: var(--bg-slate-900);
color: var(--text-slate-200);
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
.navbar {
background-color: rgba(30, 41, 59, 0.8); /* bg-slate-800 with opacity */
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border-bottom: 1px solid var(--border-slate-700);
}
.puzzle-board {
background-color: var(--bg-slate-900);
border: 2px dashed var(--border-slate-700);
aspect-ratio: 4 / 3;
transition: background-color 0.3s;
}
.pieces-tray {
background-color: var(--bg-slate-800);
border-radius: 0.75rem;
max-height: 80vh;
overflow-y: auto;
border: 1px solid var(--border-slate-700);
}
.btn-cyan {
background-color: var(--bg-cyan-500);
color: var(--bg-slate-900);
font-weight: bold;
border: none;
transition: background-color 0.2s;
}
.btn-cyan:hover {
background-color: var(--text-cyan-400);
color: var(--bg-slate-900);
}
.form-select {
background-color: var(--bg-slate-800);
color: var(--text-slate-200);
border-color: var(--border-slate-700);
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%2394a3b8' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}
.form-select:focus {
background-color: var(--bg-slate-800);
color: var(--text-slate-200);
border-color: var(--text-cyan-400);
box-shadow: 0 0 0 0.25rem rgba(34, 211, 238, 0.25);
}
.text-cyan {
color: var(--text-cyan-400);
}
.text-slate-400 {
color: var(--text-slate-400);
}
.rounded-xl {
border-radius: 0.75rem;
}
/* --- Gallery --- */
.gallery-scroll-container {
overflow-x: auto;
scrollbar-width: thin;
scrollbar-color: var(--border-slate-700) var(--bg-slate-900);
}
.gallery-scroll-container::-webkit-scrollbar {
height: 8px;
}
.gallery-scroll-container::-webkit-scrollbar-track {
background: var(--bg-slate-900);
}
.gallery-scroll-container::-webkit-scrollbar-thumb {
background-color: var(--border-slate-700);
border-radius: 10px;
border: 2px solid var(--bg-slate-900);
}
.gallery-item {
display: block;
flex: 0 0 auto; /* Prevent shrinking */
width: 150px; /* Fixed width for items */
height: 112px; /* Fixed height for items (4:3 ratio) */
transition: transform 0.2s ease-in-out;
}
.gallery-item:hover {
transform: scale(1.05);
}
.gallery-item img {
width: 100%;
height: 100%;
object-fit: cover; /* Ensure images fill the container */
}

3
assets/js/main.js Normal file
View File

@ -0,0 +1,3 @@
// Custom JavaScript will go here in future steps.
console.log("Photo Puzzle UI loaded.");

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -5,6 +5,10 @@ define('DB_NAME', 'app_30903');
define('DB_USER', 'app_30903');
define('DB_PASS', '7d2d5a2d-6e5e-4580-a9b7-3f8e7288a494');
// Admin credentials (for a real app, use hashed passwords)
define('ADMIN_USER', 'admin');
define('ADMIN_PASS_HASH', '$2y$10$WnTccK10o9fJmz3xfs6gV.0GB2PH.Smtm4NOlqlnr7SmEgMcY8uC.'); // Hash for 'MaviGames'
function db() {
static $pdo;
if (!$pdo) {

353
index.php
View File

@ -1,131 +1,236 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
session_start();
require_once 'db/config.php';
// --- DATABASE SETUP ---
function setup_database() {
try {
$pdo = db();
$pdo->exec("CREATE TABLE IF NOT EXISTS puzzles (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) DEFAULT 'Senza Nome',
file_name VARCHAR(255) NOT NULL UNIQUE,
is_public BOOLEAN DEFAULT FALSE,
is_admin_upload BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)");
// Attempt to add 'name' column, ignore error if it already exists
try {
$pdo->exec("ALTER TABLE puzzles ADD COLUMN name VARCHAR(255) DEFAULT 'Senza Nome'");
} catch (PDOException $e) {
// Ignore error, likely "Duplicate column name"
}
} catch (PDOException $e) {
// In a real app, log this error instead of displaying it
die("Errore di connessione al database: " . $e->getMessage());
}
}
setup_database();
// --- GLOBAL VARIABLES ---
$uploaded_image = null;
$error_message = null;
$gallery_images = [];
// --- DATA FETCHING ---
try {
$pdo = db();
$stmt = $pdo->query("SELECT id, file_name, name FROM puzzles WHERE is_public = 1 OR is_admin_upload = 1 ORDER BY created_at DESC");
$gallery_images = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
$error_message = "Impossibile caricare la galleria.";
}
// --- POST REQUEST: IMAGE UPLOAD ---
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['puzzle_image'])) {
$target_dir = 'uploads/';
$max_file_size = 5 * 1024 * 1024; // 5 MB
$allowed_types = ['image/jpeg', 'image/png', 'image/gif'];
$file = $_FILES['puzzle_image'];
if ($file['error'] !== UPLOAD_ERR_OK) {
$error_message = 'Si è verificato un errore durante il caricamento.';
} else {
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime_type = $finfo->file($file['tmp_name']);
if ($file['size'] > $max_file_size) {
$error_message = 'Il file è troppo grande. La dimensione massima è 5MB.';
} elseif (!in_array($mime_type, $allowed_types)) {
$error_message = 'Tipo di file non valido. Sono ammessi solo JPG, PNG, GIF.';
} else {
$file_extension = pathinfo($file['name'], PATHINFO_EXTENSION);
$safe_filename = uniqid('puzzle_', true) . '.' . strtolower($file_extension);
$target_file = $target_dir . $safe_filename;
if (move_uploaded_file($file['tmp_name'], $target_file)) {
// Save to database
try {
$is_public = isset($_POST['share_puzzle']) ? 1 : 0; // Ensure 1 or 0
$puzzle_name = !empty($_POST['puzzle_name']) ? trim($_POST['puzzle_name']) : 'Senza Nome';
$pdo = db();
$stmt = $pdo->prepare("INSERT INTO puzzles (name, file_name, is_public, is_admin_upload) VALUES (?, ?, ?, ?)");
$is_admin_upload = 0; // Always false for user uploads
$stmt->execute([$puzzle_name, $safe_filename, $is_public, $is_admin_upload]);
$new_puzzle_id = $pdo->lastInsertId();
} catch (PDOException $e) {
// Error saving to DB, delete file
unlink($target_file);
// Log the detailed error for developers: error_log($e->getMessage());
$error_message = "Si è verificato un errore nel salvataggio dei dati del puzzle: " . $e->getMessage();
}
if (!$error_message) {
// Redirect to the new puzzle page
header('Location: puzzle.php?id=' . $new_puzzle_id);
exit;
}
} else {
$error_message = 'Si è verificato un errore durante il salvataggio del file.';
}
}
}
}
// --- GET REQUESTS ---
// Handle New Game
if (isset($_GET['action']) && $_GET['action'] === 'new') {
unset($_SESSION['selected_puzzle']);
header('Location: ' . $_SERVER['PHP_SELF']);
exit;
}
// --- SET CURRENT IMAGE FOR DISPLAY ---
if (isset($_SESSION['selected_puzzle'])) {
$uploaded_image = $_SESSION['selected_puzzle'];
}
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
?>
<!doctype html>
<html lang="en">
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Photo Puzzle</title>
<meta name="description" content="Crea e risolvi puzzle fotografici direttamente nel tuo browser. Scegli dalla nostra galleria o carica la tua immagine.">
<!-- Open Graph -->
<meta property="og:title" content="Photo Puzzle - Gioca Ora!">
<meta property="og:description" content="Un gioco di puzzle moderno dove puoi usare le tue immagini o scegliere dalla galleria.">
<meta property="og:type" content="website">
<meta property="og:url" content=""> <!-- Add site URL here -->
<meta property="og:image" content="https://picsum.photos/1200/630?random=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<!-- Custom CSS -->
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWiZZy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
</div>
</main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer>
<body class="d-flex flex-column min-vh-100">
<!-- Sticky Header -->
<header class="navbar navbar-expand-lg sticky-top">
<div class="container-fluid">
<a class="navbar-brand fw-bold text-cyan" href="index.php">Photo Puzzle</a>
<div class="d-flex align-items-center ms-auto">
<div class="me-2 me-md-3">
<label for="difficulty" class="form-label visually-hidden">Difficoltà</label>
<select class="form-select form-select-sm" id="difficulty">
<option value="32">Facile</option>
<option value="48">Medio</option>
<option value="64">Difficile</option>
</select>
</div>
<a href="?action=new" class="btn btn-cyan btn-sm">Nuova Partita</a>
</div>
</div>
</header>
<!-- Main Content -->
<main class="container-fluid my-3 my-md-4 flex-grow-1">
<!-- Gallery Section -->
<?php if (!empty($gallery_images)): ?>
<section class="mb-4" aria-labelledby="gallery-heading">
<h2 id="gallery-heading" class="h4 text-cyan">Galleria Pubblica</h2>
<div class="gallery-scroll-container d-flex gap-3 pb-3">
<?php foreach ($gallery_images as $img): ?>
<a href="puzzle.php?id=<?php echo $img['id']; ?>" class="gallery-item" title="<?php echo htmlspecialchars($img['name']); ?>">
<img src="uploads/<?php echo htmlspecialchars($img['file_name']); ?>" alt="<?php echo htmlspecialchars($img['name']); ?>" loading="lazy" class="rounded-xl">
</a>
<?php endforeach; ?>
</div>
</section>
<?php endif; ?>
<div class="row g-3 g-md-4">
<!-- Puzzle Board -->
<section class="col-lg-8" aria-labelledby="puzzle-board-heading">
<div class="puzzle-board rounded-xl d-flex align-items-center justify-content-center p-3">
<div class="text-center w-100">
<h1 id="puzzle-board-heading" class="visually-hidden">Tavola del Puzzle</h1>
<?php if ($error_message): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
<?php endif; ?>
<?php if ($uploaded_image && file_exists($uploaded_image)): ?>
<img src="<?php echo htmlspecialchars($uploaded_image); ?>" alt="Immagine del puzzle caricata" class="img-fluid rounded-xl">
<?php else: ?>
<h2 class="h4 text-cyan">Crea un nuovo Puzzle</h2>
<p class="text-slate-400 mb-4">Carica un'immagine per iniziare.</p>
<form action="index.php" method="post" enctype="multipart/form-data" style="max-width: 500px; margin: auto;">
<div class="mb-3 text-start">
<label for="puzzle_name" class="form-label">Nome del Puzzle</label>
<input type="text" class="form-control" id="puzzle_name" name="puzzle_name" placeholder="Es: Tramonto a Venezia" required>
</div>
<div class="mb-3 text-start">
<label for="puzzle_image_upload" class="form-label">Seleziona immagine</label>
<input class="form-control" type="file" name="puzzle_image" id="puzzle_image_upload" accept="image/jpeg,image/png,image/gif" required>
</div>
<div class="form-check form-switch mb-3 text-start">
<input class="form-check-input" type="checkbox" role="switch" id="share_puzzle" name="share_puzzle" value="1" checked>
<label class="form-check-label" for="share_puzzle">Condividi nella galleria pubblica</label>
</div>
<div class="text-center">
<button type="submit" class="btn btn-cyan">Crea Puzzle</button>
</div>
</form>
<?php endif; ?>
</div>
</div>
</section>
<!-- Pieces Tray -->
<aside class="col-lg-4" aria-labelledby="pieces-tray-heading">
<div class="pieces-tray p-3">
<h2 id="pieces-tray-heading" class="h4 text-cyan mb-3">Pezzi</h2>
<div class="d-flex justify-content-center align-items-center" style="min-height: 200px;">
<p class="text-slate-400">I pezzi del puzzle appariranno qui.</p>
</div>
<!-- Pieces will be dynamically added here -->
</div>
</aside>
</div>
</main>
<!-- Footer -->
<footer class="text-center text-slate-400 py-3 mt-auto">
<small>&copy; <?php echo date("Y"); ?> Photo Puzzle. Creato con ❤️.</small>
<small class="d-block"><a href="admin.php" class="text-slate-400">Admin</a></small>
</footer>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<!-- Custom JS -->
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>

71
login.php Normal file
View File

@ -0,0 +1,71 @@
<?php
session_start();
require_once 'db/config.php';
// If already logged in, redirect to admin panel
if (isset($_SESSION['is_admin']) && $_SESSION['is_admin'] === true) {
header('Location: admin.php');
exit;
}
$error_message = '';
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
if ($username === ADMIN_USER && password_verify($password, ADMIN_PASS_HASH)) {
$_SESSION['is_admin'] = true;
session_write_close(); // Force session to save before redirect
header('Location: admin.php');
exit;
} else {
$error_message = 'Credenziali non valide. Riprova.';
}
}
?>
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Login</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #f8f9fa;
}
.login-container {
max-width: 400px;
padding: 2rem;
border: 1px solid #dee2e6;
border-radius: 0.5rem;
background-color: #fff;
}
</style>
</head>
<body>
<div class="login-container">
<h2 class="text-center mb-4">Accesso Admin</h2>
<?php if ($error_message): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
<?php endif; ?>
<form method="post" action="login.php">
<div class="mb-3">
<label for="username" class="form-label">Nome Utente</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<button type="submit" class="btn btn-primary w-100">Accedi</button>
</form>
</div>
</body>
</html>

6
logout.php Normal file
View File

@ -0,0 +1,6 @@
<?php
session_start();
session_unset();
session_destroy();
header('Location: login.php');
exit;

228
puzzle.php Normal file
View File

@ -0,0 +1,228 @@
<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
require_once 'db/config.php';
// --- VARIABLES ---
$puzzle_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
$difficulty = isset($_GET['difficulty']) ? (int)$_GET['difficulty'] : 16;
// Determine grid size based on difficulty
$valid_difficulties = [
16 => [4, 4], // 4x4 grid
32 => [8, 4], // 8x4 grid
64 => [8, 8] // 8x8 grid
];
if (!isset($valid_difficulties[$difficulty])) {
$difficulty = 16; // Fallback to default if invalid
}
list($cols, $rows) = $valid_difficulties[$difficulty];
$puzzle = null;
$error_message = '';
$pieces = [];
$source_width = 1;
$source_height = 1;
// --- DATA FETCHING & PUZZLE CREATION ---
if ($puzzle_id > 0) {
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM puzzles WHERE id = ?");
$stmt->execute([$puzzle_id]);
$puzzle = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$puzzle) {
$error_message = "Puzzle non trovato.";
} else {
$image_path = 'uploads/' . $puzzle['file_name'];
if (!file_exists($image_path)) {
$error_message = "File immagine del puzzle non trovato.";
} else {
$image_info = getimagesize($image_path);
$source_width = $image_info[0];
$source_height = $image_info[1];
// Use a difficulty-specific directory
$pieces_dir = sprintf('puzzles/%d/%d', $puzzle['id'], $difficulty);
if (!is_dir($pieces_dir)) {
mkdir($pieces_dir, 0777, true);
}
$piece_files = glob($pieces_dir . '/piece_*.jpg');
if (count($piece_files) !== ($cols * $rows)) {
foreach ($piece_files as $file) { unlink($file); }
$mime_type = $image_info['mime'];
$source_image = null;
switch ($mime_type) {
case 'image/jpeg': $source_image = imagecreatefromjpeg($image_path); break;
case 'image/png': $source_image = imagecreatefrompng($image_path); break;
case 'image/gif': $source_image = imagecreatefromgif($image_path); break;
default: $error_message = "Formato immagine non supportato."; break;
}
if ($source_image) {
$piece_width = floor($source_width / $cols);
$piece_height = floor($source_height / $rows);
for ($y = 0; $y < $rows; $y++) {
for ($x = 0; $x < $cols; $x++) {
$piece = imagecreatetruecolor($piece_width, $piece_height);
imagecopy($piece, $source_image, 0, 0, $x * $piece_width, $y * $piece_height, $piece_width, $piece_height);
imagejpeg($piece, "{$pieces_dir}/piece_{$y}_{$x}.jpg", 95);
imagedestroy($piece);
}
}
imagedestroy($source_image);
}
}
$pieces = glob($pieces_dir . '/piece_*.jpg');
shuffle($pieces);
}
}
} catch (PDOException $e) {
$error_message = "Errore di sistema: " . $e->getMessage();
}
} else {
$error_message = "ID del puzzle non valido.";
}
?>
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Risolvi: <?php echo $puzzle ? htmlspecialchars($puzzle['name']) : 'Puzzle'; ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<style>
#puzzle-board {
border: 2px dashed #ccc;
background-color: #f8f9fa;
display: grid;
grid-template-columns: repeat(<?php echo $cols; ?>, 1fr);
grid-template-rows: repeat(<?php echo $rows; ?>, 1fr);
aspect-ratio: <?php echo $source_width; ?> / <?php echo $source_height; ?>;
max-width: 100%;
}
.drop-zone { border: 1px solid #eee; width: 100%; height: 100%; }
.drop-zone.hovered { background-color: #e9ecef; }
.puzzle-piece { cursor: grab; width: 100%; height: 100%; object-fit: cover; box-shadow: 0 0 5px rgba(0,0,0,0.5); }
#pieces-tray { display: flex; flex-wrap: wrap; justify-content: center; align-items: flex-start; min-height: 150px; background-color: #f0f0f0; border-radius: 10px; }
#pieces-tray .puzzle-piece { width: 80px; height: auto; margin: 4px; }
#win-message { display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: rgba(40, 167, 69, 0.95); color: white; padding: 40px; border-radius: 15px; text-align: center; z-index: 1000; }
</style>
</head>
<body>
<div id="win-message">
<h2>Complimenti!</h2>
<p>Hai risolto il puzzle!</p>
<a href="index.php" class="btn btn-light">Gioca Ancora</a>
</div>
<div class="container mt-4">
<div class="text-center mb-4">
<h1><?php echo $puzzle ? htmlspecialchars($puzzle['name']) : 'Risolvi il Puzzle'; ?></h1>
<a href="index.php" class="btn btn-secondary btn-sm">Torna alla Galleria</a>
</div>
<?php if ($error_message): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
<?php elseif ($puzzle): ?>
<div class="text-center mb-3">
<form method="GET" action="puzzle.php" class="d-inline-flex align-items-center">
<input type="hidden" name="id" value="<?php echo $puzzle_id; ?>">
<label for="difficulty" class="form-label me-2 mb-0">Difficoltà:</label>
<select name="difficulty" id="difficulty" class="form-select form-select-sm" onchange="this.form.submit()">
<option value="16" <?php if ($difficulty == 16) echo 'selected'; ?>>Facile (16 pezzi)</option>
<option value="32" <?php if ($difficulty == 32) echo 'selected'; ?>>Medio (32 pezzi)</option>
<option value="64" <?php if ($difficulty == 64) echo 'selected'; ?>>Difficile (64 pezzi)</option>
</select>
</form>
</div>
<div class="row">
<div class="col-lg-8 col-md-12 mb-3">
<div id="puzzle-board"></div>
</div>
<div class="col-lg-4 col-md-12">
<h2 class="h5">I Tuoi Pezzi</h2>
<div id="pieces-tray" class="p-2">
<?php foreach ($pieces as $piece_path): ?>
<img src="<?php echo htmlspecialchars($piece_path); ?>" class="puzzle-piece" alt="Pezzo del puzzle" draggable="true">
<?php endforeach; ?>
</div>
</div>
</div>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const board = document.getElementById('puzzle-board');
const piecesTray = document.getElementById('pieces-tray');
const pieces = document.querySelectorAll('.puzzle-piece');
const cols = <?php echo $cols; ?>;
const rows = <?php echo $rows; ?>;
let draggedPiece = null;
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
const dropZone = document.createElement('div');
dropZone.classList.add('drop-zone');
dropZone.dataset.position = `${y}-${x}`;
board.appendChild(dropZone);
dropZone.addEventListener('dragover', e => { e.preventDefault(); dropZone.classList.add('hovered'); });
dropZone.addEventListener('dragleave', () => dropZone.classList.remove('hovered'));
dropZone.addEventListener('drop', e => {
e.preventDefault();
dropZone.classList.remove('hovered');
if (draggedPiece && dropZone.children.length === 0) {
dropZone.appendChild(draggedPiece);
checkWin();
}
});
}
}
piecesTray.addEventListener('dragover', e => e.preventDefault());
piecesTray.addEventListener('drop', e => {
e.preventDefault();
if (draggedPiece) { piecesTray.appendChild(draggedPiece); }
});
pieces.forEach(piece => {
const match = piece.src.match(/piece_(\d+)_(\d+)\.jpg$/);
if (match) { piece.dataset.position = `${match[1]}-${match[2]}`; }
piece.addEventListener('dragstart', e => {
draggedPiece = e.target;
setTimeout(() => { e.target.style.opacity = '0.5'; }, 0);
});
piece.addEventListener('dragend', e => {
draggedPiece = null;
e.target.style.opacity = '1';
});
});
function checkWin() {
const dropZones = board.querySelectorAll('.drop-zone');
if ([...dropZones].every(zone =>
zone.children.length > 0 &&
zone.children[0].dataset.position === zone.dataset.position
)) {
document.getElementById('win-message').style.display = 'block';
board.style.borderColor = '#28a745';
}
}
});
</script>
</body>
</html>

BIN
puzzles/6/16/piece_0_0.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
puzzles/6/16/piece_0_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
puzzles/6/16/piece_0_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
puzzles/6/16/piece_0_3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
puzzles/6/16/piece_1_0.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
puzzles/6/16/piece_1_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
puzzles/6/16/piece_1_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
puzzles/6/16/piece_1_3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
puzzles/6/16/piece_2_0.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
puzzles/6/16/piece_2_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
puzzles/6/16/piece_2_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
puzzles/6/16/piece_2_3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
puzzles/6/16/piece_3_0.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
puzzles/6/16/piece_3_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
puzzles/6/16/piece_3_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

BIN
puzzles/6/16/piece_3_3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
puzzles/6/32/piece_0_0.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
puzzles/6/32/piece_0_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
puzzles/6/32/piece_0_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
puzzles/6/32/piece_0_3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
puzzles/6/32/piece_0_4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
puzzles/6/32/piece_0_5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
puzzles/6/32/piece_0_6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
puzzles/6/32/piece_0_7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
puzzles/6/32/piece_1_0.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
puzzles/6/32/piece_1_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
puzzles/6/32/piece_1_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
puzzles/6/32/piece_1_3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
puzzles/6/32/piece_1_4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
puzzles/6/32/piece_1_5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
puzzles/6/32/piece_1_6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
puzzles/6/32/piece_1_7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
puzzles/6/32/piece_2_0.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
puzzles/6/32/piece_2_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
puzzles/6/32/piece_2_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
puzzles/6/32/piece_2_3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
puzzles/6/32/piece_2_4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
puzzles/6/32/piece_2_5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
puzzles/6/32/piece_2_6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
puzzles/6/32/piece_2_7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
puzzles/6/32/piece_3_0.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
puzzles/6/32/piece_3_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
puzzles/6/32/piece_3_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
puzzles/6/32/piece_3_3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
puzzles/6/32/piece_3_4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
puzzles/6/32/piece_3_5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
puzzles/6/32/piece_3_6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
puzzles/6/32/piece_3_7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
puzzles/6/64/piece_0_0.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
puzzles/6/64/piece_0_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
puzzles/6/64/piece_0_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
puzzles/6/64/piece_0_3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
puzzles/6/64/piece_0_4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
puzzles/6/64/piece_0_5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
puzzles/6/64/piece_0_6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
puzzles/6/64/piece_0_7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
puzzles/6/64/piece_1_0.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
puzzles/6/64/piece_1_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
puzzles/6/64/piece_1_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
puzzles/6/64/piece_1_3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
puzzles/6/64/piece_1_4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
puzzles/6/64/piece_1_5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
puzzles/6/64/piece_1_6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
puzzles/6/64/piece_1_7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
puzzles/6/64/piece_2_0.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
puzzles/6/64/piece_2_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
puzzles/6/64/piece_2_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
puzzles/6/64/piece_2_3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
puzzles/6/64/piece_2_4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
puzzles/6/64/piece_2_5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
puzzles/6/64/piece_2_6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
puzzles/6/64/piece_2_7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
puzzles/6/64/piece_3_0.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
puzzles/6/64/piece_3_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
puzzles/6/64/piece_3_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
puzzles/6/64/piece_3_3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
puzzles/6/64/piece_3_4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
puzzles/6/64/piece_3_5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
puzzles/6/64/piece_3_6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
puzzles/6/64/piece_3_7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
puzzles/6/64/piece_4_0.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
puzzles/6/64/piece_4_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
puzzles/6/64/piece_4_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
puzzles/6/64/piece_4_3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
puzzles/6/64/piece_4_4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
puzzles/6/64/piece_4_5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
puzzles/6/64/piece_4_6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
puzzles/6/64/piece_4_7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Some files were not shown because too many files have changed in this diff Show More