-
released this
2026-04-21 14:44:33 +02:00 | 35 commits to main since this releaseInternal refactor + UX polish.
v0.11.1is a non-breaking release:
the grammar accepted by the parser, the engine behaviour, and the
public API viabs_scoring::*are all unchanged compared to
v0.11.0. Yourv0.11.0games, scripts, and workflows continue to
work exactly the same way.♻️ Command-taxonomy refactor
The scoring-command pipeline now has a single source of truth for
the vocabulary it accepts: the newCommandKindenum in
engine::commands::kind.- Flat 26-variant enumeration, one variant per lexical verb shape.
- Companion
CommandFamilyenum groups variants by structural role
(Control,Status,Pitch,Hit,BatterOut,FielderChoice,
Steal,Advance). CommandKind::family()andCommandKind::canonical_name()helpers.- 4 invariant tests verify that every variant has a family, every
variant has a canonical name, and the family partition covers the
whole enum.
Parallel sub-enums removed
The tag sub-enums that used to duplicate the taxonomy at each
pipeline layer are gone. Each layer now referencesCommandKind
directly:Layer Before v0.11.1 After v0.11.1 tokens::TokenKind::VerbHitVerb/PitchVerb/StealVerb/KeywordVerb(CommandKind)segment::Segment::ControlControl(ControlKind)Control(CommandKind)segment::Segment::StatusStatus(StatusKind)Status(CommandKind)segment::Segment::PitchPitch(PitchKind)Pitch(CommandKind)segment::Segment::Hit { kind }HitKindCommandKindvalidator::Resolved::Pitch / HitPitchKind/HitKindCommandKindBatterOutKindis retained — its variants carry fielder identifiers,
not just a tag — but gains a.command_kind()helper. Note that
FlyOut { foul: true }maps toCommandKind::FoulFlyOut, which is
a lexically distinct verb (ff<n>) in the grammar.Lexer simplification
Three regexes (
RE_HIT_VERB,RE_PITCH_VERB,RE_STEAL_VERB) have
been removed. Parameter-less verbs are now recognised by a single
exact-matchmatchon lowercased text — faster and more readable
than carrying a regex per one-letter keyword. Regex use is reserved
to verbs with numeric parameters (o<n>,f<n>,l<n>,if<n>,
fielding sequences).Why this matters
Adding a new command in a future release (caught stealing, errors,
sacrifice variants, etc.) now requires touching one enum variant
and the handful of handlers that dispatch on its family, instead of
updating four or five parallel sub-enums acrosstokens.rs,
segment.rs,validator.rs, and their docs. The module doc on
kind.rsincludes a step-by-step "adding a command" checklist.🎨 Scoreboard UX polish
The TUI scoreboard has been redesigned for clearer at-a-glance
comprehension:- Batting team row highlighted with yellow + bold and a subtle
left-edge marker — the active half-inning is immediately obvious. - Linescore header reworked to render with
Line/Spaninstead
of a flat string; the current inning is emphasised via reversed
style. - Count styling. Dynamic highlighting per count via the new
styled_count_spanhelper:- Full count (3-2) → reversed + bold.
- Critical counts (3-1, 2-2) → yellow + bold.
- Outs indicator. Two dots (
○/●) instead of three via the
newstyled_outs_spanshelper. Active outs in yellow + bold,
inactive in dark gray. - Status line re-rendered with mixed styled spans and proper
centring; redundant inning indicator ("4↑") removed. - Internal helper rename:
build_linescore_lines→
build_linescore_header_and_range. These helpers are
module-privatefn, notpub— no public API break.
🧪 Tests
All existing command-pipeline tests (
tokens,segment,
grammar::mod,validator,parserfacade) pass unchanged —
structural refactor only, same inputs produce the same outputs.4 new invariant tests in
kind.rsverify the coverage of the new
taxonomy. Total: 130+ existing + 4 = green on Rust 1.95 stable.📦 Status
Stable release. Fully backward compatible with
v0.11.0.Full details in CHANGELOG.md,
STRUCTURE.md, and
SCORING_GUIDE.md.Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
released this
2026-04-21 10:14:55 +02:00 | 44 commits to main since this releaseFirst stable release of the v0.11.0 milestone.
v0.11.0aggregates the structural refactor fromalpha1, the grammar
refactor fromalpha2, and eight issue fixes (#55, #56, #59, #60, #61,
#62, #64, #66) that landed on thev0.11.0-alpha2-fix_codexbranch.
Public API viabs_scoring::*is unchanged compared to the previous
v0.10.x releases.♻️ Grammar refactor (from alpha2)
The scoring-command parser has been rebuilt on top of a formal grammar.
Parsing is now split into two clearly-separated stages:- A stateless syntactic layer that classifies tokens and parses
comma-separated segments via regex-assisted recognisers. - A state-aware validator that cross-checks each segment against
the currentGameState.
Every error found in a single input line is surfaced at once, with a
1-based segment index, rather than the parser stopping at the first
failure.The subject rule
The batting-order subject (
1–9) is mandatory on every action
segment, with one documented exception: verbs whose shape cannot be
confused with a lone digit may omit the subject, in which case it
defaults to the current batter.Verb family Subject is Implicit default Hit verbs ( h,2h,3h,hr)optional current batter Fielding sequence ( 63,6-3,862, …)optional current batter Fly ( f8), foul-fly (ff3)optional current batter Line-out ( l6), infield-fly (if4)optional current batter Fielder's choice ( o6 1b)optional current batter Unassisted single-digit ( 5)mandatory — Steal ( st)mandatory — Standalone runner-advance ( 2b)mandatory — Pitches ( b,k,s,f,fl)forbidden — Control / status ( exit,playball, …)forbidden — Order-independent segments
The following three inputs all produce the same triple play:
5 l6, 3 64, 4 43 3 64, 5 l6, 4 43 4 43, 5 l6, 3 64Accumulated error diagnostics
Every syntactic or semantic problem is reported as:
error at segment <N>: '<text>': <reason>See
SCORING_GUIDE.mdsection 11 for the full diagnostic reference.🏗️ Structural refactor (from alpha1)
core/absorbed intoengine/: game logic now lives in a single
coherent subtree.- Top-level
commands/removed and moved underengine/commands/,
eliminating ambiguity withcli/commands/. cli/commands/renamed tocli/screens/to reflect its real role.- Anti-homonym renames:
utils/cli.rs→utils/term.rs,
ui/cli.rs→ui/cli_impl.rs. - Deprecated compatibility shims from v0.8.1 removed.
🐛 Correctness fixes on top of alpha2
Issue Fix #55 Composite defensive plays — live and replay converge on the same GameStatefor triple plays, runner outs on FC, and FC-safe advances on runners.#56 FC-to-home run credit — batter reaching home directly on a fielder's choice now credits the run. #59 Steal mixing — steals combined with end-of-PA actions (hit, out, FC, standalone advance) are rejected. #60 Fielding-sequence lexer — invalid fielder 0in60or6-0is now rejected at lex time.#61 Resume double-scoring — walk and hit movements are no longer re-applied in the replay's composite pass. #62 Composite replay pass whitelist — only ground_out,fly_out,line_out,infield_fly,unassisted_out,fielders_choiceare replayed via the composite pass.#64 Inning buckets on HOME movements — replay path now increments per-inning away_innings/home_innings.#66 Cross-half steal-home — credited to the team/inning recorded on the row, not the current traversal position. 🔁 Migration notes
External consumers of
engine::commands::parser::parse_engine_commands
must use the new signature introduced inv0.11.0-alpha2:let commands = match parse_engine_commands(line, &state) { Ok(cmds) => cmds, Err(errors) => { for err in errors { ui.emit(UiEvent::Error(err.to_string())); } continue; } }; for cmd in commands { /* … */ }Input strings that used to parse under the old pre-v0.11.0 ad-hoc
rules but do not follow the subject-always grammar are now rejected
at input time. In particular:st 2b,2b,5 b→ now errors.5alone → now an error; use<n> 5(e.g.5 5) for a batter
unassisted by the third baseman.
The top-level re-exports from
bs_scoring::*(Database,Menu,
GameState,Player,Team,League, scoring types, …) are
unchanged.🧪 Tests
83 unit tests shipped with the grammar refactor, plus unit tests
added for each fix (10 new tests acrossengine::apply::testsand
engine::commands::validator::tests).📦 Status
Stable release. Ready for production use.
Full details in CHANGELOG.md,
SCORING_GUIDE.md, and STRUCTURE.md.Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- A stateless syntactic layer that classifies tokens and parses
-
released this
2026-04-20 16:36:22 +02:00 | 57 commits to main since this releaseSecond alpha of the v0.11.0 milestone.
This alpha rebuilds the scoring-command parser on top of a formal
grammar. Parsing is now split into two clearly-separated stages — a
stateless syntactic layer that classifies tokens and parses
comma-separated segments via regex-assisted recognisers, and a
state-aware validator that cross-checks each segment against the
currentGameState. Every error found in a single input line is
surfaced at once, with a 1-based segment index, rather than the parser
stopping at the first failure.♻️ Grammar refactor
The subject rule
The batting-order subject (
1–9) is mandatory on every action
segment, with one documented exception: verbs whose shape cannot be
confused with a lone digit may omit the subject, in which case it
defaults to the current batter.Verb family Subject is Implicit default Hit verbs ( h,2h,3h,hr)optional current batter Fielding sequence ( 63,6-3,862, …)optional current batter Fly ( f8), foul-fly (ff3)optional current batter Line-out ( l6), infield-fly (if4)optional current batter Fielder's choice ( o6 1b)optional current batter Unassisted single-digit ( 5)mandatory — Steal ( st)mandatory — Standalone runner-advance ( 2b)mandatory — Pitches ( b,k,s,f,fl)forbidden — Control / status ( exit,playball, …)forbidden — Order-independent segments
Composite plays, runner overrides after a hit, and mixed lines can be
typed in any order relative to one another. For example, the
following three inputs all produce the same triple play:5 l6, 3 64, 4 43 3 64, 5 l6, 4 43 4 43, 5 l6, 3 64Accumulated error diagnostics
Every syntactic or semantic problem is reported as:
error at segment <N>: '<text>': <reason>When a line contains multiple problems, every one is emitted in a
single pass. SeeSCORING_GUIDE.mdsection 11 for the full diagnostic
reference.🏗️ Implementation
New modules under
src/engine/commands/:Module Responsibility errors.rsParseError/ValidationError/CommandErrorwith segment indexgrammar/tokens.rsLazy-compiled regexes + TokenKindclassifiergrammar/segment.rsSegmentenum +parse_segment/parse_linevalidator.rsState-aware classification, line-level checks, coalescing parser.rsFacade: parse_engine_commands(&str, &GameState)🔁 Migration notes
External consumers of
engine::commands::parser::parse_engine_commands
must update their call sites:// Before (v0.11.0-alpha1 and earlier): let commands: Vec<EngineCommand> = parse_engine_commands(line); for cmd in commands { if let EngineCommand::Unknown(msg) = &cmd { /* handle */ } /* … */ } // After (v0.11.0-alpha2): let commands = match parse_engine_commands(line, &state) { Ok(cmds) => cmds, Err(errors) => { for err in errors { ui.emit(UiEvent::Error(err.to_string())); } continue; } }; for cmd in commands { /* … */ }Input strings that used to parse under the old ad-hoc rules but do not
follow the subject-always grammar will now be rejected at input time.
In particular:st 2b,2b,5 b→ now errors, previously handled in ad-hoc ways.5alone → now an error; use<n> 5(e.g.5 5) for a batter
unassisted by the third baseman.
The top-level re-exports from
bs_scoring::*(Database,Menu,
GameState,Player,Team,League, scoring types, …) are
unchanged.🧪 Tests
130 new unit tests covering:
- token classification (12 tests)
- segment parsing, happy paths + every error variant (38 tests)
parse_linefacade including error accumulation (5 tests)- validator: per-segment checks, line-level checks, coalescing,
order invariance (20 tests) - parser facade integration (6 tests)
📦 Dependencies
- Adds
regex = "1.11.1"as a runtime dependency.
📦 Status
Pre-release. Use
v0.10.6for production.Full details in CHANGELOG.md,
SCORING_GUIDE.md, and STRUCTURE.md.Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v0.11.0-alpha1 — Structural refactor of src/ Pre-release
released this
2026-04-20 10:44:55 +02:00 | 61 commits to main since this releaseFirst alpha of the v0.11.0 milestone.
This alpha ships only the structural refactor of
src/. Additional
features planned forv0.11.0final will be delivered in subsequent
alphas.♻️ Refactor
The source tree has been reorganized for readability and maintainability.
Runtime behaviour is unchanged and the public API re-exported from
lib.rs(Database,Menu,GameState,Player,Team,League,
scoring types, …) is preserved.What moved
From To core/runner_logic.rsengine/runners.rscore/play_ball_apply.rsengine/apply.rscore/play_ball_reducer.rsengine/reducer.rscore/parser.rsengine/notation.rscore/scoring/engine/scoring/core/menu.rscli/menu.rscommands/engine_parser.rsengine/commands/parser.rscommands/types.rsengine/commands/types.rscli/commands/*cli/screens/*utils/cli.rsutils/term.rsui/cli.rsui/cli_impl.rsWhat was removed
core/play_ball.rs— deprecated compatibility shim since v0.8.1.models/play_ball.rs— deprecated compatibility shim since v0.8.1.
Why
- Eliminated the ambiguity between the top-level
commands/module
(engine-level commands) andcli/commands/(user-facing screens). - Collapsed
core/intoengine/so all game logic lives in a single
coherent subtree. - Removed two module-name homonyms (
utils/clivsui/cli) that made
usesites harder to read. - Dropped two compatibility shims whose callers had long since migrated
to the canonical paths.
🔁 Migration notes
External consumers that still imported the old paths should update their
usestatements:crate::core::*→crate::engine::*crate::commands::engine_parser→crate::engine::commands::parsercrate::commands::types→crate::engine::commands::typescrate::cli::commands::*→crate::cli::screens::*crate::core::menu::*→crate::cli::menu::*crate::utils::cli→crate::utils::termcrate::ui::cli→crate::ui::cli_impl
📦 Status
Pre-release. Use
v0.10.6for production.Full details in CHANGELOG.md and the updated
STRUCTURE.md.Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads