Google Sheets Fifteen Puzzle (JavaScript)
To allow for instant image movements, this version of the fifteen puzzle for Google Sheets was created in a custom dialog box.
The advantage of doing so (compared to a puzzle that would be inserted on a sheet) is that the JavaScript code is executed client-side, which is infinitely faster.
You can copy this Google Sheets document from this page.
Playable Preview of the Puzzle
Since this is a JavaScript version, the game could also be embedded on this page:
Game Code
The Google Apps Script code:
function play() {
const html = HtmlService.createTemplateFromFile('puzzle').evaluate()
.setWidth(515)
.setHeight(515);
SpreadsheetApp.getUi().showModalDialog(html, 'Fifteen Puzzle');
}
function include(name) {
return HtmlService.createHtmlOutputFromFile(name).getContent();
}
The HTML page with JavaScript and CSS:
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
}
#grid {
display: grid;
grid-template-columns: repeat(4, 125px);
grid-gap: 5px;
}
#grid > div {
width: 125px;
height: 125px;
}
</style>
<?!= include('images'); ?>
</head>
<body>
<div id="grid">
</div>
<script>
/*************************************************************************/
/** Fifteen puzzle created by Sébastien Mathier - Sheets-Pratique.com **/
/*************************************************************************/
const addRow = [0, 0, 1, -1];
const addCol = [-1, 1, 0, 0];
let grid = [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]];
const control = grid.flat().join();
let ok = true;
let end = true;
// New game
function newGame() {
end = false;
// Shuffle images (by randomly moving adjacent images into the empty space)
for (let n = 0; n < 9999; n++) {
const startRow = Math.floor(Math.random() * 4);
const startCol = Math.floor(Math.random() * 4);
// If not the empty space
if (grid[startRow][startCol] != 15) {
// Test each side of the space
for (let i = 0; i < 4; i++) {
const row = startRow + addRow[i];
const col = startCol + addCol[i];
// If valid position
if (row >= 0 && row <= 3 && col >= 0 && col <= 3) {
// If empty space
if (grid[row][col] === 15) {
[grid[row][col], grid[startRow][startCol]] = [grid[startRow][startCol], grid[row][col]];
break;
}
}
}
}
}
// Place images
displayGrid();
}
// Display the grid
function displayGrid(finished = false) {
let squares = '';
for (const [row, line] of grid.entries()) {
for (const [col, number] of line.entries()) {
squares += '<div id="p' + number + '"' + (!finished && number == 15 ? ' class="white"' : '') + ' onclick="imageClick(' + number + ')"></div>';
}
}
document.getElementById('grid').innerHTML = squares;
}
// Action on clicking an image
function imageClick(number) {
if (!ok || end) {
return;
}
ok = false;
let rowClick, colClick;
// Position of clicked image in the grid
finish:
for (const [row, line] of grid.entries()) {
for (const [col, num] of line.entries()) {
// If clicked image
if (num == number) {
rowClick = row;
colClick = col;
break finish;
}
}
}
// Test each side of the space
for (let i = 0; i < 4; i++) {
const row = rowClick + addRow[i];
const col = colClick + addCol[i];
// If valid position
if (row >= 0 && row <= 3 && col >= 0 && col <= 3) {
// If empty space
if (grid[row][col] === 15) {
[grid[row][col], grid[rowClick][colClick]] = [grid[rowClick][colClick], grid[row][col]];
break;
}
}
}
// Check if finished
const finished = grid.flat().join() == control;
// Place images
displayGrid(finished);
// End
if (finished) {
alert('Congratulations!');
end = true;
}
ok = true;
}
// New game on load
window.addEventListener('load', newGame);
</script>
</body>
</html>
To avoid overloading the content of this page, images in base64 are not displayed here.