Skip to content

Tetris code comment

elzibus edited this page Jul 25, 2020 · 41 revisions

Introduction

This is an exploration of the tetris.r3 code Pablo wrote.

Full path in the repository: https://github.com/phreda4/r3d4/blob/master/r3/games/tetris.r3

The version below was captured on 25JUL2020.

I will write comments below each block of code.

To the reader: I expect you have looked at the documentation a bit. Let us know where you are lost in the below text so that we can improve it. Thanks!

Comments

| teris r3
| PHREDA 2020
|-------------------

Lines that begin with the vertical bar | prefix are comments.

They are ignored by the interpreter/compiler UNLESS they are formatted in a special way. There are no such special comments in this source file, you can find some in main.r3 at the root of the distribution, look for lines that start with |WIN|, |LIN|..

See that typo in the first comment? You can make a pull request :)

^r3/lib/gui.r3

Here, another prefix is introduced: the ^ character.

It lets you import words that are defined in other files.

For example, in gui.r3 the word whin is defined with two colons :: instead of a single one.

This means that when gui.r3 is imported in another r3 file, the whin word is made available.

The enables the creation of modules that comprise words for specific aspects: gui, math, dated, etc..

The gui module contains graphical user interface related functionality.

^r3/lib/rand.r3

The rand module contain random numbers related functionality.

#tablero * 800 | 10 * 20 * 4

New prefix: the # character defines variables.

For tetris we need to keep track of what has been drawn where.

This variable contains space for a 2d array of 10 * 20 which is the dimension of the grid in which the game is played.

It is an array of 800 bytes.

#colores 0 $ffff $ff $ff8040 $ffff00 $ff00 $ff0000 $800080

colores is another variable that contains a list of colors encoded in RGB hex format.

0 is black

$ff is blue

etc..

#figuras
	1 5 9 13
	1 5 9 8
	1 5 9 10
	0 1 5 6
	1 2 4 5
	1 4 5 6
	0 1 4 5

tetris is played with specific shapes. They all comprise 4 blocks (tetraminoes).

The list above contains 7 blocks of 4 numbers. The first piece is: 1 5 9 13.

⁉️ Challenge: how is each piece encoded? It is very nice and compact.

Spoiler warning: hint 1 It is not about the bits in each value.
Spoiler warning: hint 2

Another hint, look at this table:

col1 col2 col3 col3
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
Spoiler warning: solution

Okay, here is the solution. Let's take this piece: 1 4 5 6

col1 col2 col3 col3

This is how each piece is encoded!

#rota>
	8 4 0 0
	9 5 1 13
	10 6 2 0
	0 7 0 0

Now for the rotation!

In the game, when you press the UP key, the piece that is in play rotates.

So each piece has 4 orientations but above we encoded only one of them.

We therefore need a way to calculate piece rotations.

⁉️ Challenge: look at the values and try to reverse engineer how Pablo did it.

Spoiler warning: hint 1 It looks like rota> defines a 4 by 4 grid..
Spoiler warning: hint 2 The 4 by 4 grid encodes a mapping for each block to its rotated position..
Spoiler warning: hint 3

Let's take the numbered grid we showed earlier:

col1 col2 col3 col3
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15

Now let's put the numbers in rota> in a similar grid:

col1 col2 col3 col3
8 4 0 0
9 5 1 13
10 6 2 0
0 7 0 0
Spoiler warning: Solution To know how a block from a piece is rotated, take its value and look up in rota> what its rotated position is:
col1 col2 col3 col3
0 ➡️ 8 1 ➡️ 4 2 ➡️ 0 3 ➡️ 0
4 ➡️ 9 5 ➡️ 5 6 ➡️ 1 7 ➡️ 13
8 ➡️ 10 9 ➡️ 6 10 ➡️ 2 11 ➡️ 0
12 ➡️ 0 13 ➡️ 7 14 ➡️ 0 15 ➡️ 0

Example, the "L" piece which is encoded as 1 5 9 10 becomes 4 5 6 2. When drawn:

col1 col2 col3 col3

There is an issue with one of the pieces. Can you find it? then make a pull request :)

#suma
	$000 $001 $002 $003
	$100 $101 $102 $103
	$200 $201 $202 $203
	$300 $301 $302 $303

#jugador 0 0 0 0
#jugadors 0
#jugadorc 0
#puntos 0
#proxima 0
#velocidad 300

:tab2pant | n -- y x
    dup $ff and 4 << 50 +
	swap 8 >> 4 << 100 +
	;

:ficha | x y --
	2dup op
	over 15 + over pline
	over 15 + over 15 + pline
	over over 15 + pline
	pline poli ;

