madansa7's picture
Add 3 files
201ed39 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sliding Puzzle Game</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.tile {
transition: all 0.3s ease;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
}
.tile:hover {
transform: scale(1.02);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
.win-message {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.puzzle-container {
perspective: 1000px;
}
</style>
</head>
<body class="bg-gradient-to-br from-blue-50 to-purple-100 min-h-screen flex flex-col items-center justify-center p-4">
<div class="max-w-4xl w-full">
<h1 class="text-4xl font-bold text-center text-purple-800 mb-2">Sliding Puzzle Challenge</h1>
<p class="text-center text-gray-600 mb-8">Slide the tiles to reconstruct the image. Click on adjacent tiles to swap with the empty space.</p>
<div class="flex flex-col md:flex-row gap-8 items-center justify-center">
<div class="puzzle-container bg-white rounded-xl p-4 shadow-xl">
<div class="grid grid-cols-3 gap-2 mb-4" id="puzzle-grid">
<!-- Puzzle tiles will be inserted here by JavaScript -->
</div>
<div class="flex justify-between items-center mt-4">
<div class="text-lg font-semibold text-purple-700">
Moves: <span id="move-counter">0</span>
</div>
<div class="flex gap-2">
<button id="shuffle-btn" class="bg-purple-600 hover:bg-purple-700 text-white px-4 py-2 rounded-lg transition flex items-center gap-2">
<i class="fas fa-random"></i> Shuffle
</button>
<button id="new-game-btn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition flex items-center gap-2">
<i class="fas fa-redo"></i> New Game
</button>
</div>
</div>
</div>
<div class="bg-white rounded-xl p-6 shadow-xl max-w-xs w-full">
<h2 class="text-xl font-bold text-purple-800 mb-4">How to Play</h2>
<ul class="space-y-3 text-gray-700">
<li class="flex items-start gap-2">
<i class="fas fa-arrow-right text-purple-500 mt-1"></i>
<span>Click on a tile adjacent to the empty space to move it</span>
</li>
<li class="flex items-start gap-2">
<i class="fas fa-arrow-right text-purple-500 mt-1"></i>
<span>Rearrange all tiles to complete the image</span>
</li>
<li class="flex items-start gap-2">
<i class="fas fa-arrow-right text-purple-500 mt-1"></i>
<span>Try to solve it in the fewest moves possible</span>
</li>
</ul>
<div class="mt-6">
<h3 class="font-semibold text-gray-800 mb-2">Completed Image:</h3>
<img src="https://source.unsplash.com/random/300x300/?nature" alt="Target image" class="rounded-lg border border-gray-200 w-full" id="target-image">
</div>
</div>
</div>
</div>
<!-- Win Modal -->
<div id="win-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden">
<div class="bg-white rounded-xl p-8 max-w-md w-full mx-4 win-message">
<div class="text-center">
<i class="fas fa-trophy text-6xl text-yellow-500 mb-4"></i>
<h2 class="text-3xl font-bold text-purple-800 mb-2">Congratulations!</h2>
<p class="text-gray-600 mb-6">You solved the puzzle in <span id="final-moves" class="font-bold">0</span> moves!</p>
<button id="play-again-btn" class="bg-purple-600 hover:bg-purple-700 text-white px-6 py-3 rounded-lg text-lg font-medium w-full">
Play Again
</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Game state
const gridSize = 3;
let puzzleState = [];
let emptyPos = { row: gridSize - 1, col: gridSize - 1 };
let moveCount = 0;
let isGameWon = false;
// DOM elements
const puzzleGrid = document.getElementById('puzzle-grid');
const moveCounter = document.getElementById('move-counter');
const shuffleBtn = document.getElementById('shuffle-btn');
const newGameBtn = document.getElementById('new-game-btn');
const winModal = document.getElementById('win-modal');
const finalMoves = document.getElementById('final-moves');
const playAgainBtn = document.getElementById('play-again-btn');
// Initialize the game
initGame();
// Event listeners
shuffleBtn.addEventListener('click', shufflePuzzle);
newGameBtn.addEventListener('click', initGame);
playAgainBtn.addEventListener('click', initGame);
// Initialize game
function initGame() {
// Reset game state
moveCount = 0;
isGameWon = false;
moveCounter.textContent = moveCount;
winModal.classList.add('hidden');
// Create ordered puzzle state (1-8, with 9 as empty)
puzzleState = [];
for (let i = 0; i < gridSize * gridSize - 1; i++) {
puzzleState.push(i + 1);
}
puzzleState.push(0); // 0 represents the empty space
// Shuffle the puzzle
shufflePuzzle();
// Render the puzzle
renderPuzzle();
}
// Shuffle the puzzle
function shufflePuzzle() {
if (isGameWon) {
isGameWon = false;
winModal.classList.add('hidden');
}
// Reset move counter
moveCount = 0;
moveCounter.textContent = moveCount;
// Fisher-Yates shuffle algorithm
for (let i = puzzleState.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[puzzleState[i], puzzleState[j]] = [puzzleState[j], puzzleState[i]];
}
// Find the empty position
const emptyIndex = puzzleState.indexOf(0);
emptyPos = {
row: Math.floor(emptyIndex / gridSize),
col: emptyIndex % gridSize
};
// Check if the puzzle is solvable (must have even inversions)
if (!isSolvable()) {
// If not solvable, swap first two non-empty tiles to make it solvable
if (puzzleState[0] === 0 || puzzleState[1] === 0) {
[puzzleState[2], puzzleState[3]] = [puzzleState[3], puzzleState[2]];
} else {
[puzzleState[0], puzzleState[1]] = [puzzleState[1], puzzleState[0]];
}
}
renderPuzzle();
}
// Check if the puzzle is solvable
function isSolvable() {
let inversions = 0;
const flattened = puzzleState.filter(num => num !== 0);
for (let i = 0; i < flattened.length - 1; i++) {
for (let j = i + 1; j < flattened.length; j++) {
if (flattened[i] > flattened[j]) {
inversions++;
}
}
}
// For a 3x3 grid, the puzzle is solvable if inversions are even
return inversions % 2 === 0;
}
// Render the puzzle grid
function renderPuzzle() {
puzzleGrid.innerHTML = '';
for (let row = 0; row < gridSize; row++) {
for (let col = 0; col < gridSize; col++) {
const index = row * gridSize + col;
const tileValue = puzzleState[index];
if (tileValue === 0) {
// Empty space
const emptyTile = document.createElement('div');
emptyTile.className = 'bg-gray-200 rounded-lg aspect-square flex items-center justify-center opacity-70';
emptyTile.dataset.row = row;
emptyTile.dataset.col = col;
puzzleGrid.appendChild(emptyTile);
} else {
// Numbered tile
const tile = document.createElement('div');
tile.className = 'tile bg-gradient-to-br from-purple-100 to-blue-100 rounded-lg aspect-square flex items-center justify-center text-2xl font-bold text-purple-800 cursor-pointer';
tile.textContent = tileValue;
tile.dataset.row = row;
tile.dataset.col = col;
tile.dataset.value = tileValue;
// Add background image slice
tile.style.backgroundImage = `url(https://source.unsplash.com/random/300x300/?nature)`;
tile.style.backgroundSize = `${gridSize * 100}% ${gridSize * 100}%`;
// Calculate background position for the image slice
const targetRow = Math.floor((tileValue - 1) / gridSize);
const targetCol = (tileValue - 1) % gridSize;
const xPos = (targetCol / (gridSize - 1)) * 100;
const yPos = (targetRow / (gridSize - 1)) * 100;
tile.style.backgroundPosition = `${xPos}% ${yPos}%`;
tile.addEventListener('click', () => handleTileClick(row, col));
puzzleGrid.appendChild(tile);
}
}
}
// Update empty position
const emptyIndex = puzzleState.indexOf(0);
emptyPos = {
row: Math.floor(emptyIndex / gridSize),
col: emptyIndex % gridSize
};
}
// Handle tile click
function handleTileClick(row, col) {
if (isGameWon) return;
// Check if the clicked tile is adjacent to the empty space
const isAdjacent =
(Math.abs(row - emptyPos.row) === 1 && col === emptyPos.col) ||
(Math.abs(col - emptyPos.col) === 1 && row === emptyPos.row);
if (isAdjacent) {
// Swap positions
const clickedIndex = row * gridSize + col;
const emptyIndex = emptyPos.row * gridSize + emptyPos.col;
// Update puzzle state
[puzzleState[clickedIndex], puzzleState[emptyIndex]] =
[puzzleState[emptyIndex], puzzleState[clickedIndex]];
// Update move counter
moveCount++;
moveCounter.textContent = moveCount;
// Re-render the puzzle
renderPuzzle();
// Check if puzzle is solved
checkWinCondition();
}
}
// Check if the puzzle is solved
function checkWinCondition() {
for (let i = 0; i < puzzleState.length - 1; i++) {
if (puzzleState[i] !== i + 1) {
return false;
}
}
if (puzzleState[puzzleState.length - 1] === 0) {
// Puzzle is solved
isGameWon = true;
finalMoves.textContent = moveCount;
winModal.classList.remove('hidden');
// Add confetti effect
const confettiSettings = { target: 'confetti-canvas', max: 150 };
const confetti = new ConfettiGenerator(confettiSettings);
confetti.render();
setTimeout(() => {
confetti.clear();
}, 3000);
}
}
});
</script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/index.min.js"></script>
<canvas id="confetti-canvas" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1000;"></canvas>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=madansa7/sliding-puzzle-challenge-game" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>