303 lines
11 KiB
PHP
303 lines
11 KiB
PHP
<?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;
|
|
|
|
// 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];
|
|
|
|
$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.";
|
|
}
|
|
|
|
$page_title = 'Risolvi: ' . ($puzzle ? htmlspecialchars($puzzle['name']) : 'Puzzle');
|
|
require_once 'includes/header.php';
|
|
?>
|
|
|
|
<style>
|
|
#puzzle-board {
|
|
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%;
|
|
}
|
|
</style>
|
|
|
|
<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>
|
|
|
|
<main class="container mt-4">
|
|
<div class="text-center mb-4">
|
|
<h1><?php echo $puzzle ? htmlspecialchars($puzzle['name']) : 'Risolvi il Puzzle'; ?></h1>
|
|
<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): ?>
|
|
<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" class="puzzle-board shadow-lg rounded"></div>
|
|
</div>
|
|
<div class="col-lg-4 col-md-12">
|
|
<div class="card p-2">
|
|
<h2 class="h5 text-center">I Tuoi Pezzi</h2>
|
|
<div id="pieces-tray" class="pieces-tray 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>
|
|
</div>
|
|
<?php endif; ?>
|
|
</main>
|
|
|
|
<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; ?>;
|
|
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++) {
|
|
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) {
|
|
startTimer();
|
|
incrementMoves();
|
|
dropZone.appendChild(draggedPiece);
|
|
checkWin();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
piecesTray.addEventListener('dragover', e => e.preventDefault());
|
|
piecesTray.addEventListener('drop', e => {
|
|
e.preventDefault();
|
|
if (draggedPiece) {
|
|
startTimer();
|
|
incrementMoves();
|
|
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
|
|
)) {
|
|
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 = 'var(--mavi-orange)';
|
|
} else {
|
|
console.error('Failed to save score:', data.error);
|
|
alert('Si è verificato un errore durante il salvataggio del punteggio.');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<?php require_once 'includes/footer.php'; ?>
|