Log in
Seblog.nl

#myelixirstatus

So, I've been addicted to Tetris for the last week or so. Last night I decided to do something else, and when I came across the Phoenix LiveView contest, I thought: why not try and make Tetris? #myelixirstatus

I was surprised by how far I got. I have watched videos of people coding games before, have been coding myself for years and years, but somehow games felt out of my league. But here, after a (long) evening, I got a game! I should've streamed it myself.

I mean I'm standing of shoulders of giants. I got so much for free from Erlang/Elixir, LiveView to hook it up, @chris_mccord's Snake example gave me the basic idea of using just <div>'s for blocks, the browser is doing the drawing and key-repeats for me.

But it felt magical, once the game got playable. I was hooked again to my own creation. And so many "features" from NES Tetris like sliding, tucking and spinning "just worked" in my first implementation (probably because I was close to their implementation).

It all started with this HTML based board, using flexbox to to the hard works of blocks for me. I later wrapped the board in a Game-struct, and added bindings for the scores.

How to get a game loop in Elixir? Just send yourself a message after, say, 500ms. The LiveView component has a handle_info/2, which queues the next tick and moves the current Tetromino down.

Thanks to the phx-keydown="keydown" in the template, we get messages in handle_event/3 for each key. Just delegating to my Game module. And yes, I use Vim, so I need those H, J and L. (Dropping with K is not implemented.)

My Game.move/2 got a bit complicated, some refactoring is in place. But I started with a Tetromino struct, with :x and :y keys and a :color. To move it down, you paint the current :x and :y :white, and then paint :y + 1 to the :color. Code below reduces for all four points:

To obtain all the points I got this wonderful Tetromino.points/1 function. It's ugly and was a pain to write out and get right, but it works like a charm.

Colors and rotations work similar, with Tetromino.rotate/1. (color_for_type/1 gets called in new/1, and it never changes once it is created.)

Then the messy Game.move/1: can you move down? Move down. Otherwise, call Board.clear_lines/1 to remove full lines and obtain the score, get a new random Tetromino and put it on the board. The game-over handling is a bit buggy still, I had only one evening.

Clearing the board is simple: reject rows of which all cells are not empty and use counts to add new rows to the top. The Board.color_at/2 gives the color for that particular cell. The guards against negative numbers prevent List.pop_at/2 to get items from the back of the list :)

I think that's mostly it! Again, I wish I had streamed it, it was very weird watching myself doing it, but also very rewarding. Keep coding, people, you can do this too!