Episode 12 & 13: Fade out and clean up

3 minutes
16 October 2021

In episode 12(1h:25m) I started with building a simple score tracker. Adding gradual fading became a bit of a cliffhanger, because I introduced a gnarly bug that I was only able to resolve in episode 13 (1h:20). I used this as motivation to clean up and reorganize the code with the help of the glorious Conquer of Completion Vim plugin.

For episode 12 I created a single commit as before. It’s the head of the episode12 branch.

For the changes of episode 13, I went with a different approach. Since I reorganized the code a lot a large number of lines changed. To keep them comprehensible I separated them into multiple smaller commits. Of course, there is still also the episode13 branch that represents the state of the code after that episode. To see only the steps I did in that episode you can check out this pull request.


Score tracking

To be able to keep score I added a new type Score that I use in the GameDetails. I implemented it in the most simple way: Clearing 1 row = 1 Point. The real Tetris has a much more sophisticated scoring algorithm (Source: Tetris Wiki)

Level Points for 1 line 2 lines 3 lines 4 lines
0 40 100 300 1200
1 80 200 600 2400
2 120 300 900 3600
9 400 1000 3000 12000

level(n) = 40 * (n + 1) 100 * (n + 1) 300 * (n + 1) 1200 * (n + 1)

Fading cleared lines

To be able to fade a row I added a new variant FadingRow to the Row type.

type Row
    = Row (List Field)
    | FadingRow (List Field) Opacity

A fading row represents a row that was logically removed but is visually still present. Next to the tile information it also contains a value for the opacity.

To be able to progress the fading out in the speed I desire I need to update our model more often. To achieve that we’re now firing the GravityTick every 30 instead of every 100 milliseconds. For every tick we decrease the opacity of all fading rows a bit with the progressFading function until they’ve completely vanished.

It was when I tried to integrate progressFading in all the right places that I introduced the error that eventually made me give up that day. As part of updating the eraseCompleteRows function I reset the currentPiece. The first change in episode 13 resolves the error and finally the fading works as expected.

Clean up

The rest of that episode I spend separating the general application from the ‘pure’ game logic. Though I wouldn’t go as far as calling it a refactoring since I’ve been mainly moving functions from one module to another.

For that task, I put a new tool of my development environment to use. Conquer of Completion (short CoC) is a Vim plugin that uses the Language Server Protocol to support the developer with language-specific hints. The same protocol is used in Visual Studio Code for many languages for autocompletion and other features like organizing inputs.

With the help of CoC execution operations like moving functions to a different module can be executed much more efficiently. Tedious subtasks like updating imports and removing unused code are reduced to executing the actions proposed by the plugin inline.

Most of the significant steps to set up CoC with Elm for Vim are outlined in the Elm Language Server project.

I think I’ll do a special episode soon showing how to set up a complete Elm development environment with Vim from scratch.