In today's episode, we're just going to make a new piece come down every time the current piece hits the bottom of the board. Nothing fancy yet, but it's another piece of the game we can implement fairly easily off of the back of the work we did in the last episode.

## Project

I've tagged the repo with `before_episode_118` if you're following along.

Let's just get into it. We'd like to write a test to verify that when the current piece hits the bottom, we get a new piece and our x/y in the state are reset.

Now, in Tetris the next piece is known ahead of time, so we'll add a `next_piece` key to the Game State:

``````defmodule Extris.Game.State do
defstruct shape: :ell, rotation: 0, x: 5, y: 0, next_shape: :ell
end``````

This is a test of the game logic, rather than user interaction, so open up `test/extris/game_test.exs`:

``````defmodule Extris.GameTest do
use ExUnit.Case
alias Extris.Game
alias Extris.Game.State

test "The next piece becomes the current piece when the current piece hits the bottom" do
state = %State{shape: :ell, next_shape: :oh, y: 20}
new_state = Game.tick_game(state)
assert new_state.shape == :oh
end
end``````

Go ahead and run the tests, and they'll fail. Open up the `Game` module and we'll make this happen:

``````  def tick_game(state=%State{y: y}) when y == 20 do
%State{shape: state.next_shape, x: 5, y: 0}
end
def tick_game(state) do
%State{state|y: state.y + 1}
end``````

Alright, so run the tests again and they'll pass. Let's playtest it real quick.

(( do it ))

Alright, so obviously we don't really want it to be when the shape hits y 20, but rather when it hits 20 - the height of the shape. Let's add a test to our `Shapes` module to confirm we can get the height:

``````  test "an oh always has a height of 2" do
for rotation <- 0..3 do
assert Shapes.height(:oh, rotation) == 2
end
end

test "an ell has a height of 2 or 3" do
assert Shapes.height(:ell, 0) == 3
assert Shapes.height(:ell, 1) == 2
assert Shapes.height(:ell, 2) == 3
assert Shapes.height(:ell, 3) == 2
end``````

Run the tests and they'll fail. Open up the shapes module and add the height function:

``````  def height(shape, rotation) do
shapes[shape]
|> Enum.at(rotation)
|> length
end``````

OK, so open up the game test and let's make sure that it gives us a new piece when the current piece is height away from the bottom:

``````  test "The next piece becomes the current piece when the current piece hits the bottom" do
state = %State{shape: :ell, next_shape: :oh, y: 17}
new_state = Game.tick_game(state)
assert new_state.shape == :oh
end``````

Run the tests, and they'll fail. Now open up the `Game` module and modify the logic for providing the next piece. We started out with a guard, but we just can't use it with our height function, so we'll refactor it using cond.

``````  def tick_game(state) do
cond do
Shapes.height(state.shape, state.rotation) + state.y > 19 ->
%State{state | shape: state.next_shape, x: 5, y: 0}
true ->
%State{state | y: state.y + 1}
end
end``````

Go ahead and run the tests, and they'll pass. Let's do a quick playtest.

Alright, the only other thing we need to do this session is to move the random-next-piece logic into the Shapes module and then use it appropriately.

(( move `Window.random_shape` to `Shapes.random` and make it public ))

(( open up `lib/extris/window.ex` ))

``````  alias Extris.Shapes
#...
Extris.Game.loop(%State{shape: Shapes.random, next_shape: Shapes.random}, frame)``````

Next, let's seed a new `next_shape` when we hit the bottom. Open up the `Game` module again:

``````  alias Extris.Shapes
# ...

def tick_game(state) do
cond do
Shapes.height(state.shape, state.rotation) + state.y > 19 ->
%State{state | shape: state.next_shape, x: 5, y: 0, next_shape: Shapes.random}
true ->
%State{state | y: state.y + 1}
end
end``````

Alright, with that let's do a quick play test to make sure we're getting new random shapes.

(( do it ))

## Summary

In today's episode we just provided the game logic for getting next pieces and showing them when the current piece drops. Again, it's just incrementally more like an actual game. See you soon!