I once made this sliding number puzzle, and it was a fun little project. It’s a simple game where you arrange numbers in order by sliding tiles into an empty space.
We all played this puzzle at some point in our lives.
The code worked perfectly when I first built it, but somewhere along the line, especially on mobile. It stopped behaving as expected. I never got around to fixing it until now.
How It Works
The puzzle starts with 15 numbered tiles and one empty space.
You can click or swipe to move tiles into the empty spot.
The goal is to arrange the numbers in order from 1 to 15.
What Went Wrong?
The old version had issues with mobile gestures and resetting the board. It also failed to handle the empty tile properly, which made the grid act weird.
The Fix
I updated the code to fix:
߷ Added proper swipe detection for mobile.
߷ The 16th tile (empty) is now included and styled differently.
߷ Now fully resets the grid without breaking the layout.
߷ Also now Looks better on smaller screens.
Try It Out
You can view and play with the updated version.
If you’re curious, here’s the full code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sliding Number Puzzle</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; margin: 0; padding: 0; background: #f4f4f9; }
#game-board { margin: 20px auto; width: 320px; }
h1 { font-size: 20px; margin: 20px 0; }
#number-grid { display: grid; grid-template-columns: repeat(4, 1fr); grid-gap: 5px; width: 320px; margin: 0 auto; }
.number-cell { background: lightblue; padding: 20px; font-size: 18px; text-align: center; border: 1px solid #ccc; cursor: pointer; }
.empty { background: #f4f4f9; }
button { margin-top: 20px; padding: 10px 20px; font-size: 16px; cursor: pointer; }
</style>
</head>
<body>
<h1>Sliding Number Puzzle</h1>
<div id="game-board">
<div id="number-grid"></div>
<button id="reset-button">Reset</button>
</div>
<script>
const grid = document.getElementById("number-grid");
const resetButton = document.getElementById("reset-button");
let numbers = [];
function createNumberGrid() {
numbers = shuffleArray([...Array(15).keys()].map(n => n + 1).concat(16)); // Add empty 16
renderGrid();
}
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
function renderGrid() {
grid.innerHTML = "";
numbers.forEach((num, index) => {
const cell = document.createElement("div");
cell.classList.add("number-cell");
if (num === 16) cell.classList.add("empty"); // Empty tile
else cell.textContent = num;
cell.addEventListener("click", () => moveTile(index));
grid.appendChild(cell);
});
}
function moveTile(index) {
const emptyIndex = numbers.indexOf(16);
const validMoves = [
emptyIndex - 1, emptyIndex + 1,
emptyIndex - 4, emptyIndex + 4
];
if (validMoves.includes(index)) {
[numbers[index], numbers[emptyIndex]] = [numbers[emptyIndex], numbers[index]];
renderGrid();
checkWin();
}
}
function checkWin() {
if (numbers.slice(0, 15).every((num, idx) => num === idx + 1)) {
setTimeout(() => alert("Congratulations! You solved the puzzle!"), 300);
}
}
resetButton.addEventListener("click", createNumberGrid);
createNumberGrid();
</script>
</body>
</html>
It’s a small project but fun to build. I learned a lot about debugging, mobile gestures, and responsive layouts while fixing it.
If you want to try it or improve it further, feel free to.