+ login Google

This commit is contained in:
Flatlogic Bot 2025-09-28 23:02:21 +00:00
parent 8554409a47
commit 188419fc12
31404 changed files with 3170849 additions and 55 deletions

View File

@ -15,7 +15,6 @@ $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;
}
@ -93,34 +92,28 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_puzzle'])) {
$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]);
$stmt = $pdo->prepare('INSERT INTO puzzles (name, original_image, file_name, pieces, is_public, created_at) VALUES (?, ?, ?, ?, ?, NOW())');
$stmt->execute([$puzzle_name, $safe_filename, $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;
}
@ -130,27 +123,22 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_puzzle'])) {
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('.','..'));
@ -162,13 +150,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_puzzle'])) {
delete_directory($puzzle_dir);
}
header("Location: admin.php?deleted=1"); // Redirect to avoid re-posting
header("Location: admin.php?deleted=1");
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);
// Fetch users
$users = $pdo->query('SELECT id, username, email, created_at FROM users ORDER BY created_at DESC')->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html lang="it">
@ -264,10 +255,9 @@ $puzzles = $pdo->query('SELECT id, name, original_image, pieces, is_public, crea
<?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
$imageUrl = 'assets/images/placeholder.png';
}
?>
<tr>
@ -289,6 +279,40 @@ $puzzles = $pdo->query('SELECT id, name, original_image, pieces, is_public, crea
</tbody>
</table>
</div>
<!-- User Management Section -->
<div class="mt-5">
<h1 class="mb-4">Gestione Utenti</h1>
<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Username</th>
<th>Email</th>
<th>Registrato il</th>
</tr>
</thead>
<tbody>
<?php if (empty($users)): ?>
<tr>
<td colspan="4" class="text-center">Nessun utente trovato.</td>
</tr>
<?php else: ?>
<?php foreach ($users as $user): ?>
<tr>
<td><?php echo htmlspecialchars($user['id']); ?></td>
<td><?php echo htmlspecialchars($user['username']); ?></td>
<td><?php echo htmlspecialchars($user['email']); ?></td>
<td><?php echo htmlspecialchars($user['created_at']); ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<footer class="text-center mt-5 py-3 bg-light">

6
composer.json Normal file
View File

@ -0,0 +1,6 @@
{
"require": {
"google/apiclient": "^2.0"
}
}

1283
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,11 @@ define('DB_PASS', '7d2d5a2d-6e5e-4580-a9b7-3f8e7288a494');
define('ADMIN_USER', 'admin');
define('ADMIN_PASS_HASH', '$2y$10$WnTccK10o9fJmz3xfs6gV.0GB2PH.Smtm4NOlqlnr7SmEgMcY8uC.'); // Hash for 'MaviGames'
// Google API Credentials - ** PLEASE REPLACE WITH YOUR OWN CREDENTIALS **
define('GOOGLE_CLIENT_ID', 'YOUR_GOOGLE_CLIENT_ID');
define('GOOGLE_CLIENT_SECRET', 'YOUR_GOOGLE_CLIENT_SECRET');
define('GOOGLE_REDIRECT_URI', 'https://mavigames-puzzle.dev.flatlogic.app/google-callback.php');
function db() {
static $pdo;
if (!$pdo) {

16
google-auth.php Normal file
View File

@ -0,0 +1,16 @@
<?php
session_start();
require_once 'vendor/autoload.php';
require_once 'db/config.php';
$client = new Google_Client();
$client->setClientId(GOOGLE_CLIENT_ID);
$client->setClientSecret(GOOGLE_CLIENT_SECRET);
$client->setRedirectUri(GOOGLE_REDIRECT_URI);
$client->addScope('email');
$client->addScope('profile');
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
exit();

63
google-callback.php Normal file
View File

@ -0,0 +1,63 @@
<?php
session_start();
require_once 'vendor/autoload.php';
require_once 'db/config.php';
$client = new Google_Client();
$client->setClientId(GOOGLE_CLIENT_ID);
$client->setClientSecret(GOOGLE_CLIENT_SECRET);
$client->setRedirectUri(GOOGLE_REDIRECT_URI);
$client->addScope('email');
$client->addScope('profile');
if (isset($_GET['code'])) {
$token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
$client->setAccessToken($token);
$google_oauth = new Google_Service_Oauth2($client);
$google_account_info = $google_oauth->userinfo->get();
$email = $google_account_info->email;
$name = $google_account_info->name;
$google_id = $google_account_info->id;
try {
$pdo = db();
// Check if user exists
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();
if ($user) {
// User exists, log them in
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
if ($user['username'] === ADMIN_USER) {
$_SESSION['is_admin'] = true;
}
} else {
// User doesn't exist, create a new one
$username = strtok($email, '@'); // Create a username from email
$password = password_hash(random_bytes(16), PASSWORD_BCRYPT); // Create a random password
$stmt = $pdo->prepare("INSERT INTO users (username, email, password, google_id) VALUES (?, ?, ?, ?)");
$stmt->execute([$username, $email, $password, $google_id]);
$user_id = $pdo->lastInsertId();
$_SESSION['user_id'] = $user_id;
$_SESSION['username'] = $username;
}
header('Location: index.php');
exit();
} catch (PDOException $e) {
die("Database error: " . $e->getMessage());
}
} else {
header('Location: login.php');
exit();
}

View File

@ -136,16 +136,31 @@ if (isset($_SESSION['selected_puzzle'])) {
<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>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<?php if (isset($_SESSION['user_id'])): ?>
<li class="nav-item">
<a class="nav-link" href="index.php">Gioca</a>
</li>
<li class="nav-item">
<a class="nav-link" href="leaderboard.php">Classifica</a>
</li>
<li class="nav-item">
<a class="nav-link" href="logout.php">Logout (<?php echo htmlspecialchars($_SESSION['username']); ?>)</a>
</li>
<?php else: ?>
<li class="nav-item">
<a class="nav-link" href="login.php">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="register.php">Registrati</a>
</li>
<?php endif; ?>
</ul>
</div>
</div>
</header>

97
leaderboard.php Normal file
View File

@ -0,0 +1,97 @@
<?php
session_start();
require_once 'db/config.php';
// Gatekeeper: redirect if not logged in
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
$pdo = db();
$stmt = $pdo->query("
SELECT
s.score,
s.time_taken,
s.moves,
s.completed_at,
u.username,
p.name as puzzle_name
FROM scores s
JOIN users u ON s.user_id = u.id
JOIN puzzles p ON s.puzzle_id = p.id
ORDER BY s.score DESC
LIMIT 100
");
$scores = $stmt->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>Classifica</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="index.php">Puzzle Game</a>
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="index.php">Gioca</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="leaderboard.php">Classifica</a>
</li>
<li class="nav-item">
<a class="nav-link" href="logout.php">Logout</a>
</li>
</ul>
</div>
</nav>
<div class="container mt-5">
<h1 class="mb-4 text-center">Classifica Generale</h1>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead class="table-dark">
<tr>
<th>#</th>
<th>Utente</th>
<th>Puzzle</th>
<th>Punteggio</th>
<th>Tempo</th>
<th>Mosse</th>
<th>Data</th>
</tr>
</thead>
<tbody>
<?php if (empty($scores)): ?>
<tr>
<td colspan="7" class="text-center">Nessun punteggio registrato.</td>
</tr>
<?php else: ?>
<?php foreach ($scores as $index => $score): ?>
<tr>
<td><?php echo $index + 1; ?></td>
<td><?php echo htmlspecialchars($score['username']); ?></td>
<td><?php echo htmlspecialchars($score['puzzle_name']); ?></td>
<td><?php echo htmlspecialchars($score['score']); ?></td>
<td><?php echo htmlspecialchars($score['time_taken']); ?>s</td>
<td><?php echo htmlspecialchars($score['moves']); ?></td>
<td><?php echo htmlspecialchars(date("d/m/Y H:i", strtotime($score['completed_at']))); ?></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>

View File

@ -1,11 +1,10 @@
<?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');
// Redirect if already logged in
if (isset($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
@ -15,13 +14,37 @@ 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;
if (empty($username) || empty($password)) {
$error_message = 'Username e password sono obbligatori.';
} else {
$error_message = 'Credenziali non valide. Riprova.';
// Admin login check
if ($username === ADMIN_USER && password_verify($password, ADMIN_PASS_HASH)) {
$_SESSION['user_id'] = 0; // Special ID for admin
$_SESSION['username'] = ADMIN_USER;
$_SESSION['is_admin'] = true;
header('Location: admin.php');
exit;
}
// Regular user login check
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['is_admin'] = false; // Ensure this is false for regular users
header('Location: index.php');
exit;
} else {
$error_message = 'Credenziali non valide. Riprova.';
}
} catch (PDOException $e) {
$error_message = "Errore del database: " . $e->getMessage();
}
}
}
?>
@ -30,7 +53,7 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Login</title>
<title>Login</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
@ -51,10 +74,20 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
</head>
<body>
<div class="login-container">
<h2 class="text-center mb-4">Accesso Admin</h2>
<h2 class="text-center mb-4">Login</h2>
<?php if ($error_message): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($error_message); ?></div>
<?php endif; ?>
<a href="google-auth.php" class="btn btn-danger w-100 mb-3">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-google" viewBox="0 0 16 16">
<path d="M15.545 6.558a9.42 9.42 0 0 1 .139 1.626c0 2.434-.87 4.492-2.384 5.885h.002C11.978 15.292 10.158 16 8 16A8 8 0 1 1 8 0a7.689 7.689 0 0 1 5.352 2.082l-2.284 2.284A4.347 4.347 0 0 0 8 3.166c-2.087 0-3.86 1.408-4.492 3.25C2.806 7.48 2.5 8.522 2.5 9.5s.306 2.02.812 2.834c.632 1.842 2.405 3.25 4.492 3.25 1.257 0 2.34-.47 3.162-1.224l.064-.064a3.837 3.837 0 0 0 1.15-2.487H8v-2h7.545z"/>
</svg> Accedi con Google
</a>
<div class="d-flex align-items-center my-3">
<hr class="flex-grow-1">
<span class="px-2 text-muted">oppure</span>
<hr class="flex-grow-1">
</div>
<form method="post" action="login.php">
<div class="mb-3">
<label for="username" class="form-label">Nome Utente</label>
@ -66,6 +99,9 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
</div>
<button type="submit" class="btn btn-primary w-100">Accedi</button>
</form>
<div class="text-center mt-3">
<p>Non hai un account? <a href="register.php">Registrati</a></p>
</div>
</div>
</body>
</html>
</html>

View File

@ -1,9 +1,40 @@
<?php
session_start();
// Gatekeeper: redirect if not logged in
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
ini_set('display_errors', 1);
error_reporting(E_ALL);
require_once 'db/config.php';
// Handle Score Submission
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'save_score') {
header('Content-Type: application/json');
$user_id = $_SESSION['user_id'];
$puzzle_id = (int)$_POST['puzzle_id'];
$time_taken = (int)$_POST['time_taken'];
$moves = (int)$_POST['moves'];
// Simple scoring formula
$score = max(0, 10000 - ($time_taken * 10) - ($moves * 5));
try {
$pdo = db();
$stmt = $pdo->prepare("INSERT INTO scores (user_id, puzzle_id, time_taken, moves, score) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$user_id, $puzzle_id, $time_taken, $moves, $score]);
echo json_encode(['success' => true, 'score' => $score]);
} catch (PDOException $e) {
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
exit; // Important: stop script execution after AJAX response
}
// --- VARIABLES ---
$puzzle_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
$difficulty = isset($_GET['difficulty']) ? (int)$_GET['difficulty'] : 16;
@ -44,7 +75,6 @@ if ($puzzle_id > 0) {
$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);
@ -100,15 +130,7 @@ if ($puzzle_id > 0) {
<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%;
}
#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); }
@ -121,13 +143,19 @@ if ($puzzle_id > 0) {
<div id="win-message">
<h2>Complimenti!</h2>
<p>Hai risolto il puzzle!</p>
<p>Punteggio: <strong id="final-score">0</strong></p>
<a href="leaderboard.php" class="btn btn-warning">Classifica</a>
<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 class="d-flex justify-content-center align-items-center gap-3">
<a href="index.php" class="btn btn-secondary btn-sm">Torna alla Galleria</a>
<span class="badge bg-primary">Tempo: <span id="timer">0s</span></span>
<span class="badge bg-info">Mosse: <span id="move-counter">0</span></span>
</div>
</div>
<?php if ($error_message): ?>
@ -168,8 +196,30 @@ if ($puzzle_id > 0) {
const pieces = document.querySelectorAll('.puzzle-piece');
const cols = <?php echo $cols; ?>;
const rows = <?php echo $rows; ?>;
const puzzleId = <?php echo $puzzle_id; ?>;
let draggedPiece = null;
let moves = 0;
let gameStarted = false;
let startTime = 0;
let timerInterval = null;
function startTimer() {
if (gameStarted) return;
gameStarted = true;
startTime = Date.now();
timerInterval = setInterval(updateTimer, 1000);
}
function updateTimer() {
const seconds = Math.floor((Date.now() - startTime) / 1000);
document.getElementById('timer').textContent = `${seconds}s`;
}
function incrementMoves() {
moves++;
document.getElementById('move-counter').textContent = moves;
}
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
@ -184,6 +234,8 @@ if ($puzzle_id > 0) {
e.preventDefault();
dropZone.classList.remove('hovered');
if (draggedPiece && dropZone.children.length === 0) {
startTimer();
incrementMoves();
dropZone.appendChild(draggedPiece);
checkWin();
}
@ -194,7 +246,11 @@ if ($puzzle_id > 0) {
piecesTray.addEventListener('dragover', e => e.preventDefault());
piecesTray.addEventListener('drop', e => {
e.preventDefault();
if (draggedPiece) { piecesTray.appendChild(draggedPiece); }
if (draggedPiece) {
startTimer();
incrementMoves();
piecesTray.appendChild(draggedPiece);
}
});
pieces.forEach(piece => {
@ -218,11 +274,33 @@ if ($puzzle_id > 0) {
zone.children.length > 0 &&
zone.children[0].dataset.position === zone.dataset.position
)) {
document.getElementById('win-message').style.display = 'block';
board.style.borderColor = '#28a745';
clearInterval(timerInterval);
const timeTaken = Math.floor((Date.now() - startTime) / 1000);
const formData = new FormData();
formData.append('action', 'save_score');
formData.append('puzzle_id', puzzleId);
formData.append('time_taken', timeTaken);
formData.append('moves', moves);
fetch('puzzle.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
document.getElementById('final-score').textContent = data.score;
document.getElementById('win-message').style.display = 'block';
board.style.borderColor = '#28a745';
} else {
console.error('Failed to save score:', data.error);
alert('Si è verificato un errore durante il salvataggio del punteggio.');
}
});
}
}
});
</script>
</body>
</html>
</html>

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
puzzles/7/64/piece_5_0.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
puzzles/7/64/piece_5_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

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