Sliding Tiles Puzzle
The sliding tiles puzzle written in Preact: https://www.math.nmsu.edu/~pmorandi/math111f01/SlidingTiles.html
by remarkablemark
HTML
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/preact/8.2.6/preact.min.js"></script>
CSS
body {
font-family: 'Lucida Grande', Helvetica, sans-serif;
}
table button {
height: 21px;
width: 25px;
}
form {
font-size: 12px;
}
form input[type="number"] {
width: 25px;
}
Babel + JSX
const { h, render, Component } = preact; /** @jsx h */
/**
* Generates an array of numbers starting at 1.
*
* @param {Number} size
* @return {Array}
*/
const generateNumbers = size => Array
.apply(null, { length: size })
.map((_, index) => index + 1);
/**
* Shuffles an array.
*
* @param {Array} array
* @param {Boolean} [shouldMutate=false]
* @return {Array}
*/
const shuffleArray = (array = [], shouldMutate = false) => {
const arr = shouldMutate ? array : array.slice();
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
return arr;
};
/**
* Generates solution.
*
* @param {Number} size
* @return {Object}
*/
const generateSolution = size => {
const numbers = generateNumbers(size ** 2 - 1);
numbers.push(null);
const solution = [];
while (numbers.length) {
solution.push(numbers.splice(0, size));
}
return {
solution,
solutionJSON: JSON.stringify(solution),
};
};
/**
* Generates puzzle.
*
* @param {Number} size
* @return {Object}
*/
const generatePuzzle = size => {
if (size < 2) {
throw Error('Puzzle size must be an integer greater than 1.');
}
const numbers = generateNumbers(size ** 2 - 1);
const random = shuffleArray(generateNumbers(size ** 2 - 1));
// make sure numbers are random
if (JSON.stringify(random) === JSON.stringify(numbers)) {
return generatePuzzle(size);
}
random.push(null);
const puzzle = [];
while (random.length) {
puzzle.push(random.splice(0, size));
}
return { puzzle };
};
class TileSlider extends Component {
state = {
size: this.props.size,
...generatePuzzle(this.props.size),
...generateSolution(this.props.size),
};
/**
* Swaps tile with empty one when clicked.
*
* @param {SyntheticEvent}
*/
handleClick = ({ target }) => {
const puzzle = this.state.puzzle.map(row => row.slice());
let { value, x, y } =...