0

I am making tetris in JS. When making a block fall, it makes the block reach the bottom of the screen in one draw instead of slowly approaching the bottom. I tried creating a variable that stores the changes to be made so that it only looks at the current board, but no luck. After checking whether the output variable is == to the board, it seems like the board is changing after all, as it returns true. What's going on?

EDIT: I have successfully made a shallow copy of the array. It still falls to the bottom immediately, though. What's going on?

var data = [];
          function array(x, text) {
              var y = [];
              for (var i = 0; i < x-1; i++) {y.push(text);}
              return y;
          }          
for (var i=0; i<20; i++){data.push(array(10, "b"));}
function draw(){
    var j;
    var i;
    var dataOut = [...data];
    for (i = 0; i < data.length - 1; i++){
        for (j = 0; j < data[i].length; j++){
            if (data[i][j] == "a" && data[i + 1][j] == "b" && i < data.length - 1) {
                
                dataOut[i][j] = "b";
                dataOut[i + 1][j] = "a";
               
            }
        }
    }
    data = dataOut;
}
data[0][4] = 'a';
draw();
console.log(data);
ARI FISHER
  • 343
  • 1
  • 13
  • Every time you loop over a row, you push the `a` to the row below. Then, on the next row, you meet the `a` again, so you push it below. Then, on the row below that, you meet it again... It drops to the bottom – blex Feb 26 '20 at 19:48
  • @blex Read the question. It looks like it should be that, but I'm putting it on a seperate array and setting the main board to the secondary array after the loop ends, so it should take note of what to change before actually changing it. – ARI FISHER Feb 26 '20 at 19:51
  • Your logic looks good to me. The problem is all this code will run synchronously - it'll run in the space of one animation frame, so you'll never see anything but the end product. Seems to me you need here either css-based or JavaScript-based (and asynchronous) animation using either `setTimeout()` or the like, or, better but more complex, `requestAnimationFrame()`. – avocadatoria Feb 26 '20 at 19:54
  • 1
    @JSScratchDouble In JS, Arrays and Objects are passed by reference, not value. So when you do this: `var dataOut = data;`, they are the same Array. Modifying it will using either reference will get you the same result – blex Feb 26 '20 at 19:55
  • @avocadatoria I am using `requestAnimationFrame`. It should only run once in this case! – ARI FISHER Feb 26 '20 at 19:56
  • @blex Thanks, how do I fix that? – ARI FISHER Feb 26 '20 at 19:56
  • Here is a way: https://stackoverflow.com/a/23481096/1913729 – blex Feb 26 '20 at 19:57
  • @JSScratchDouble "I am using requestAnimationFrame. It should only run once in this case!" - Not sure I understand; if you are using requestAnimationFrame(), you'd want to call it once for every frame of the falling-tetris-block animation. Otherwise you'll have a one-frame animation, which is not what you want, but seems to be what you currently have? – avocadatoria Feb 26 '20 at 20:26

1 Answers1

2

In JavaScript, Arrays and Objects are passed by reference. So when you do this:

var dataOut = data;

Both of these references point to the same Array. You could clone the Array every time:

var dataOut = JSON.parse(JSON.stringify(data));

Or simply revert your loop, to go from the bottom to the top. I took the liberty of renaming the variables to make this more clear. Try it below:

var chars = {empty: '.', block: '#'},
    grid = createEmptyGrid(10, 20);

function createEmptyGrid(width, height) {
  var result = [], x, y;
  for (y = 0; y < height; y++) {
    var row = [];
    for (x = 0; x < width; x++) {
      row.push(chars.empty);
    }
    result.push(row);
  }
  return result;
}

function draw() {
  var x, y;
  for (y = grid.length - 1; y > 0; y--) {
    for (x = 0; x < grid[y].length; x++) {
      if (grid[y][x] === chars.empty && grid[y - 1][x] === chars.block) {
        grid[y][x] = chars.block;
        grid[y - 1][x] = chars.empty;
      }
    }
  }
}

// Just for the demo
var t = 0, loop = setInterval(function () {
  draw();
  if (grid[0].includes(chars.block)) {
    clearInterval(loop);
    grid[9] = 'GAME OVER!'.split('');
  }
  document.body.innerHTML = '<pre style="font-size:.6em">'
                          +   grid.map(row => row.join(' ')).join('\n')
                          + '</pre>';
  if (t % 20 === 0) {
    grid[0][Math.floor(Math.random() * 10)] = chars.block;
  }
  t++;
}, 20);
blex
  • 24,941
  • 5
  • 39
  • 72