:dibuja_ficha | y x -- y x
	a@+ 0? ( drop ; ) 'ink !
	2dup or tab2pant ficha ;

:dibuja_tablero
	'tablero >a
	0 ( $1400 <?
		1 ( 11 <?
			dibuja_ficha
			1 + ) drop
		$100 + ) drop ;

:r>j
	dup @ 2 << 'rota> + @ swap ;

:rota>_ficha
	'jugador r>j !+ r>j !+ r>j !+ r>j ! ;

:j2t
	2 << 'suma + @ jugadors + ;

:j2f
	j2t
	tab2pant
	ficha ;

:dibuja_jugador
	jugadorc 'ink !
	'jugador @+ j2f @+ j2f @+ j2f @ j2f ;

:dpf
	2 << 'suma + @ 15 + tab2pant ficha ;

:dibuja_prox
	proxima dup 2 << 'colores + @ 'ink !
	1 - 4 << 'figuras +
	@+ dpf @+ dpf @+ dpf @ dpf ;

:rand1.7 | -- rand1..7
    ( rand dup 16 >> xor $7 and 0? drop ) ;

:nueva_ficha
	proxima
	'jugador
	over 1 - 4 << 'figuras +
	4 move | dst src cnt
	2 << 'colores + @ 'jugadorc !
	5 'jugadors !
	rand1.7 'proxima !
	;

:coord2f | coord -- realcoord
	dup $f and 1 - | x
	swap 8 >> 10 * +
	2 << 'tablero + ;

:check | pos -- 0/pos
	$1400 >? ( drop 0 ; )
	dup coord2f @ 1? ( 2drop 0 ; ) drop
	$ff and
	0? ( drop 0 ; )
	10 >? ( drop 0 ; )
	;

:colision | suma -- sumareal ; 0 encontro colision
	'jugador
	@+ j2t pick2 + check 0? ( nip nip ; ) drop
	@+ j2t pick2 + check 0? ( nip nip ; ) drop
	@+ j2t pick2 + check 0? ( nip nip ; ) drop
	@ j2t over + check 0? ( nip ; ) drop
	;

:rcolision | -- 0/1
	'jugador
	@+ 2 << 'rota> + @ j2t check 0? ( nip ; ) drop
	@+ 2 << 'rota> + @ j2t check 0? ( nip ; ) drop
	@+ 2 << 'rota> + @ j2t check 0? ( nip ; ) drop
	@ 2 << 'rota> + @ j2t check ;

:j2tt
	j2t coord2f jugadorc swap ! ;

#combo
#combop 0 40 100 300 1200

:bajalinea |
	'tablero dup 40 + swap a> pick2 - 2 >> move>
	-1 'velocidad +!
	4 'combo +! ;

:testlinea
	'combop 'combo !
	'tablero >a
	0 ( $1400 <?
		0 1 ( 11 <?
			a@+ 1? ( rot 1 + rot rot ) drop
			1 + ) drop
		10 =? ( bajalinea ) drop
		$100 + ) drop
	combo @ 'puntos +!
	;

:fija
	'jugador @+ j2tt @+ j2tt @+ j2tt @ j2tt
	testlinea
	nueva_ficha
	;

:logica
	$100 colision 0? ( drop fija ; )
	'jugadors +!
	;

:mueve
	colision 'jugadors +! ;

:rotar
	rcolision 0? ( drop ; ) drop
	rota>_ficha ;

#ntime
#dtime

:juego
	cls home
	$ff00 'ink !
	20 20 atxy "Tetris R3" print

	$444444 'ink !
	128 70 286 96 fillrect
	166 326 62 96 fillrect

	$ffffff 'ink !
	360 100 atxy puntos "%d" print

	dibuja_tablero
	dibuja_jugador
	dibuja_prox

	msec dup ntime - 'dtime +! 'ntime !
	dtime velocidad >? ( dup velocidad - 'dtime !
		logica
		) drop

	key
	>esc< =? ( exit )
	<dn> =? ( 250 'dtime +! )
	<ri> =? ( 1 mueve )
	<le> =? ( -1 mueve )
	<up> =? ( rotar )
	drop ;

:inicio
	0 'puntos !
	300 'velocidad !
	msec 'ntime ! 0 'dtime !
	rerand
	rand1.7 'proxima !
	'juego onshow
	;

: inicio ;

The single colon followed by a space has a special meaning: it is the 'boot' location. This is where the execution of the program starts.

When you run this program, this word is located and 'inicio' is the first word that is executed.

Clone this wiki locally