The contents of this page have been written by me around 2003. Since then, I have learned a lot, both technically and grammatically. As such I cannot guarantee that any of the techniques described in here are still accurate (odds are good that this can be done far easier nowadays).

Tetris

In my ever-continuing quest to make games for furcadia, I've taken on tetris this time. For several weeks I've wandered around with the idea, slowly refining the various things I would need until I felt confident that I could make it. I think the dream speaks for itself in how well I succeeded.

This page will not go into full detail on how every aspect of the dream works, but will instead focus on the way I constructed the entire game. I will also deal with a few portions of the DS that I think might be interesting. Other than that I suggest you read the DS as it's fairly well commented (or so I like to think).


The global picture

In short my version of tetris is a state machine with 51 states. These states vary from initialisation to waiting for a furre to advancing a block down the pit or removing lines. We can distinguish between the following states (with the actual states between braces):

The entire game functions by switching between these states at the appropriate times. To make sure that there were no unexpected surprised while switching between states, I made a couple of rules that each block of DS needs to adhere to, which I will list below.

Keeping it correct

Throughout the DS you will notice that one variable appears in just about every location: %blockpos. This variable contains the position of the 'center' of the current block. Each block of DS has been designed in such a way that this ALWAYS holds (even though it wasn't needed in a few cases). I did this to ensure that I could always trust the contents of %blockpos, no matter what state preceded the current one.

Coupled with this requirement was the fact that, if a block of DS altered %blockpos, it would also have to draw the block in the right position in the pit. This to ensure that %blockpos stayed the 'center' of the drawn block.

Similar to %blockpos, %furpos maintains the position of the furre-avatar in the game. The rules for %furpos are that it is always in the third column of the board (experimentation showed to me that that column provided best visibility) and that %furpos is always level with %blockpos, so when %blockpos moves down, %furpos does too. And as before, if a block of DS changes %furpos then it should move the furre to that position as well.

The structure of the pit

You might have noticed that, while playing the game, you don't see the furre walkabout anywhere. The trick I used to accomplish this is to make the actual tetris-pit float above the furre. This is also the reason why wings and such are advised against, because these will poke through the pit, destroying the illusion that it's only the block that's moving. Just take a look at items 0-16 in the iteme.fsh file, as these make up the pit.

But not only the objects for the pit are special. The floor is special too. I use floortiles 500 and 501 (custom designs), with 500 meaning that there is no fixed block in that position and 501 indicating that a fixed block is already occupying that space. This can be used to check whether a direction is clear for movement or not (more on this when I discuss the way I moved the blocks). Tiles 500 and 501 are unwalkable to make sure that the furre can't move away from his designated spot under the pit. Though I could've just moved him back whenever he tried to move, this way of doing things avoids some ugly 'bouncing' on the screen. Whenever a furre needs to move I temporarily switch to 502 and 503 (walkable varieties of 500 and 501), move the furre, then switch them back to 500 and 501.

Moving the blocks

Before you can move a block in any direction, you have to make sure it's allowed to move there first. The trick I used here was to add up the floortiles below all the spaces where the block would move into. I could then check this value to see whether the region was empty, because it would have to equal a power of 500. So for example: when you move down a horizontal yellow block, you have to check all four spaces below it before you can move it down. By adding up the floortiles, I would get a value of 2000 if it was empty or a value from 2001 to 2004 if one of the spots already held a fixed block.

If you take a look at the DS, you'll see that most movements have two blocks of DS attached. The first block is the one that adds up the floortiles at whatever spots the block would move into. The second block checks this value and performs an action based upon it. In most cases you only want something to happen when the new position is empty. The only exception is when a block moves down, as it should become fixed the moment it hits the pit-edge or another fixed block.

Removing filled lines

To easily check whether a line was filled I added an extra column outside the pit. Within this column the floortiles would indicate how many blocks occupied that specific line. Since the pit is 8 blocks wide, a value of 8 in any field in this column would indicate that it was full and that it should be removed.

This could be done by checking every single line in the pit, but I decided on a slightly shorter, though more complex, solution. Instead of checking every single line, I only check the lines to which the block might have been added. This way I only need to check four lines instead of fifteen.

Depending on whether it removed any empty lines, the following two things could happen: either no lines were removed, in which case we return to state 2 to process the next block or one or more lines were removed, which means that there might be blocks that need moving.

Moving the blocks was simply the most tedious part of the DS. While in state 91, I check all the lines each frame to see whether there might be might be an empty line directly below one with blocks. If so I move that entire line down one notch before moving on to the line above. If done often enough, you'll reach a point where there are no more empty lines with a non-empty line above it, in which case you can move onto the next block.

Once this algorithm was done, it was also a very good spot to check for a pit-bonus. We only need to check one value and that is the block-count for the bottomline. If it is zero, then all lines above it must be zero as well (this follows from the algorithm), so the pit must be empty, so we can award the pit-bonus.

The end

I've tried to explain the way I implemented this version of tetris, though I can't help but feel that nobody will understand a word I wrote. If you have questions or think I have missed something, feel free to email me or contact me on any of the messengers. I'll try to answer your question and, if I think it's really something I missed, I might even update this page. For now, I bid you adieu and wish you lots of fun playing the game.