Tetrimino

As said previously, every tetrimino has four blocks. Another thing to note is that they can rotate. So for example you have this tetrimino:

Figure 3.1

It can also rotate in the three following positions:

  
Figure 3.2

Theoretically, every tetrimino should have four states, but in reality, not all of them do. For example, this one has no transformation so to speak:

Figure 3.3

And these three only have two states:

Figure 3.4

We have two ways of handling these rotations: using matrix rotation or storing the different states. To have a code that's easy to read and update, I picked the second option, but don't hesitate to try using matrix on your own, it could help you learn a lot of new things!

So first, let's write down a struct for tetriminos:

struct Tetrimino {
    states: Vec<Vec<Vec<u8>>>,
    x: isize,
    y: usize,
    current_state: u8,
}

Everything seems fine except this line:

states: Vec<Vec<Vec<u8>>>,

Pretty ugly, right? Let's make it look a bit better by using type aliasing!

So what is our states field representing? Simply a list of states. Each state represents a piece's transformation. I suppose it's a bit hard to understand all of this. Let's write an example:

vec![vec![1, 1, 0, 0],
     vec![1, 1, 0, 0],
     vec![0, 0, 0, 0],
     vec![0, 0, 0, 0]]

In here, 0 means the block is empty, otherwise, it's a tetrimino block. So from reading this code, I suppose you could guess that we were representing the square:

Figure 3.5

In case you wondered, we have four lines with four blocks because the biggest tetrimino has a height (or a width, depending the transformation) of four:

Figure 3.6

This isn't mandatory (we could make it fit the form of each tetrimino), but it makes our lives easier, so why not?

Coming back to our type aliasing: a piece is basically a vector or vector of numbers. It's long to write it down every time, so let's alias it as follows:

type Piece = Vec<Vec<u8>>;

Now we can rewrite the states field declaration as follows:

states: Vec<Piece>,

Way better and more explicit, right? But since we'll be using those states as well, why not alias them too?

type States = Vec<Piece>;

And now our states field declaration becomes:

states: States,

Let's explain the other fields (just in case):

struct Tetrimino {
    states: States,
    x: isize,
    y: usize,
    current_state: u8,
}

A little explanation of this struct:

Ok, so far so good. Now how should we handle the creation of this type generically? We don't want to rewrite this for every tetrimino. This is where traits kick in!