You're browsing the old v2.x documentation. This version is no longer maintained. Click here to go the v3.x documentation.

Step 2.2: Designing the shapes

The following shapes may appear in a standard Tetris game:

They are often called I, J, L, O, S, T and Z, respectively.

If we take all of them in all rotations, we will get 28 shapes:

Now we must decide how we store each shape in memory. We have 28 shapes, four times each of the 7 distinct shapes (we will store the 4 rotations for each shape). We know that they'll be all 4×4 matrices, holding 16 squares blocks each.

It seems obvious that one square should be mapped to one bit in memory: it can hold a value 0 or 1, meaning a square is empty or set. And since there are 16 squares in each shape, one shape can be stored in a 16-bit range, that is exactly the length of an integer in XC=BASIC. Moreover, XC=BASIC features bitwise operations on 16-bit integers that can be useful when we need to check if shapes overlap, or merge the shape to the playfield, etc… More about this later.

So how do we encode a single shape in 16 bits? By simply reading out the squares from top left to bottom right and writing the bits one after another, for example:

The T shape will be encoded as 0000 1110 0100 0000 in binary form, which is decimal 16398. But since XC=BASIC accepts numbers in binary form, we don't have to care about the latter.

Okay, so we can store all shapes in an array of 28 integers.

Let's roll up our sleeves and encode all the shapes in a single DATA statement. I did the dirty job for you and here is the statement that holds all of them:

REM -- Shapes of all pieces in all rotations
DATA shapes[] = %0100010001000100, %0000111100000000, %0010001000100010, %0000000011110000, ~
                %0100010011000000, %1000111000000000, %0110010001000000, %0000111000100000, ~
                %0100010001100000, %0000111010000000, %1100010001000000, %0010111000000000, ~
                %1100110000000000, %1100110000000000, %1100110000000000, %1100110000000000, ~
                %0000011011000000, %1000110001000000, %0110110000000000, %0100011000100000, ~
                %0000111001000000, %0100110001000000, %0100111000000000, %0100011001000000, ~
                %0000110001100000, %0100110010000000, %1100011000000000, %0010011001000000

Ther are several important things to note here:

  • The DATA statement in XC=BASIC is different to the one in CBM BASIC. In XC=BASIC it means “create an array in compile time and have its values preloaded”.
  • The square brackets [] after the variable name mean this is an array without the array length explicitly specified. It is rather auto-calculated by the compiler from the number of elements that follow.
  • If no sigil is appended to the variable name, it means the variable is an integer type, representing a 16-bit value between -32768 and 32767.
  • The % sign before a numeric literal means that a binary number will follow.
  • You might also have noticed the tilde (~) character. Since a new line means the end of a statement in XC=BASIC (as well as in most BASIC dialects), we need a special character to allow the statement continue in the next line. It is very important to note that there can be no more characters after the tilde, not even a single whitespace!
  • You can place the DATA statement anywhere in the program. For readibility reasons it is best to keep it at the end.

You can read more about the DATA statement here.

Let's define another array that holds the color code for each shape:

REM -- Piece color codes
DATA colors![] = 14, 6, 8, 7, 5, 4, 2

This time the ! sigil is appended to the variable name which means it is of the byte type, a value betweeen 0 and 255.

Good job, we have found a data type for the shapes and written a DATA statement that holds all of them in an array.

Let's move on to the next page where we'll design the playfield.