
# Tetro TUI - Terminal Game
[](https://crates.io/crates/tetro-tui)
[](https://github.com/Strophox/tetro-tui#license)
Tetro TUI is a terminal-based but modern tetromino-stacking game that is very customizable and runs cross-platform.

## Ways to Run
### Download & Run
1. [Download a release](<https://github.com/Strophox/tetro-tui/releases>) for your platform (Linux/MacOS/Windows/...) if available.
2. Navigate to the application binary (`tetro-tui`/`tetro-tui.exe`/...) and run it in a terminal
### Quick Install
#### Cargo ([crates.io](<https://crates.io/crates/tetro-tui>)):
```bash
cargo install tetro-tui
# tetro-tui
```
#### Arch Linux ([aur.archlinux.org](<https://aur.archlinux.org/packages?K=tetro-tui>)):
```bash
yay -S tetro-tui # Or paru, ...
# tetro-tui
```
### Compile from source
Tetro TUI is written in [Rust](<https://doc.rust-lang.org/book/ch01-01-installation.html>) and can be compiled as usual:
```bash
git clone https://github.com/Strophox/tetro-tui # Or otherwise download source code.
cd tetro-tui
cargo run
```
## FAQ
### How does the base game work?
> *Tetro TUI* is about [tetromino](<https://en.wikipedia.org/wiki/Tetromino>) pieces falling from the sky and stacking inside a 2D playing field. When a horizontal line is full it automatically clears away and everything 'stacked' above shifts down.
>
> A skilled player may keep playing indefinitely.
> Different game modes will change up the gameplay while still using the same base mechanics.
### How good is it in terms of customization / features?
> We provide a solid amount of configuration options and features:
> - **Graphics:** Unicode/ASCII/Electronika, color palettes, many effects and toggles.
> - **Gameplay and handling:** Rotation systems, randomizers, preview, timings (DAS, ARR, SDF, LCD, ARE), IRS/IHS/IMS/ITS (\[caveat](#why-do-some-gameplay-settings-dasarretc-or-registering-shift-as-keypress-not-work-for-me)).
> - **Game keybinds:** to your heart's desire.
> - **Game mode miscellany:** Swift ('40lines'), Classic ('Marathon'), Master, Puzzle, Cheese, Combo, Custom (select goal, initial gravity, toggle gravity progress, *cmdline flags:* start board, seed).
> - **Highscores, replays, statistics, ...** - can can be accessed as well as backed up with a simple **savefile**.
>
> <details>
> <summary>
> Visuals depend on / can be customized together with your underlying terminal settings.
> E.g. you can set a bigger font to scale the game, or use <a href="https://github.com/Swordfish90/cool-retro-term">cool-retro-term</a> for nostalgic gameplay etc.:
> </summary>
>
> 
>
> 
>
> 
>
> 
>
> 
>
> 
>
> </details>
>
> <details>
> <summary>
> See also: Complete list of Tetro TUI v3.0 menus contents:
> </summary>
>
> **New game/**
> - Swift: How fast can you clear 40 lines?
> - Classic: Clear 150 lines at increasing gravity.
> - *(unlocked after Classic)* Master: Clear 150 lines at instant gravity.
> - Puzzle: Clear 24 hand-crafted puzzles.
> - Cheese-20: Eat through lines like Swiss cheese. Limit∈[None, Some(10), Some(11), .., Some(20), ..]
> - Combo-30: Get consecutive line clears. Limit∈[None, Some(10), Some(11), .., Some(30), ..]
> - Ascent*: (experimental, requires 'Ocular'- + 180° rotation)
> - Custom: [Del]=reset
> * Initial fall delay = 1.0s (Gravity: 1.0 Hz)
> * Progressive gravity ∈ [on, off]
> * Limit ∈ [None, TimeElapsed(300s), .., PointsScored(200), .., PiecesLocked(100), .., LinesCleared(40), ..]
> - Game save: *(Only shows up after using `Ctrl+S`)*
>
> **Settings/Adjust-Graphics/**
> * Slot ∈ [Default, Focus+, Guideline, Terminal compatibility, Elektronika 60, Blank slate, Custom I/II/..]
> * Color Palette (modifiable presets) ∈ [Monochrome, ANSI, Standard, Okpalette, Gruvbox, Solarized, Terafox, Fahrenheit, The Matrix, Sequoia]
> * TUI style ∈ [ASCII, Unicode, Elektronika 60]
> * Mino textures ∈ [ASCII, Unicode, Elektronika 60]
> * Hard drop effect ∈ [None, Particles ASCII, Streak ASCII, Beam ASCII, Beam]
> * Lock effect ∈ [None, Transform ASCII, Pulse Unicode, White]
> * Line clear effect ∈ [None, Instant, Left to right, Inward, Outward, Flash white, Blink, Pop out, Pop out (more), Sparks ASCII, Blast]
> * Mini tetromino style ∈ [ASCII, Braille]
> * Small tetromino style ∈ [ASCII, Blocks, Braille]
> * Normalsize previews ∈ [unlimited, 1, 2 ..]
> * Frames per second ∈ [5, 10, .., 60, ..]
> * Show grid ∈ [on, off]
> * Show piece shadow ∈ [on, off]
> * Preview spawn when stack high ∈ [on, off]
> * Color board tiles ∈ [on, off]
> * Show left HUD ∈ [on, off]
> * Show keybinds legend ∈ [on, off]
> * Show active buttons ∈ [on, off]
> * Show FPS counter ∈ [on, off]
>
> **Settings/Adjust-Keybinds/**
> * Slot (modifiable preset) ∈ [Default, Control+, Guideline, Vim, Custom I/II/..]
> * MoveLeft, MoveRight
> * RotateLeft, RotateRight, Rotate180
> * DropSoft, DropHard
> * TeleDown, TeleLeft, TeleRight
> * HoldPiece
>
> **Settings/Adjust-Gameplay/**
> * Slot ∈ [Default, Finesse+, Guideline, NES, Gameboy, Custom I/II/..]
> * Piece rotation system ∈ [Ocular, ClassicL, ClassicR, Super]
> * Piece randomization ∈ [Completely random, 7-Bag, 14-Bag, .., Recency (^2.5), Recency (^2.6), .., Balance out]
> * Piece preview count ∈ [0, 1, .., 4, ..]
> * Delayed auto move (DAS) ∈ [0ms, 1ms, .., 167ms, ..]
> * Auto move rate (ARR) ∈ [0ms, 1ms, .., 33ms, ..]
> * Soft drop speedup (SDF) ∈ [0x, 0.25x, .., 15x, ..]
> * Line clear duration (LCD) ∈ [0ms, 5ms, .., 200ms, ..]
> * Spawn delay (ARE) ∈ [0ms, 5ms, .., 50ms, ..]
> * Allow initial rotation/hold (IRS/IHS/IMS/ITS) ∈ [on, off]
> * Convert double-tap to teleport ∈ [None, Some(5ms), Some(10ms), ..]
>
> **Settings/Advanced-Settings/**
> * Save contents ∈ ["--Nothing", "Only settings --No scores,replays", "Only settings,scores --No replays", "Everything (settings,scores,replays)"]
> * Assume enhanced-key-events available ∈ [on, off]
> * Blindfold gameplay ∈ [on, off]
> * Pause on focus lost ∈ [on, off]
> * Renderer selected ∈ [Standard, Legacy, Prototype, Twoxel, Braille]
>
> </details>
### Why do some gameplay (DAS/ARR/SDF...) settings (or some keybinds like Ctrl/Shift/Alt/...) not work for me?
> It is likely that your current terminal provides **too little input information** to enable custom timings\* or those special keys.
> (\*Instead, DAS/ARR/SDF will be determined by how quickly your *terminal* sends key-repeat events.)
> If possible use an **enhanced terminal** like <a href="https://sw.kovidgoyal.net/kitty/">Kitty</a> or <a href="https://alacritty.org/">Alacritty</a> (also <a href="https://docs.rs/crossterm/latest/crossterm/event/struct.PushKeyboardEnhancementFlags.html">others</a>) for flawless game handling.
>
> <details>
> <summary>
> Explanation:
> </summary>
>
> The fundamental problem lies in how terminals usually send input signals.
> - Due to historical reasons, most will only send "key pressed" but **not** "key released again".
> This makes it impossible to implement mechanics such as:
> "If `[←]` is pressed, move left with a certain speed *until key is released again*."
> * Affected mechanics: Fixed DAS, Fixed ARR, Fixed SDF (& holding Soft Drop will lock the piece), Unable to hold Teleport, Unable to hold buttons for IRS/IHS/IMS/ITS.
> * Note that some terminals e.g. on Windows *do* send key-release signals, without this being auto-detected:
> Use the 'Override' in *Advanced Settings* for such cases.
> - Also due to history, modifier keys can only modify 'actual' text signals and are never sent by themselves.
> * Affected mechanics: Cannot register modifier `Ctrl`/`Alt`/`Shift`/`Win`/`⌘`/... as individual key presses.
>
> Precisely these issues are fixed with 'enhanced keyboard events' / ['progressive enhancement'](<https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement>) / 'kitty protocol'.
>
> </details>
### Can you give me a table of all controls / shortcuts / keybinds?
> Please refer to the tables below.
>
> <details>
> <summary>
> Menu controls:
> </summary>
>
> Universal:
> | Keys | ≈ Meaning |
> | -: | :- |
> | `↓`/`↑`, `j`/`k` | Navigate up/down |
> | `←`/`→`, `h`/`l` | Change value |
> | `Enter`, `e` | Select |
> | `Esc`, `q`, `Back`, | Go back |
> | `Del`, `d` | Delete/Reset |
> | `Ctrl`+`Alt`+`L` | Re-load from savefile (Caution: discards current application data!) |
> | `Ctrl`+`Alt`+`S` | Do savefile 'store' (respects save preferences i.e. deletes if 'keep save file' is off) |
> | `Ctrl`+`C` | Exit application (respects save preferences) |
>
> Specific to 'Scores and Replays':
> | Keys | Meaning |
> | -: | :- |
> | `Alt`+`Del`, `Alt`+`d` | Delete replay (in 'Scores and Replays') |
> | `Home`/`End` | Navigate to top/bottom |
>
> Specific to 'Start New Game':
> | Keys | Meaning |
> | -: | :- |
> | `1`/`2`/`3`... | Quickselect game mode |
> | `Alt`+`←`/`→`, `Alt`+`h`/`l` | Change value differently (under ⇝['Combo','Custom','Game save']) |
> | `Home`/`End` | Jump to beginning/end of inpus (under ⇝'Game save') |
> | `Alt`+`Enter` | View replay instead of starting game (under ⇝'Game save') |
> | `Ctrl`+`U` | (For experienced/impatient players) unlock all game modes |
>
> Specific to 'Gameplay settings':
> | Keys | Meaning |
> | -: | :- |
> | `Alt`+`←`/`→`, `Alt`+`h`/`l` | Change value differently (under ⇝'Piece randomization'⇝['\*-Bag', 'Recency (\*)']) |
>
> </details>
>
>
> <details>
> <summary>
> (Default) Game controls:
> </summary>
>
> | Key (customizable) | Action |
> | -: | :- |
> | `Esc` | Pause game |
> | `←` | Move left |
> | `→` | Move right |
> | `A` | Rotate left (CCW) |
> | - | Rotate around (180°) |
> | `D` | Rotate right (CW) |
> | `↓` | Soft drop |
> | `↑` | Hard drop |
> | - | Teleport down |
> | - | Teleport left |
> | - | Teleport right |
> | `Space` | Hold piece |
>
> | Key | Special action |
> | -: | :- |
> | `Ctrl`+`D` | Forfeit game |
> | `Ctrl`+`R` | Restart game mode (Caution: discards current game) |
> | `Ctrl`+`L` | Load game save (Caution: discards current game) |
> | `Ctrl`+`S` | Store game save (accessible in 'Start New Game'⇝'Game save' or (live)'Game'⇝`Ctrl`+`L`) |
> | `Ctrl`+`E` | Store seed (accessible in 'Start New Game'⇝'Custom') |
> | `Ctrl`(+`Alt`)+`G` | Cycle through graphics settings slots |
> | `Ctrl`+`Alt`+`B` | Toggle on/off visibility of tiles ('Blindfolded') |
> | `Ctrl`+`Alt`+`L` | Re-load from savefile (Caution: discards current application data!) |
> | `Ctrl`+`Alt`+`S` | Do savefile 'store' (respects save preferences i.e. deletes if 'keep save file' is off) |
> | `Ctrl`+`C` | Exit application (respects save preferences) |
>
> </details>
>
>
> <details>
> <summary>
> Replay controls:
> </summary>
>
> | Key | Action |
> | -: | :- |
> | `Esc`, `q`, `Back` | Exit replay |
> | `Space` | Pause replay |
> | `↓`/`↑` , `j`/`k` | Speed up / slow down replay by ±0.25x |
> | `Alt`+`↓`/`↑`, `Alt`+`j`/`k` | Speed up / slow down replay by ±0.05x |
> | `-` | Reset replay speed to =1.0x |
> | `←`/`→` , `h`/`l` | Skip forward / backward 1s in time |
> | `1`/`2`/`3`... | Jump to 10%/20%/30%/... |
> | `Home`/`End` | Jump to beginning/end |
> | `.` | Skip forward one player input + pause |
> | `Alt`+`.` | Skip forward one game state change\* + pause (\*might not work properly for modded games) |
> | `Enter`, `e` | Start (live) Game from current replay state ('take over') |
>
> | Key | Special action |
> | -: | :- |
> | `Ctrl`+`L` | Toggle replay looping |
> | `Ctrl`+`S` | Store game save (accessible in 'Start New Game'⇝'Game save' or (live)'Game'⇝`Ctrl`+`L`) |
> | `Ctrl`+`E` | Store seed (accessible in 'Start New Game'⇝'Custom') |
> | `Alt`+`I` | (Experimental) Toggle instantaneous interactive input intervention mode |
> | `Ctrl`(+`Alt`)+`G` | Cycle through Graphics Settings slots |
> | `Ctrl`+`Alt`+`L` | Re-load from savefile (Caution: discards current application data!) |
> | `Ctrl`+`Alt`+`S` | Do savefile 'store' (respects save preferences i.e. deletes if 'keep save file' is off) |
> | `Ctrl`+`C` | Exit application (respects save preferences) |
>
> </details>
### Where's the config file? Will it clutter my system?
> <details>
> <summary>
> The application will not store anything by default and 'Keep save file' needs to be opted in:
> </summary>
>
> The exact location of the config file is shown in the *Advanced Settings* menu and is based on `dirs::config_dir()`:
> - Usually `/home/yourname/.config/.tetro-tui_v1.0_savefile.json`
> - or `C:/User/yourname/AppData/Roaming/.tetro-tui_v1.0_savefile.json`
>
> Savefile grows primarly due to number/length of saved replays.
> As a rule of thumb, 1min of gameplay with fast inputs adds ≲ 1 kB.
> If you end up with a lot of play time but can't/don't want to spare the kB / MB, you can:
> - Delete some entries (// just their replay data) in *Scores and Replays* using `[Del]` (// `[Alt+Del]`).
> - Configure which categories of data get stored in the first place on program exit (see *Advanced Settings*).
>
> </details>
### *Experienced players:* How does it compare to / deviate from common stacker games?
> <details>
> <summary>
> We put to practical use our customizability and provide many *settings slots* (profiles/templates), e.g. to simulate guideline gameplay*/keybinds*/graphics (*Handling limitations may apply to your terminal):
> </summary>
>
> Note that the 'Default' settings slots – though they should remain very familiar – do take liberties in 'shifting mechanics closer to the platonic ideal' of a tetromino stacker game. This is obviously not an objective statement, in practice it just means:
>
> **Keybinds:**
> - Default controls set to **WASD + Arrow keys** (also preferred due to common [terminal limitations](#why-do-some-gameplay-dasarrsdf-settings-or-some-keybinds-like-ctrlshiftalt-not-work-for-me)).
> - Dedicated keys possible for **Rotate 180°**, **Teleport Down** ('Sonic Drop'), even Teleport Left/Right.
>
> **Gameplay:**
> - Default use of the flexible/intuitive/symmetrical [**Ocular Rotation** System](#experienced-players-what-is-this-ocular-rotation-system) (instead of: the quirky / sometimes asymmetrical industry default).
> - Default **Recency Randomizer** which is random but biases toward choosing less recent pieces (instead of: 'overdeterministic' 7-Bag).
> - **Points (score) bonus** system is currently kept custom and simple.
> - '1pt for simple line clear, with increasing bonus for larger lineclears, combos, spins and perfect clears.'
> - *Note:* 'Allspin' (instead of: preoccupation with 'T-spins'), currently no 'minis'.
> - *Note:* Combos (but no additional points for 'back to back' other than existing incentives for special maneuvers).
> - Exact formula: `point_bonus = if is_perfect_clear{ 4 }else{ 1 } * if is_spin{ 2 }else{ 1 } * (lineclears * 2 - 1) + (combo - 1)`
> - Different **lock reset** limit: 'max time = 10⋅current lock delay' (instead of: 'max 15 moves with current lock delay').
> - Speed/gravity/fall curve slightly less fast but very close to 'standard'.
>
> **Graphics:**
> - Chosen default palette is more pastel with **uniform perceptual brightness**.
>
> </details>
### *Experienced players:* And how extensive are the stacker mechanics exactly?
> <details>
> <summary>
> See this list quoted from the <a href="https://crates.io/crates/falling-tetromino-engine">Falling Tetromino Engine</a> that powers the core game logic:
> </summary>
>
> In terms of advanced game mechanics the engine aims to compare with other modern tetromino stackers.
> It should already incorporate many features desired by familiar/experienced players, such as:
> - Available player actions:
> - **Move** left/right,
> - **Rotate** left/right/180°
> - **Drop** soft/hard
> - **Teleport** down(='Sonic drop') and left/right
> - **Hold** piece,
> - **Tetromino randomizers**: 'Uniform', 'Stock' (generalized Bag), 'Recency' (history), 'Balance-out',
> - **Piece preview** (arbitrary size),
> - **Spawn delay** (ARE),
> - **Spawn actions** (IRS/IHS/IMS/ITS; by keeping rotate/hold pressed during spawn),
> - **Rotation systems**: 'Ocular' (engine-specific, playtested), 'Classic', 'Super',
> - **Delayed auto-move** (DAS),
> - **Auto-move rate** (ARR),
> - **Soft drop factor** (SDF),
> - **Customizable gravity/fall and lock delay curves** (exponential and/or linear; also, '20G' (fall rate of ≥1200 Hz) just becomes ≤00083s fall delay),
> - **Ensure move delay less than lock delay** toggle (i.e. DAS/ARR are automatically shortened when lock delay is very low),
> - **Allow lenient lock-reset** toggle (i.e. reset lock delay even if rotate/move fails),
> - **Lock-reset cap factor** (i.e. maximum time before lock delay cannot be reset),
> - **Line clear duration** (LCD),
> - **Customizable win/loss conditions** based on the time, pieces, lines, points,
> - Score more **points** for larger lineclears, spins ('allspin'), perfect clear, combo,
> - Game **reproducibility** (PRNG/determinism).
>
> </details>
### *Experienced players:* What is the 'Ocular Rotation System'?
> <details>
> <summary>
> An extensive attempt at better tetromino rotation with regards to symmetry and visual intuition:
> </summary>
>
> The Ocular rotation system affords:
> - Symmetric/mirrored situations should lead to symmetric/mirrored outcomes (e.g. no distinct but visually identical states).
> - Rotation generally based on 'proximity where it looks like the piece should (be able to) go'.
> - Pieces should prefer downwards placement, not 'teleport up' in general.
>
> See this visual/'heatmap' comparison of industry default vs. Ocular rotation:
>
> 
>
> 
>
> </details>
### *Programmers / Terminal enthusiasts:* Can you tell me more about the programming behind this terminal game?
> <details>
> <summary>
> This project handles quite a few aspects to provide an excellent user experience for a classic game. Among the many things that have accumulated inside the scope of this project are:
> </summary>
>
> - A **fully-featured [Tetromino game engine/backend](<https://github.com/Strophox/falling-tetromino-engine>)** featuring:
> * Several dozens of **configurable, advanced and important options**.
> * Provided pre-implementations of extremely common **standard mechanics**.
> * Decoupled core game loop ('interpreter') with ergonomic API (hopefully).
> * Basic but functional **modding functionality** with ergonomic API (hopefully).
> - **Different and interesting game modes**, including special game modes that rely on engine modding.
> - **Extensive TUI menus** to allow modifying all relevant configuration options without being forced to edit the save file manually:
* **Graphics** options, **Configurable keybinds**, **Gameplay settings**.
> * Providing many **curated configuration templates** for everything, inspired by existing standards and games.
> * *Sidenote:* Ever since its inception as a proof-of-concept the terminal user interface (TUI) has directly and only relied on [Crossterm](<https://crates.io/crates/crossterm>). Currently there appears no need to change this situation, though a full TUI library like [Ratatui](<https://ratatui.rs/>) might be reconsidered e.g. to handle UI translation (displaying other languages) etc.
> - A good **input-update-render game loop**.
> - Implementing **game replay**.
> - **Savefile** storage:
> * Especially **replay data serialization and compression**.
> - **Scoreboard** and **statistics**.
> - Game **graphics renderer** that handles all of the **effects** and dozens of graphics settings, efficiently.
> * Custom **buffer diff'ing** so we can guarantee we only send the minimum number of required changes to the terminal (this minimizes flicker), see <https://github.com/Strophox/tetro-tui/blob/7f0ebacee7a1ed8d399057e270f9071aa13aaaa8/src/game_renderers/standard_buffered/dense_terminal_double_buffer.rs#L103>.
> - Miscellaneous:
> * Commandline arguments.
> * Terminal limitations, all the time.
> * Doing all of the above as simply, ergonomically and as correctly as possible while providing feedback to the user when something doesn't work as expected.
> * Code quality.
>
> </details>
### What is the motivation behind this project?
> <details>
> <summary>
> Tetro TUI started as a passion project from someone who loves programming, minimalistic games and ASCII art:
> </summary>
>
> Out of curiosity I looked into the depths of this common type of Tetromino game:
> Basic versions are simple to code up, but it gets surprisingly nontrivial when it comes to comprehensively supporting of modern/advanced features and slews of QOL mechanics while dealing with terminal limitations!
>
> I've given it my best effort to implement a most featureful and customizable version that not only remains totally faithful to the basic idea of the game, but also runs and looks nice within the confines of a mere terminal - Enjoy!
> ☻ [L. Werner](<https://github.com/Strophox>)
>
> </details>
## License
Licensed under MIT.
## Provenance
100% human-sourced spaghetti code
Color palettes used: [Gruvbox](<https://github.com/morhetz/gruvbox>), [Solarized](<https://ethanschoonover.com/solarized/>), [Terafox](<https://wezterm.org/colorschemes/t/index.html#terafox>), [Fahrenheit](<https://wezterm.org/colorschemes/f/index.html#fahrenheit>), [matrix](<https://wezterm.org/colorschemes/m/index.html#matrix>), [Sequoia Monochrome](<https://wezterm.org/colorschemes/s/index.html#sequoia-monochrome>).
## Acknowledgements
A big thank you to the [AUR package](#install-on-arch-linux) maintainers:
- [wcasanova](<https://github.com/wcasanova>) and [druxorey](<https://github.com/druxorey>), and Dominiquini
Thank you to the people who provided inspiration:
- Dunspixel – regarding ['O'-spins](<https://dunspixel.github.io/ospin-guide/chapter4.html#tetro-tui>)
- Martín G – regarding new line clear effect from his own PICO-8 game
- thehuglet – for showing the [potential](<https://github.com/thehuglet/germterm>) of terminal graphics
- Akousoukos – for making [Apotris](<https://apotris.com/>)
Special Thanks
- GrBtAce, KonSola5 and bennxt – support during early dev and research
- madkiwi – for advice regarding 4-wide-6-residual combo layouts
- and RayZN and ˗ˋˏthe One and Onlyˎˊ˗ – for advice regarding the Tetro logo