# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## [Unreleased]

- *NOTE: Listed unreleased changes might not be comprehensive. Check using `git diff vX.Y.Z HEAD` with latest `vX.Y.Z`.*


## [11.0.0] - 2026-05-28

### Changed
- Lock delay now correctly stays at start/base value until fall delay is done decreasing.


## [10.0.0] - 2026-05-19

### Changed
- Generalized `TileData` in `Game` and others; The game can now hold custom tile data.
- `lib.rs` has changed its exports: `prelude_base` exports non-generic types, constants, traits, `prelude_generic` all the rest, and `prelude_generic_stdized` the same generic types but with reasonable defaults chosen.
- `Board` is now a `Vec<(line, is_frozen)>` instead of a `[Line; BOARD_HEIGHT]`.
- Lines can now be 'frozen', which means they are not cleared even if they happen to be full.
- Many renames. (TODO: List explicitly for correctness.)

### Fixed
- Piece locks after Hard Drop now, which didn't happen before when movement button was held and prompted an autoshift to occur first.

### Removed
- `type TileID = u8`


## [9.0.0] - 2026-05-04

### Added
- Examples folder (run with `cargo run --example`): 'tui' and 'tui_customized_engine'.
- `trait TetrominoGenerator`, `struct StdTetGen`.
- `trait PieceRotator`, `struct StdPceRot`.
- `struct DelayTable`,
- `type DelayData = Either<DelayParams, DelayTable>`.
- `type SoftDropRate = Either<ExtNonNegF64, ExtDuration>`.
- `Configuration::delayed_soft_drop`
- `GameModifier::stats`
- `struct DebugMod` (impls `GameModifier)

### Changed
- Scoring formula has changed.
- `Game` and various other types internally (`core`) use general `TetrominoGenerator`s and `PieceRotator`s.
- Renames:
    * internal modules.
    * `Phase::LinesClearing` -> `::ClearingLines`
    * `GameEndCause::TopOut` -> `::BufferOut`
    * `State::{piece_generator,piece_preview,piece_held}` -> `::{tetromino_generator,tetromino_preview,tetromino_held}`
- `Piece::orientation` is now serialized as `ori`.
- Switch to Rust Edition 2024, v1.95.0.
- Game `HEIGHT` is now 32.

### Fixed
- Locking a piece that is not fully on board does not crash the engine anymore, but only locks the relevant tiles.
- `ensure_shift_delay_lt_lock_delay`:
    * When a piece is auto-shifting and touches the ground, it will readjust its next auto-shift to happen sooner in case lock would be faster. (E.g.: moving left, auto-shift at 2.2s, but piece hits ground at 2.0s and wants to lock down at 2.1s -> auto-shift happens at 2.1s (before lock) and refreshes the lock and auto-shift timers as well.)
    * When a piece is auto-shifting and already on the ground, and one direction is released but the other direction is still active, it will readjust its next auto-shift to happen sooner in case lock would be faster. (E.g.: moving left, release left but right is held with auto-shift at 4.4s, but lock would occur at 4.2s -> auto-shift happens at 4.2s (before lock) and refreshes the lock and auto-shift timers as well.)

### Removed
- `struct NotificationLevel`, variant `Notification::Debug`.

## [8.2.0] - 2026-04-17

### Fixed
- Prevent auto-move event resetting lock time even though piece has not changed position. 


## [8.1.0] - 2026-04-17

### Changed
- 180° rotation for SRS is not visual anymore (i.e. no visual change for 180°-symmetric pieces), but actually acts like two left- or right rotations (visual wobble).


## [8.0.0] - 2026-04-17

### Changed
- `TetrominoGenerator::Uniform` has been generalized to `::Classic`.


## [7.1.0] - 2026-04-15

### Changed
- Dependencies updated.


## [7.0.0] - 2026-04-15

### Changed
- `GameModifier::args` is now `::cfg` suggesting it can be used for more general configuration.


## [6.0.0] - 2026-04-15

### Changed
- `Notification::LinesClearing` now includes full `lines: Vec<(usize, [TileID; Game::WIDTH])>` information.


## [5.0.0] - 2026-04-14

### Changed
- `Notification::GameEnded` finally includes `cause: GameEndCause` information.


## [4.2.0] - 2026-04-14

### Changed
- The coordinates produces by `Piece::minos` follow a guaranteed (likely more useful) order now.


## [4.1.0] - 2026-04-13

### Added
- `impl TryFromf64> for ExtNonNegF64`
- `impl From<ExtNonNegF64> for f64`


## [4.0.0] - 2026-04-07

### Added
- Initial Move and Initial Teleport Systems added.
- `RotationSystem::free_rotate`
- `trait CoordAdd { fn add... }`
- `Piece::offset`

### Changed
- Initial systems (see `allow_spawn_manipulation`) remade from scratch.
- Renames:
    * `allow_spawn_actions` -> `allow_spawn_manipulation`
    * `fits_onto` -> `fits_on`
    * `RotationSystem::Raw` -> `::Debug`

### Fixed
- `ensure_shift_delay_lt_lock_delay` no longer tries/needs to make shift_delay 1 ns shorter for no reason.

### Removed
- `Piece::reoriented_offset_on`


## [3.0.0] - 2026-04-06

### Changed
- Movement mechanics have been adapted slightly:
    * Teleport Left/Right now uses DAS as well, which allows e.g. teleporting and right and moving one space left without being forced to release teleport before the move.
    * Current implementation only allows manual moves within the Tele DAS timeframe.
- The following structs have had their fields annotated such that their serialzation is much shorter:
    * `Piece`, `DelayParameters`, `GameLimits`, `Configuration`, `StateInitialization`, `TetrominoGenerator`
- Renames:
    * `Tetromino::piece_spawn_state` -> `Tetromino::spawn_piece`
    * `piece_preview_count` -> `generate_piece_preview`
    * `allow_initial_actions` -> `allow_spawn_actions`
    * `consecutive_line_clears` -> `consecutive_lineclears`
    * `lock_time_cap` -> `lock_cap_time`
    * `points_bonus` -> `point_bonus`
    * `is_perfect_clear` -> `is_perfect`
    * `auto_move_scheduled` -> `autoshift_scheduled`
    * `on_auto_move_pre/post` -> `on_autoshift_pre/post`
    * `ensure_move_delay_lt_lock_delay` -> `ensure_shift_delay_lt_lock_delay`
    * `Coord` -> `Coordinate`
    * `CoordOffset` -> `Offset`


## [2.0.0] - 2026-04-02

### Added
- `Tetromino::piece_spawn_state`.

### Changed
- DAS now (correctly) applies on every direction change.
    * E.g., holding both directions and then releasing the current one applies DAS into opposite direction before ARR starts.
- Holding any of the `Button::Teleport*` variants now correctly keeps piece teleporting in that direction.
    * I.e. `TeleportDown` is now closer to "`SoftDrop` + SDF=∞" than it is to "`HardDrop` + no piece lock".
- Slight but breaking changes in public fields and types.
    * `blocked_lines -> top_lines`
    * `previous_piece`,`updated_piece` -> `height_dropped`,`dropped_piece`
    * `TileTypeID`,`tiletypeid` -> `TileID`,`tile_id`
    * `score_bonus` -> `points_bonus`

### Fixed
- `TetrominoGenerator::Stock` is now correctly implemented to simulate a random bag.
    * Instead of choosing uniformly among *tetromino types remaining*, which leads to biased picks in extreme bags.


## [1.5.0] - 2026-03-25

### Changed
- Minor internal changes to engine.
    * Notably, points bonus is applied after lines clear, not on lock.

### Fixed
- Bug where game limited by pieces would only end on lineclear, not lock.


## [1.4.0] - 2026-03-23

### Added
- `GameModifier` trait with many, many proper hooks (methods).
    * `try_clone` for mods!

### Changed
- Major revamp of Modding facilities.
- Renames:
    * 'feedback' terminology becomes 'notification'.
    * `FeedbackVerbosity` -> `NotificationLevel`.
    * `Feedback` -> `Notification`.
    * no more `FeedbackMsg = (Feedback, Time)`, but `NotificationFeed = Vec<(Notification, Time)>`.
* `Game::clone_unmodded` now becomes the more proper `Game::try_clone` (if modifiers' `try_clone` succeed).


## [1.3.0] - 2026-03-22

### Added
- `Piece::is_airborne`.

### Changed
- Player input is now handled before falling/locking/auto-moving if they occur at the exact same in-game-time.
- Auto-movement is now handled before falling/locking if they occur at the exact same in-game-time.
- Auto-movement now consistently causes its own timeline event every time. (Relevant for mods.)
- Use `ChaCha8Rng` (previously `ChaCha12Rng`).
- Module names: `builder`, `randomization`, `rotation`

### Removed
- `struct PieceData { .. }` has been superseded by `Phase::PieceInPlay { .. }`.


## [1.2.0] 2026-03-20

### Added
- `ensure_move_delay_lt_lock_delay` toggle: Ensures that when lock delay is very low, DAS/ARR is dynamically adapted to be shorter.
- `ButtonsState` type alias.
- `GameLimits` struct and methods `::{new, single, iter}`.
- `GameEndCause::{Custom(String), TopOut { blocked_lines: Vec<Line> }}`.
- Left-/Right-handed versions of classic rotation: `RotationSystem::{ClassicL, ClassicR}`.
- `TetrominoGenerator::Recency` gained new field `is_base_not_exp`, further providing control over weights calculation.

### Changed
- Default `Configuration` adapted.
- `Coord`s are now `isize` instead of `usize` (for flexibility; we need to check bounds either way).
- Refactor entire player movement logic:
    * Improved handling of incoming button activations even while button is still active / has not been deactivated yet.
    * Handle edge cases where movement buttons are pressed at the exact same instant.
- Piece offset / rotation logic has been cleaned up and more explicitly implements desired behavior in edge cases.
    * See `Piece::{fits_onto, offset_on, reoriented_offset_on, find_reoriented_offset_on}`.
- Game more efficiently checks most limits only when necessary instead of every update loop iteration.
- `Game::forfeit` is now much more similar in functionality and behavior to `Game::update`.
- `Game::result` has been superseded by `Game::has_ended`.
- `GameResult` has been superseded by `GameEndCause`.
- `ButtonChange::{Press, Release}` has been superseded by `Input::{Activate, Deactivate}`.
- `GameEndCause::BlockOut` -> `GameEndCause::BlockOut { blocked_piece: Piece }`.
- `GameEndCause::LockOut` -> `GameEndCause::LockOut { locking_piece: Piece }` + do not lock this piece onto board.
- `GameEndCause::Forfeit` -> `GameEndCause::Forfeit { piece_in_play: Option<Piece> }`
- `Phase::GameEnd { result: GameResult }` -> `Phase::GameEnd { cause: GameEndCause, is_win: bool }`.
- `TetrominoGenerator`s tweaked and hardened against crashes.
- List of renames:
    * `Offset` -> `CoordOffset`
    * `allow_prespawn_actions` -> `allow_initial_actions`
    * `soft_drop_divisor` -> `soft_drop_factor`
    * `lenient_lock_delay_reset` -> `allow_lenient_lock_reset`
    * `end_conditions` -> `game_limits`
    * `Button::RotateAround` -> `Button::Rotate180`
    * `GameOver` -> `GameEndCause`
    * `capped_lock_time` -> `lock_time_cap`
    * `UpdateGameError::GameEnded` -> `UpdateGameError::AlreadyEnded`
    * `SKYLINE_HEIGHT` -> `LOCK_OUT_HEIGHT`
    * `buttons_pressed` -> `active_buttons`
    * `TetrominoGenerator::BalanceRelative -> TetrominoGenerator::BalanceOut`
    * `recency` -> `snappy_recency`


## [1.1.0] - 2026-03-08

### Fixed
- When left and right are pressed + left[/right] was pressed last,
  the mere act of releasing right[/left] by itself does not trigger a move to the left[/right] anymore.


## [1.0.0] - 2026-02-16

### Added
- Initial release
    * Game implementation (notably engine type definitions and `Game::update`) split off and transferred to here from main proof-of-concept frontend.

### Changed
-

### Fixed
-

### Removed
-
