JSFiddle - React, Tailwind, and code Playground

CSS

.puzzle {
  display: inline-block;
  margin: -8px;
}

JavaScript

const src = "https://i.imgur.com/r0LPrzk.png";

class Puzzle {
  constructor({
    src,
    rows,
    columns,
    width,
    height
  }) {
    this.src = src;
    this.rows = rows;
    this.columns = columns;
    this.width = width;
    this.height = height;
    this.tileWidth = width / columns;
    this.tileHeight = height / rows;
    this.tiles = Array.from({
      length: rows * columns
    }).map((_, i) => {
      const x = i % columns;
      const y = Math.floor(i / columns);
      return {
        x,
        y,
        imageX: x,
        imageY: y,
        empty: false
      };
    });
    this.emptyTile = last(this.tiles);
    this.emptyTile.empty = true;
    this.scramble();
    this.createElements();
  }

  slideTile(tile) {
    const {
      emptyTile
    } = this;
    if (!this.areTilesNeighbors(tile, emptyTile)) return;
    this.swapTiles(tile, emptyTile);
    this.updateTileElPosition(tile);
    this.updateTileElPosition(emptyTile);
  }

  swapTiles(a, b) {
    const {
      x: ax,
      y: ay
    } = a;
    const {
      x: bx,
      y: by
    } = b;
    a.x = bx;
    a.y = by;
    b.x = ax;
    b.y = ay;
  }

  scramble() {
    const {
      emptyTile
    } = this;
    let last = null;
    const isValidTile = (tile) => tile !== last && tile !== emptyTile && this.areTilesNeighbors(tile, emptyTile);

    for (let i = 0; i < 1000; i++) {
      const nextTile = last = sample(this.tiles.filter(isValidTile));
      this.swapTiles(emptyTile, nextTile);
    }
  }

  updateTileElPosition(tile) {
    const {
      tileWidth,
      tileHeight
    } = this;
    const {
      el,
      x,
      y
    } = tile;
    el.style.left = `${x * tileWidth}px`;
    el.style.top = `${y * tileHeight}px`;
  }

  areTilesNeighbors(a, b) {
    const {
      x: ax,
      y: ay
    } = a;
    const {
      x: bx,
      y: by
    } = b;
    return (
      (a !== b) &&
      (
        (ax === bx && (ay === by + 1 || ay === by - 1)) ||
        (ay === by && (ax === bx + 1 || ax...