DEV Community

artydev
artydev

Posted on

Create a Sliding Puzzle with your favorite tools.

Sometimes ago I created a sliding puzzle using DML, which is in fact mainly Javascript.

I was curious to see other implementations.

For recall here is my version : Sliding

<html lang="de">
  <head>
  <meta charset="utf-8">
    <title>title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://efpage.de/DML/DML_homepage/lib/DML-min.js"></script>
  </head>
  <body> 

  <script> 
  function sleep (ms) {
    return new Promise((r,err) => {
      setTimeout(r,ms)
    })
  }

  class Board {

    cssCell = `
      background:blueviolet;height:100px; width:100px;
      margin:2px;cursor:pointer;flex: 0 0 100px;
      display:flex;align-items: center;
      justify-content: center;color: orange; 
    `

    cssBoard = `
      padding:5px;display:flex;
      flex-wrap:wrap;background:orange;
      width:416px;margin:0 auto;
    `

    checkValidBoard () { 
      function checkPos (c) {
        return c.dataset.pos == c.dataset.curpos
      }
      return this.cells.every(checkPos);
    }

    findDirectionToMove(cell) {

      const [getCol,getRow] = [(pos) => pos%4,(pos) => Math.trunc(pos/4)]

      const cellpos = Number(cell.dataset.curpos);
      const emptyCellpos  = Number(this.emptyCell.dataset.curpos); 

      const rowcolEmpty = [getRow(emptyCellpos), getCol(emptyCellpos)];
      const rowcolCell =  [getRow(cellpos), getCol(cellpos)];

      const sameColOrRow =  
      (rowcolEmpty[0] == rowcolCell[0] ) || (rowcolEmpty[1] == rowcolCell[1]);

      if (!sameColOrRow) return;

      const directions = {
        [ cellpos - 1] : "left",
        [ cellpos + 1] : "right",
        [ cellpos - 4] : "up",
        [ cellpos + 4] : "down" 
      }
      let posdir = Object.keys(directions).find(pos => pos == emptyCellpos);
      return (directions[posdir]);        
    } 

    findOpositeDirection(direction) { 
      if (direction == "up") { return "down" }
      if (direction == "down") { return "up" }
      if (direction == "left") { return "right" }
      if (direction = "right") { return "left" }
    }

    moveCell (cell) {
      const direction  = this.findDirectionToMove(cell);
      if (!direction) return;

      const amount = 104;
      const duration = 200;
      const coef = {
        'left' : -1,
        'right' : 1,
        'down' : 1,
        'up' : -1
      }

      const oppositeDirection =  this.findOpositeDirection(direction);

      const cssTranslate = (cell, direction) => {
        if (direction == 'left' || direction == 'right') { 
          cell.dataset.dx = Number(cell.dataset.dx) + amount * coef[direction]
        }
        if (direction == 'up' || direction == 'down') {  
          cell.dataset.dy = Number(cell.dataset.dy) + amount * coef[direction]     
        }  
        return `translate(${cell.dataset.dx}px, ${cell.dataset.dy}px )` 
      }

      cell.animate( 
        {transform: cssTranslate(cell, direction)}, 
        {duration: duration, fill: "forwards"});    

      this.emptyCell.animate(
       {transform:  cssTranslate(this.emptyCell, oppositeDirection)}, 
       {duration: duration, fill: "forwards"} 
      );

      let temp = this.emptyCell.dataset.curpos;
      this.emptyCell.dataset.curpos = cell.dataset.curpos;
      cell.dataset.curpos = temp;

      if (cell.dataset.pos !=  cell.dataset.curpos) {
        cell.style.background = "deeppink";
        cell.style.color = "white";
      }
      else { 
         cell.style.background = "blueviolet";
      }
    }

    createCells () {
      // fills cells array with cell element
      this.cells = [...Array(16)].map((_, i) => { 
        let options = {
          style : this.cssCell,
          ["data-pos"] : i, ["data-curpos"] : i,
          ["data-dx"] : 0, ["data-dy"] : 0
        }
        let content =  h1(`${i}`);
        let cell = create("div", options, content);
        cell.onclick =  () => this.moveCell(cell);
        return cell;
      });

      // empty cell customisation 
      this.emptyCell = this.cells[15];
      this.emptyCell.style.background = "rgba(255,0,0,0.0)";
      this.emptyCell.style.zIndex = 0;
      this.emptyCell.style.color = "transparent";
    }

    displayBoard () {
      selectBase(div(""));
        print("<h1 style='text-align:center'>Sliding Puzzle</h1>");
      unselectBase();
      selectBase(div("", {style: this.cssBoard}));
        this.cells.map(appendBase);
      unselectBase();
      selectBase(div("", "text-align:center;margin-top:20px"));
        button("Shuffle") .onclick = () => this.shuffle();
      unselectBase();
    }

    moveCellByCurpos(cellpos) {
      let cell = this.cells.find(cell => cell.dataset.curpos == cellpos);
      this.moveCell(cell);
    }

    async shuffle () {
      for (let i = 0; i < 200; i++) {
        const emptyPos = Number(this.emptyCell.dataset.curpos); 
        const validPos = [
            emptyPos - 1, emptyPos + 1,
            emptyPos - 4, emptyPos + 4
          ].filter(pos => {return (pos >= 0) && (pos <= 15);
        }) 
        const randomPos= validPos[Math.floor(Math.random()*validPos.length)];
        await sleep(10);
        this.moveCellByCurpos(randomPos);  
      }
    }

    start () {  
      this.createCells ();  
      this.displayBoard(); 
    }
  }

  let game = new Board() 
  game.start()
  game.shuffle()
  </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

AWS GenAI LIVE image

How is generative AI increasing efficiency?

Join AWS GenAI LIVE! to find out how gen AI is reshaping productivity, streamlining processes, and driving innovation.

Learn more

Top comments (0)

Billboard image

Create up to 10 Postgres Databases on Neon's free plan.

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Try Neon for Free →

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay