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

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post →

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more