1

i will try to add only relevant code but just in case its needed full page is here, and feel free to check it out on github as well.

i am building a tetris game using canvas/javascript and part of it

    //drop the falling piece by one space
function dropByOne(){
        //loop through the four squares that make up the piece
    for ( i = 3; i > -1; i-- ) {
            //empty old spot of piece
        board[ fallingPiecePos[i].y ][ fallingPiecePos[i].x ] = 0;
            //drop position place-holder by one row
        fallingPiecePos[i].y++;
            //add square to new spot
        board[ fallingPiecePos[i].y ][ fallingPiecePos[i].x ] = fallingPiece;
    }
}

board is a 20*10 array, fallingPiecePos is a array of objects with numerical x and y values, i.e. [{y:0,x:4},{y:0,x:5},{y:0,x:6},{y:0,x:7}] (line piece) or [{y:0,x:4},{y:0,x:5},{y:1,x:4},{y:1,x:5}] (square) that are rendered into board with the following code:

for ( i = 0; i < 4; i++ ) {
    board[ fallingPiecePos[i].y ][ fallingPiecePos[i].x ] = fallingPiece;
}

fallingPiece is a randomly assigned number (1-7) used by canvas to render the piece as the right color.

hope thats clear enough, now the problem is that whenever fallingPiece has a value it has had before i get

TypeError: board[fallingPiecePos[i].y] is undefined
[Break On This Error]   

board[ fallingPiecePos[i].y ][ fallingPiecePos[i].x ] = fallingPiece;

(board[ fallingPiecePos[i].y ][ fallingPiecePos[i].x ] = fallingPiece; is the last line of above code-block)

i have a function nothingIsBelow() which checks if the piece has reached the bottom, so i am pretty stumped as to why this is happening.

edit

i wasnt clear enough on this point before it works fine the first 3-4 pieces (aside for piece collision protection) and only gives me above error when fallingPiece has a previously held value

edit

it seems that the issue is like this i have a array shapes

var shapes = [
    [{y:0,x:4},{y:0,x:5},{y:0,x:6},{y:0,x:7}],
    [{y:0,x:4},{y:0,x:5},{y:0,x:6},{y:1,x:4}],
    [{y:0,x:4},{y:0,x:5},{y:0,x:6},{y:1,x:5}],
    [{y:0,x:4},{y:0,x:5},{y:0,x:6},{y:1,x:6}],
    [{y:0,x:4},{y:0,x:5},{y:1,x:4},{y:1,x:5}],
    [{y:0,x:4},{y:0,x:5},{y:1,x:3},{y:1,x:4}],
    [{y:0,x:4},{y:0,x:5},{y:1,x:5},{y:1,x:6}]
];

and i have a line of code assigning a shape to a new piece

fallingPiecePos = shapes[fallingPiece - 1];

it seems that when i later refer to fallingPiecePos and change the value (fallingPiecePos[i].y++;), it changes the value in shapes as well

in simple terms the following code

var myArray = [
     [{a:0,b:1},{a:1,b:1}],
     [{a:0,b:0},{a:1,b:0}]
];

var foo = myArray[0];

foo[0].a++;
console.log(myArray[0][0][a]);

will give me 1 because not only foo but also myArray was updated, so how can i make a variable which holds a new array (foo) holding the values of myArray[0] and can be updated without updating myArray[0].

Math chiller
  • 4,123
  • 6
  • 28
  • 44
  • 3
    If you put `console.log(board[ fallingPiecePos[i].y ][ fallingPiecePos[i].x ]);` before the line creating the error, what does it say the values are? I think debugging using the console would be the best approach here. any chance of a jsFiddle? – Zach Saucier Nov 13 '13 at 20:33
  • @Zeaklous too much code for a fiddle, i tried some debugging b4 i came here, and i get the same error with `console.log(board[ fallingPiecePos[i].y ][ fallingPiecePos[i].x ]);` i hope thats enough info – Math chiller Nov 13 '13 at 21:08
  • Console is not a fix for a solution, it is a tool used to investigate objects and find errors. It's a staple for web programming in general. [**See this for more information**](https://developer.mozilla.org/en-US/docs/Tools/Web_Console) – Zach Saucier Nov 13 '13 at 21:14
  • @Zeaklous im not a noob, i know what `console` is what i meant was that when it reaches that line it gives me the same error and doesnt `log` anything – Math chiller Nov 13 '13 at 21:16
  • @Zeaklous copy-paste from `console` - `tetris.js (line 70) 0 tetris.js (line 70) 0 tetris.js (line 70) 0 tetris.js (line 70) 0 tetris.js (line 70) 0 tetris.js (line 70) 0 tetris.js (line 70) 3 tetris.js (line 70) 3 tetris.js (line 70) 0 tetris.js (line 70) 0 tetris.js (line 70) 0 tetris.js (line 70) 3 tetris.js (line 70) 0 tetris.js (line 70) 3 tetris.js (line 70) TypeError: board[fallingPiecePos[i].y] is undefined [Break On This Error] console.log(board[ fallingPiecePos[i].y ][ fallingPiecePos[i].x ]); tetris.js (line 70)` – Math chiller Nov 13 '13 at 21:25
  • You're right - I thought I was able to repro it with even a first time `fallingPiece` value, but this was not the case. I've updated my answer below (you seem to be modifying the values in your `shapes` array when a piece falls, which results with you referencing board[20] which does not exist). – spectralbat Nov 13 '13 at 21:46
  • @spectralbat thanks i have realized that on my own now, check my edited question. – Math chiller Nov 13 '13 at 21:50

