Update docs, add tests for SITUATION #define flag

This commit is contained in:
mrkmntal 2026-09-24 23:40:17 -04:00
commit 94a4e6032d
3 changed files with 165 additions and 14 deletions

View file

@ -1,6 +1,6 @@
# tssr Engine Reference
The tssr engine generates soccer match results. It operates in two modes: **random** (everything fabricated on the spot) and **scripted** (driven by a `.tsr`c file).
The tssr engine generates soccer match results. It operates in three modes: **random** (everything fabricated on the spot), **scripted** (driven by `.tsrc` goals), and **situation** (hybrid — scripted goals guaranteed plus random extras).
## Overview
@ -68,9 +68,14 @@ let (home_name, away_name) = match script {
### 2. Score & Scorers
Two paths depending on whether `goal()` was scripted:
Three paths depending on script configuration:
**Scripted goals present:**
**Situation mode (SITUATION + goal()):**
- Partition scripted goals by team
- Add 0-5 random extra goals per team from roster/name pools
- Score = scripted + random
**Scripted goals only (goal() without SITUATION):**
- Partition goals by team
- Score = count of goals per team
- Scorers = exact `(player, minute)` from script
@ -111,19 +116,37 @@ struct TsrcScript {
away_var: String, // variable name for away
goals: Option<Vec<ScriptedGoal>>, // scripted goals (None = random)
output_mode: OutputMode, // UnicodeTable, AsciiTable, Json
situation_mode: bool, // hybrid deterministic/stochastic
}
```
### Override Behavior
| Field | Source when scripted | Source when random |
|-------|---------------------|-------------------|
| Team names | `s.teams[home_var]` | Random from TEAM_NAMES |
| Player roster | `s.roster[team]` | Empty (random names) |
| Goal scorers | Exact from `s.goals` | `pick_scorers()` |
| Score | Count from `s.goals` | `rng.gen_range(0..=5)` |
| Shots | `max(score, random)` | `rng.gen_range(4..=18)` |
| Possession | Always random | Always random |
| Field | Scripted | Situation | Random |
|-------|-----------|------------|--------|
| Team names | `s.teams[home_var]` | `s.teams[home_var]` | Random from TEAM_NAMES |
| Player roster | `s.roster[team]` | `s.roster[team]` | Empty (random names) |
| Goal scorers | Exact from `s.goals` | Scripted + 0-5 random/team | `pick_scorers()` |
| Score | Count from `s.goals` | Scripted + random | `rng.gen_range(0..=5)` |
| Shots | `max(score, random)` | `max(score, random)` | `rng.gen_range(4..=18)` |
| Possession | Always random | Always random | Always random |
## Situation Mode
Hybrid deterministic/stochastic mode enabled by `#define SITUATION`. When combined with `goal()`:
```rust
// Partition scripted goals
let (home_goals, away_goals) = partition_by_team(&s.goals, home_name, away_name);
// Add 0-5 random extra goals per team
let extra_home = rng.gen_range(0..=5);
let extra_away = rng.gen_range(0..=5);
home_goals.extend(pick_scorers(home_roster, extra_home, rng));
away_goals.extend(pick_scorers(away_roster, extra_away, rng));
```
Requires at least one `goal()` — error if SITUATION is defined without goals.
## Output Formatting
@ -256,6 +279,7 @@ tssr/
│ └── README.md # Engine reference
├── scripted_match.tsrc # Example: scripted goals
├── defines-test-match.tsrc # Example: ASCII_TABLE mode
├── situation-demo.tsrc # Example: SITUATION hybrid mode
├── gnu-bsd-soccer.tsrc # Example: variable references
└── test_match.tsrc # Intentionally broken (for testing errors)
```
@ -276,5 +300,6 @@ cargo test
19 integration tests cover:
- `.tsrc` language validation (13 tests)
- `#define` directive handling (6 tests)
- SITUATION mode validation
- All error conditions produce correct error messages
- Happy paths for valid scripts