Tetris game logic.
The two motivations for creating this library were:
-
Evaluating
fulcro/guardrails
whilst developing something non-trivial -
Creating a unit-tested and repurposable
tetris.logic
engine that can be used to build fully-fleshed out games using various Clojure-based UI frameworks and deployment targets (e.g. web, desktop, mobile)
The main API function is tetris.logic/handle-events
which takes a ::game-state
and a sequence of ::game-events
, and returns a new ::game-state
.
Optionally a play-next-tetrimino-fn
can be passed to allow the changes that happen when a new tetrimino enters play to be overridden e.g. to support debouncing strategies or grace-periods specific to a particular embodiment of the game.
The game-grid
consists of 24 rows and 10 columns. The visible-grid
is 20 rows indexed as (row, col)
where row 0
corresponds to the first visible row in the grid.
Four lead-in
rows exist with row indices -1..-4
. When a new tetrimino enters play, it resides at a random column offset within the lead-in
area, then automatically enters the visible-grid
according to the cadence of the game.
Tetriminos are strictly contained by the game-grid
. They cannot extend beyond the left and right edges, or travel beyond the baseline
of the game-grid.
The game-grid
consists of cells that are either empty 0
or contain a number 1..7
which represents the presence of a tetrimino cell at that location.
There are seven different tetrimino-shapes
. Each tetrimino is defined as a vector of its unique rotational variants. E.g.
{...
:tetrimino/T [[[0 3 0]
[3 3 3]]
[[3 0]
[3 3]
[3 0]]
[[3 3 3]
[0 3 0]]
[[0 3]
[3 3]
[0 3]]]
...
}
The component cells of each tetrimino are given a distinct number. For the case of :tetrimino/T
that number is 3.
The only way the ::game-state
changes is in response to ::game-events
.
(s/def ::game-event #{::move-left
::move-right
::move-down
::rotate
::drop})
Points are earned when rows filled with tetrimino cells are cleared. 100
pts are earned for every line cleared, up to a maximum of four lines and 400
pts.
The game cadence is initially one ::move-down
per 1000ms
, this corresponds to a level 1
of difficulty.
This level increments every time an additional difficulty-increment-in-pts
are earned, which is currently hard-wired to 1000
pts.
During game play varying numbers of tetriminos
will have accumulated into the game-grid
. The peaks
are 10 row indices corresponding to the 10 columns in the game grid, each representing the row index of the highest cell greater than the current playback row
occupied by a tetrminino cell.
This information is essential for determining when the cells of tetrimino in play are adjacent to existing tetrimino cells.
Within a virgin game-grid
that contains no tetriminos, the peaks
are [20,20,20,20,20,20,20,20,20,20]
, ensuring that tetriminos never extend beyond the baseline
of the game-grid
.
A game is over when the current player position (row) is on the first line of the visible-grid
or within the lead-in
area i.e. (<= row 0)
and the current tetrmino piece is adjacent to peaks
in the grid.
tetris-clj (main) bb tasks
The following tasks are available:
deps:tree
lint
test:coverage
test:watch
A simple example of usage can be found at tetris-reagent