3 Answers3

3

"board[ fallingPiecePos[i].y ] is undefined", meaning whatever value that fallingPiecePos[i].y is at that point in time, it is out of the range of board array.

I took a quick look and it always seems to throw error on the 4th tetromino piece because it's trying to draw it on (x, 20) position on board (fallingPiecePos[i].y at that point is 20), when the board can only draw up to row 19, remember indices start at 0 and not 1.

I would suggest you fix your nothingIsBelow function to check whether the tetromino has "settled" on top of another tetromino or the bottom edge of the game screen. Before dropping the current tetromino into place.

Amy
  • 7,388
  • 2
  • 20
  • 31
  • i didnt include the code of `nothingIsBelow()` because its a bit long [here is a fiddle of that function](http://jsfiddle.net/mendelthecoder/7uwMH/) as for _it always seems to throw error on the 4th tetromino piece because it's trying to draw it on (x, 20) position on board_ as i said in my question "now the problem is that whenever `fallingPiece` **has a value it has had before**" which i think is a bit weird – Math chiller Nov 13 '13 at 21:14
3

You are modifying your shapes array. When a particular piece falls and you update its position you actually update that array as opposed to just fallingPiecePos exclusively. This means that when piece n falls the first time, it stops at row board[19], but when it is chosen again it ends up in board[20], which does not exist.

You can check this by checking the contents of your shapes array when you run the game - note that the positions change. To fix this you'd need to either avoid modifying your array of shapes for each piece or resetting the array values each time a piece is chosen.

Example:

function writePiece() { 
    var copy = JSON.parse(JSON.stringify(shapes)); 
    fallingPiecePos = copy[fallingPiece - 1]; 
    for ( i = 0; i < 4; i++ ) { 
        board[ fallingPiecePos[i].y ][ fallingPiecePos[i].x ] = fallingPiece; 
    } 
}
spectralbat
  • 407
  • 2
  • 13
  • thanks u found the problem (by now i found it 2), can u help me with a solution plz (edited question - `so how can i make a variable which holds a new array (foo) holding the values of myArray[0] and can be updated without updating myArray[0].`) – Math chiller Nov 13 '13 at 21:52
  • You have to clone the object instead of just creating a reference to it. There are a few different methods, but I usually use a variant of the selected answer here, which also comes with a wonderful in-depth explanation: http://stackoverflow.com/questions/728360/most-elegant-way-to-clone-a-javascript-object – spectralbat Nov 13 '13 at 22:11
  • from looking those over the simplest thing i see (for my case at least) is using a `for` loop, is there a more instinctual method? and i can accept your answer if you add some working (replacement) code. – Math chiller Nov 13 '13 at 22:46
  • tried replacing `function writePiece(){ fallingPiecePos = shapes[fallingPiece - 1]; for ( i = 0; i < 4; i++ ) { board[ fallingPiecePos[i].y ][ fallingPiecePos[i].x ] = fallingPiece; } }` with `function writePiece(){ fallingPiecePos = []; for ( var i = 0; i < 4; i++ ) { fallingPiecePos[i] = {}; for( var j in shapes[fallingPiece - 1]){ fallingPiecePos[i][j] = shapes[fallingPiece - 1][j]; } board[ fallingPiecePos[i].y ][ fallingPiecePos[i].x ] = fallingPiece; } }` and its not working, could you please give me a solution for my specific case? – Math chiller Nov 13 '13 at 22:55
  • sorry for not writing code clearly [fiddle with both functions here](http://jsfiddle.net/mendelthecoder/uhEg9/) – Math chiller Nov 13 '13 at 22:57
  • Villarrealized's answer would actually work. function writePiece(){ var copy = JSON.parse(JSON.stringify(shapes)); fallingPiecePos = copy[fallingPiece - 1]; for ( i = 0; i < 4; i++ ) { board[ fallingPiecePos[i].y ][ fallingPiecePos[i].x ] = fallingPiece; } } – spectralbat Nov 13 '13 at 23:22
  • thanks, works perfectly, if u add that in your actual answer i will accept it – Math chiller Nov 13 '13 at 23:32
0

Modify your write piece function to look like this:

Check out the fiddle: http://jsfiddle.net/RJe86/11/ -- no errors

function writePiece(){
    var shapeCopy = JSON.parse(JSON.stringify(shapes));
    fallingPiecePos = shapeCopy[fallingPiece - 1];
    for ( i = 0; i < 4; i++ ) {
        board[ fallingPiecePos[i].y ][ fallingPiecePos[i].x ] = fallingPiece;
    }
}

That should fix it.

Villarrealized
  • 867
  • 1
  • 6
  • 13