# Lesson 9 - Tetris in MonoGame: Functional Game Core

In the previous lesson, Tetris in MonoGame: Game Board, we implemented the Tetris game board.

Today we're going to make our game core functional and playable.

Let's move to `LevelComponent`. We'll add a `gameBoard` field which will store an instance of the game board class we created in the previous lesson:

`private GameBoard gameBoard;`

In `Initialize()` we'll set the game board dimensions and position:

`gameBoard = new GameBoard(10, 20, gameBoardPosition);`

We'll add a method that returns us a new block. It'll simply generate a new block and set its position to the top center of the game board:

```public void NextBlock()
{
block = blockGenerator.Generate(7);
block.position = gameBoard.InsertBlock(block);
}```

We can see that we used the game board's method to set the block's spawn position. We'll call this method at the end of `Initialize()`, instead of generating the block directly:

`NextBlock();`

We'll definitely want to render the board, so we'll add the following line right below rendering the block in `Draw()`:

`gameBoard.Draw(robotrisGame.spriteBatch, tileSprites);`

Great. You can try to run the game. A random block is generated at the top center of the game board.

## Real-time Logic

We're finally getting to the game's real-time logic. Let's start with making the block falling.

### Falling Block

The block will fall down only once in a certain time. Let's begin at 1 second, but we'll speed the game up later. Let's add the `speed` field of the `float` type:

`private float speed;`

In `Initialize()` we'll set it to `1`:

`speed = 1;`

We're going to need to measure time since the last block fall. So let's add a similar field called `timeSinceLastFall`:

`private float timeSinceLastFall;`

In `Initialize()` we'll set it to `0`:

`timeSinceLastFall = 0;`

We'll move to `Update()`. Here we'll add a `seconds` variable of the `float` type and store the elapsed time since the last update there (in seconds):

`float seconds = (float)gameTime.ElapsedGameTime.TotalSeconds;`

We'll then add this number of seconds to `timeSinceLastFall`:

`timeSinceLastFall += seconds;`

Now we just have to check whether the time specified in `speed` has elapsed. If so, we'll make the block fall and reset `timeSinceLastFall`:

```if (timeSinceLastFall >= speed)
{
block.Fall();
timeSinceLastFall = 0;
}```

Let's try it:

Of course, after a while, the block will fall out of the game board. We have to check for collisions and add the block to the game board if it occurred. We've already implemented methods for this, so it should be no problem to make it work.

The collision occurs when the block is already where it shouldn't be. To fix this problem, we'll simply decrease `Y` by one. Then we'll add the block to the game board, and find and remove all complete rows. Finally, we'll spawn the next block.

```if (gameBoard.Collision(block, block.position))
{
block.position.Y--;
gameBoard.Merge(block);
gameBoard.RemoveRows();
NextBlock();
}```

We'll put this whole condition, including its body, right after calling the `Fall()` method in `Update()` because the collision occurs when the block falls either too low or on another block.

Now let's start the game and let it run for a while:

We can see that collision occurs when the block reaches the bottom of the game board or another block. Collision also occurs when the block is moved outside the game board at its sides.

### Block Movement

To have the game actually playable, we should be able to move the block. It shouldn't be difficult, we just have to check for collisions to prevent the block to move out of the game board or hit another block. This is exactly when the position parameter of the `Collision()` method becomes useful, since we need to ask whether the block's future coordinates are occupied by another block, and if so, we won't move the block there at all.

```if ((robotrisGame.keyboardState.IsKeyDown(Keys.Right)) &&
(!gameBoard.Collision(block, new Vector2(block.position.X + 1, block.position.Y))))
block.position.X++; // move right
if ((robotrisGame.keyboardState.IsKeyDown(Keys.Left)) &&
(!gameBoard.Collision(block, new Vector2(block.position.X - 1, block.position.Y))))
block.position.X--; // move left```

We'll put the code into `Update()`, right after the time update.

The movement is very fast because we check for the key input too often. We've already encountered this problem in one of the first lessons of this course. One option would be to use our `NewKey()` method and move the block by pressing and releasing the key, but that wouldn't be very user friendly. That's why we'll create a timer, just like we did to make the block fall. The movement will be performed only if a specific time interval has elapsed.

We'll add two more fields to the class - `timeSinceLastKeyPress` and `keyDelay`:

```private float timeSinceLastKeyPress;
private float keyDelay;```

We'll initialize those fields in `Initialize()`:

```keyDelay = 0.06f;
timeSinceLastKeyPress = 0;```

Now we'll implement behavior exactly the same as we did with the fall. In `Update()` we'll add the elapsed seconds to `timeSinceLastKeyPress`. Then we'll check the keyboard input in a condition and eventually reset the elapsed time since the last key press.

```timeSinceLastKeyPress += seconds;
if (timeSinceLastKeyPress > keyDelay)
{
if ((robotrisGame.keyboardState.IsKeyDown(Keys.Right)) &&
(!gameBoard.Collision(block, new Vector2(block.position.X + 1, block.position.Y))))
block.position.X++; // move right
if ((robotrisGame.keyboardState.IsKeyDown(Keys.Left)) &&
(!gameBoard.Collision(block, new Vector2(block.position.X - 1, block.position.Y))))
block.position.X--; // move left
timeSinceLastKeyPress = 0;
}```

Try it out. You can regulate the speed by changing the key delay in `Initialize()`.

Let's go back to the rotation. Although it works fine, a situation may occur that the block collides after it's rotated (it may intersect with other blocks or even move out of the game board partially). In that case, we don't want the rotation to occur. We can solve this problem with a small trick - we'll be rotating the block just as we do now, but then if any collision occurs, we'll rotate the block three more times to return it back to its original position, while the player won't notice anything

This is how we'll modify the rotation:

```if (robotrisGame.NewKey(Keys.Enter) || robotrisGame.NewKey(Keys.Up))
{
block.Rotate();
// Rotated block collides - rotating back
if (gameBoard.Collision(block, block.position))
for (int i = 0; i < 3; i++)
block.Rotate();
}```

Because the player will certainly be impatient, and the blocks are falling slowly at the beginning, we'll give them an option to accelerate the fall using the right Ctrl key. So the speed won't be constant anymore and the `speed` field won't be enough for us. Let's go to `Update()` and add a new variable named `timeBetweenFalls`, right below the keyboard input check, and set it to `speed`. If the right Ctrl key is held down, we'll set it to a lower value, let's say `0.15f`. Into the key down condition, we'll also have to specify that the speed has to be higher than `0.15f`, otherwise the player could use the key to slow down the game to make it easier.

```float timeBetweenFalls = speed;
if (robotrisGame.keyboardState.IsKeyDown(Keys.RightControl) && (speed > 0.15f))
timeBetweenFalls = 0.15f;```

We'll also modify the block fall condition. We'll use the `timeBetweenFalls` variable, instead of the `speed` field:

`if (timeSinceLastFall >= timeBetweenFalls)`

Let's try.

The original Tetris game also had another key that would make the block fall down to the game board's bottom or to the first block it'd collide with. We'll implement this behavior as well and bind it to the down arrow cursor key. All we have to do is to call the `Fall()` method in a loop until the collision is imminent. Then we'll update the `timeSinceLastFall` field to invoke the block fall handling below. Insert the following code right below the key input check:

```if (robotrisGame.NewKey(Keys.Down))
{
while (!gameBoard.Kolize(block, new Vector2(block.position.X, block.position.Y + 1)))
block.Fall();
timeSinceLastFall += timeBetweenFalls;
}```

Let's try.

That’s all for today.

In the next lesson, Tetris in MonoGame: Score and Level Completing, we'll complete the level

Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.