最短手数の最大値は80手なのだそうです。
document.addEventListener('DOMContentLoaded', () => { const tiles = document.querySelectorAll('#panel button'); const panel = document.querySelector('#panel'); const shuff = document.querySelector('#shuffle'); const solve = document.querySelector('#solve'); const sb = new SlidingBlocks(4, 4, 0.1); let mode = ''; const swap = sb.step.bind(sb); const shuffle = () => { sb.shuffle(); update(); }; const draw = (i) => { const n = sb.block[i] - 1; const x = n % 4 * tiles[i].offsetWidth; const y = (n / 4 | 0) * tiles[i].offsetHeight; tiles[i].style.backgroundImage = `url(${img.src})`; tiles[i].style.backgroundPosition = `-${x}px -${y}px`; }; const update = () => { sb.block.forEach((x, i) => { tiles[i].textContent = x; tiles[i].style.visibility = x == 16 ? 'hidden' : 'visible'; draw(i); }); }; const test = () => { if (sb.complete()) { const b = sb.get_blank(); tiles[b].style.visibility = 'visible'; tiles.forEach((x) => { x.classList.add('none'); }); } }; const swappable = () => { const b = sb.get_blank(); return tiles[b].style.visibility == 'hidden'; } const moving = (elem, direction, duration) => { const opt = { easing: [ 'ease' ] }; if (direction == -1) { opt.transform = [ 'translateX(-100%)' ]; } else if (direction == -4) { opt.transform = [ 'translateY(-100%)' ]; } else if (direction == 1) { opt.transform = [ 'translateX(100%)' ]; } else if (direction == 4) { opt.transform = [ 'translateY(100%)' ]; } elem.animate(opt, duration).onfinish = () => { update(); test(); }; }; const solver = async () => { return sb.solve(); }; panel.addEventListener('click', (e) => { if (mode == 'auto') return; if (e.target.tagName != 'BUTTON') return; if (!swappable()) return; const n = sb.block.indexOf(e.target.textContent | 0); const b = sb.get_blank(); if (swap(n)) moving(tiles[n], b - n, 100); }); shuff.addEventListener('click', () => { steps.textContent = ''; tiles.forEach((x) => { x.classList.remove('none'); }); shuffle(); }); solve.addEventListener('click', () => { if (!swappable()) return; shuff.disabled = true; solve.disabled = true; mode = 'auto'; solver().then((p) => { let start; const r = []; p.shift(); for (const x of p) { r.unshift(x.block.indexOf(16)); } steps.textContent = `${r.length} steps`; const step = (timestamp) => { if (start === undefined) start = timestamp; const elapsed = timestamp - start; if (elapsed > 150) { const n = r.pop(), b = sb.get_blank(); if (swap(n)) moving(tiles[n], b - n, 100); start = timestamp; } if (r.length > 0) { window.requestAnimationFrame(step); } else { shuff.disabled = false; solve.disabled = false; mode = ''; } }; window.requestAnimationFrame(step); }); }); const img = new Image(); img.src = 'images/game.png'; img.onload = () => { const aspect = img.height / img.width; const resizeObserver = new ResizeObserver((entries, observe) => { for (const entry of entries) { if (entry.contentBoxSize) { const w = entry.contentBoxSize[0].inlineSize; const h = w * aspect; tiles.forEach((x, i) => { x.style.width = `${w / 4}px`; x.style.height = `${h / 4}px`; x.style.backgroundSize = `${w}px ${h}px`; draw(i); }); } } }); resizeObserver.observe(panel); shuffle(); }; });