
# 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 customizable and cross-platform.

> <details>
> <summary>
> 6 more demo images available;
> </summary>
>
> 
>
> 
>
> 
>
> 
>
> 
>
> 
>
> </details>
## 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, etc.
# tetro-tui
```
### Compile from source
Tetro TUI is written in [Rust 1.95.0](<https://doc.rust-lang.org/book/ch01-00-getting-started.html>) and can be compiled as usual:
```bash
git clone https://github.com/Strophox/tetro-tui
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 horizontal lines are filled they automatically clear away and everything 'stacked' above moves 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 configuration / features?
> We provide a solid amount of customization options / features for casual and (potentially) experienced players alike:
> - **Graphics:** Unicode/ASCII/Elektronika styles, old-terminal-compatible or very modern designs, 10 color palettes, hard drop/piece lock/line clear effects and much more.
> - **Game keybinds:** to your heart's desire.
> - **Gameplay/handling:** Various rotation systems, piece randomizers, adjustable preview, timings (DAS¹, ARR¹, SDF¹, LCD, ARE), IRS/IHS/.. ([¹caveat](#why-do-some-gameplay-preferences-dasarrsdf-or-some-keybinds-ctrlshiftalt-not-work-for-me)).
> - **Game mode miscellany:** Regular ('Marathon'), Swift ('40-Lines'), Classic & Master (unlocked after Regular), Puzzle, Cheese, Combo, Custom (select win condition, initial gravity, toggle gravity progress, *cmdline flags:* start board, seed).
> - **Highscores, replays, statistics, ...** - can be viewed as well as backed up with a simple **savefile**.
>
> Visuals can be customized together with your underlying terminal settings.
> E.g. one 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 a nostalgic look etc.
>
> <details>
> <summary>
> See also: Big overview of actual Tetro TUI v3.5 menus:
> </summary>
>
> **- Title screen -**
> - New Game
> - Settings
> - Scores and Replays
> - All-Time Stats
> - About
> - Quit
> *[←↓↑→/Enter/Esc/Del] or Vim, Press [?] to view keybinds anytime*
>
> **+ Start New Game +**
> - Regular: Clear 160 lines as gravity increases.
> - Swift: How quickly can you clear 40 lines?
> - *(unlocked after Regular)* Classic (lvl 0+)(*): 'NES' Gameplay/Graphics settings recommended.
> - *(unlocked after Regular)* Master: Clear 320 lines at instant gravity.
> - Puzzle: Clear 24 hand-crafted puzzles (feat. the Ocular rotation system)
> - Cheese-40: Efficiently eat through some lines. Limit∈[None, Some(10), Some(11), .., Some(40), ..]
> - Combo-30: Get consecutive line clears. Limit∈[None, Some(10), Some(11), .., Some(30), ..]
> - Survival: Lines regenerate as you place more pieces.
> - Ascent: Experimental gamemode (requires 180° rot.)
> - Custom: [Del]=reset
> * Initial fall delay = 1.000000000s (Gravity: 1.00 Hz)
> * Progressive gravity ∈ [on, off]
> * Limit ∈ [None, TimeElapsed(300s), .., PointsScored(200), .., PiecesLocked(100), .., LinesCleared(40), ..]
> - *(available after using `Ctrl+S`)* Game save
>
> **# Graphics Settings #**
> * Slot ∈ [Default, Guideline, Terminal compatibility, Focus+, Minimal, I⠐⢷⠗ Braille, Elektronika 60, NES; Custom I/II/..]
> * Tile colors ∈ [Terminal default, Just white, ANSI, Tetro Pastel, Guideline, Gruvbox, Solarized, Terafox, Fahrenheit, Matrix, Sequoia, Just amber, NES levels]
> * UI colors ∈ [Terminal default, Just black/white, Tetro Dark, Gruvbox Dark, Solarized Light, Matrix, Sequoia, Just amber, NES, OneHalfDark]
> * UI style ∈ [ASCII, Frame UTF8, Rounded frame UTF8, No frame UTF8, No hold/next-frame, Braille, Elektronika 60]
> * Hard drop effect ∈ [None, Particle trail ASCII, Particle trail 2 ASCII, Particle beam ASCII, Colored beam UTF8, White beam UTF8, Braille helix]
> * Lock effect ∈ [None, Flash white, Transforming ASCII, Pulsing block UTF8, Spiraling Braille]
> * Line clear effect ∈ [None, Disappear halfway, Disappear instantly, Blink, Flash white, Clear left-to-right, Clear outward, White clear inward, Burn outward, Pop, Pop (more), Confetti (gratuitous), Stardust, Blast, Sparks, Sparks Braille, Sparks ASCII]
> * Tile/Tetromino symbols ∈ [ASCII, Blocks UTF8, Braille, NES simulacra, Elektronika 60]
> * Small tetromino symbols ∈ [Dots ASCII, Blocks UTF8, Braille]
> * Mini tetromino symbols ∈ [Letters, Braille]
> * Normalsized tetromino previews ∈ [unlimited, 1, 2 ..]
> * Frames rendered per second ∈ [5, 10, .., 60, ..]
> * Grid ∈ [on, off]
> * Piece shadow ∈ [on, off]
> * Upcoming spawn preview (if stack high) ∈ [on, off]
> * Uniform locked tiles ∈ [on, off]
> * Main HUD ∈ [on, off]
> * Include basic keybinds HUD ∈ [on, off]
> * Show active/held buttons ∈ [on, off]
> * Lock delay visualizer ∈ [on, off]
> * FPS counter ∈ [on, off]
>
> **@ Game Keybinds @**
> * Slot (modifiable preset) ∈ [Default, Guideline, Control+, Terminal finesse, Vim; Custom I/II/..]
> * MoveLeft, MoveRight;
> * RotateLeft, RotateRight, Rotate180;
> * DropSoft, DropHard;
> * TeleDown, TeleLeft, TeleRight;
> * HoldPiece.
>
> **Settings/Adjust-Gameplay/**
> * Slot ∈ [Default, Guideline, Finesse+, Elektronika 60, Gameboy, NES; Custom I/II/..]
> * Piece rotation ∈ [Ocular, ClassicL, ClassicR, Super]
> * Piece randomization ∈ [Uniformly random, Classic (Reroll 1x), Rerollx 2x, .., 7-Bag, 14-Bag, .., Balance out, Recency (^2.5), Recency (^2.6), ..]
> * Piece preview count ∈ [0, 1, .., 4, ..]
> * Delayed auto move (DAS) ∈ [0ms, 1ms, .., 167ms, ..]
> * Auto repeat rate (ARR) ∈ [0ms, 1ms, .., 33ms, ..]
> * Delayed soft drop ∈ [None, Some(5ms), Some(10ms), .., Some(100ms), ..]
> * Soft drop rate (SDF) ∈ [raise gravity to 30 Hz, ..; 0x gravity, 0.25x gravity, .., 15x, .., inf x gravity]
> * Line clear duration (LCD) ∈ [0ms, 5ms, .., 200ms, ..]
> * Spawn delay (ARE) ∈ [0ms, 5ms, .., 50ms, ..]
> * Allow spawn manipulation (hold-IRS/IHS/IMS/ITS) ∈ [on, off]
> * Convert double-tap to teleport ∈ [None, Some(5ms), Some(10ms), ..]
>
> **§ Advanced Settings §**
> * Save ∈ ["--Nothing", "Only settings,stats --No scores,replays", "Only settings,stats,scores --No replays", "Everything (settings,stats,scores,replays)"]
> * Renderer used ∈ [Main, Halfblock, Braille]
> * Pause on focus lost ∈ [on, off]
> * 'Blindfold' game ∈ [on, off]
> * Assume enhanced-key-events available ∈ [on, off]
>
> </details>
### Why do some gameplay preferences (DAS/ARR/SDF...) or some keybinds (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: Generally unable to actually 'hold' any buttons; DAS & ARR & SDF determined by terminal (& holding Soft Drop may 'accidentally' lock the piece), unable to hold Teleport, unable to hold 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' / 'kitty keyboard protocol'](<https://sw.kovidgoyal.net/kitty/keyboard-protocol>).
>
> </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 (or deviate from) common stacker games?
> At the time of writing we implement all the most common mechanics found in the wild (and then some).
> Groups of (e.g. gameplay) settings are bundled in a 'slot'(= settings template/profile) which allows us to provide common presets as well as our suggested defaults.
>
> <details>
> <summary>
> See list of notable differences:
> </summary>
>
> **Keybinds:**
> - Default controls suggested to be **WASD + Arrow keys** (reasoning: We prefer to assign movement and rotation to separate hands instead of mixing; do not use Shift key due to common [terminal limitations](#why-do-some-gameplay-preferences-dasarrsdf-or-some-keybinds-ctrlshiftalt-not-work-for-me)).
> - Dedicated buttons supplied for **Rotate 180°**, **Teleport Down** ('Sonic Drop') and Teleport Left/Right.
>
> **Gameplay:**
> - Default rotation system suggested to be the [**Ocular Rotation** System](#experienced-players-what-is-this-ocular-rotation-system) (reasoning: Ocular is designed to be symmetrical, intuitive and flexible (in different ways) compared to the quirky / sometimes asymmetrical 'Super' Rot.Sys.).
> - Default piece randomizer suggested to be a **Recency Randomizer** (reasoning: 'Recency' is biased toward 'fairly' choosing less recent pieces but still technically allowing arbitrary piece sequences, compared to the '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' without 'minis' (reasoning: we are not preoccupied with just 'T-spins').
> - *Note:* Combos, without 'back-to-back' (reasoning: Back-to-back incentivizes playing special maneuvers, but those already yield disproportionate score bonus by themselves).
> - Exact formula: `point_bonus = lineclears*lineclears * if is_spin{ 4 }else{ 1 } * if is_perfect{ 4 }else{ 1 } + (combo - 1)`
> - **Time-based lock reset limit** (reasoning: Providing a 'hard time limit before lock'(= `N`⋅current lock delay) seems more flexible/natural than 'hard move limit before lock'(=`N` 'moves').
> - Default speed/gravity/fall curve is slightly less aggressive but very close to standard (reason: We use a simpler formula).
>
> **Graphics:**
> - Default palette is more pastel (reasoning: Uniform perceptual brightness, 'looks pretty').
>
> </details>
>
> <details>
> <summary>
> See list of <a href="https://crates.io/crates/falling-tetromino-engine">core game engine</a> mechanics:
> </summary><!--NOTE: This list is quoted from the core game engine repository.-->
>
> - 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 manipulation** (IRS/IHS/IMS/ITS; by keeping rotate/hold/move/teleport pressed during spawn),
> - **Rotation systems**: 'Ocular' (engine-specific, playtested), 'Classic', 'Super',
> - **Delayed auto-move** (DAS),
> - **Auto-move rate** (ARR),
> - **Soft drop rate** (SDF),
> - **Delayed soft drop** ('DAS but for Soft drop'),
> - **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 the 'industry default' rotation system vs. Ocular rotation:
>
> 
>
> 
>
> </details>
### *Programmers / Terminal enthusiasts:* Can you provide some insight into the programming of this terminal game?
> <details>
> <summary>
> This project handles a handful of aspects to try and provide excellent user experience for a classic game. It also aims to maintain a decently high quality in code. The scope of this project consists of:
> </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 competent **input-update-render game loop**.
> - Implementing **game replays**.
> - **Savefile** storage:
> * In particular **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...
> * Rust 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 snuck a peek to see how deep the mechanics of such a universal game can go:
> Basic versions are simple to code up, but it gets surprisingly complex when it comes to supporting all the modern/advanced features (especially while dealing with terminal limitations)!
>
> To the best of my abilities I have implemented a most featureful & customizable version that still remains faithful to the essential
idea and also looks/runs nicely within a 'mere' terminal - Enjoy!
> ☺ [L.C.Werner](<https://github.com/Strophox>)
>
> </details>
## License
Licensed under MIT.
## Provenance
100% human-sourced spaghetti code
Color palettes featured: [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>), [OneHalfDark](<https://wezterm.org/colorschemes/o/index.html#onehalfdark>).
## Acknowledgements
A big thank you to the [AUR package](#arch-linux-aurarchlinuxorg) maintainers!
- [wcasanova](<https://github.com/wcasanova>), [druxorey](<https://github.com/druxorey>) and Dominiquini
Thank you to many sources of inspiration:
- Dunspixel – regarding ['O'-spins](<https://dunspixel.github.io/ospin-guide/chapter4.html#tetro-tui>)
- Martín G / mg1399 – regarding particle-based line clear effects from his own PICO-8 game
- thehuglet – regarding the [potential of terminal graphics](<https://github.com/thehuglet/germterm>)
- Akousoukos – regarding the [customizability of Apotris](<https://apotris.com/>)
- DoktorOcelot – regarding the addition of [Survival Zen mode in Tetr.js Enhanced](<https://doktorocelot.com/tetr.js/>)
Special Thanks
- GrBtAce, KonSola5 and bennxt – help during early dev/research
- madkiwi – help with 4-wide-6-residual combo layouts
- mathmaster13 and Kitaru – help with retro mechanics
- RayZN and ˗ˋˏthe One and Onlyˎˊ˗ – help with *Tetro* logo choice
- and Alexey Leonidovich Pajitnov – for discovering this universal genre of game