193 lines
5.7 KiB
Markdown
193 lines
5.7 KiB
Markdown
# tssr — Tiny Soccer Simulator Rust
|
|
|
|
A blazing-fast CLI that generates completely random soccer matches on the spot — or run scripted matches with the `.tsrc` (Tiny Soccer Sim Rust C) language.
|
|
|
|
## Author
|
|
|
|
Mark Robillard Jr (MARKMENTAL)
|
|
|
|
## Build
|
|
|
|
```bash
|
|
cargo build --release
|
|
```
|
|
|
|
The binary will be at `target/release/tssr`.
|
|
|
|
## Usage
|
|
|
|
```bash
|
|
./tssr # random match (Unicode table)
|
|
./tssr --json # random match (JSON)
|
|
./tssr --script match.tsrc # scripted match (table)
|
|
./tssr --script match.tsrc --json # scripted match (JSON)
|
|
./tssr --help # show usage info
|
|
```
|
|
|
|
### Shebang
|
|
|
|
```tsrc
|
|
#!/usr/bin/env tssr
|
|
string home = "Team A";
|
|
string away = "Team B";
|
|
match(home, away);
|
|
```
|
|
|
|
```bash
|
|
chmod +x match.tsrc
|
|
./match.tsrc
|
|
```
|
|
|
|
## Output Modes
|
|
|
|
| Mode | How to enable |
|
|
|------|---------------|
|
|
| Unicode table | Default (no flag, no `#define`) |
|
|
| ASCII table | `#define ASCII_TABLE` in script |
|
|
| JSON | `--json` CLI flag or `#define JSON` in script |
|
|
|
|
The `--json` CLI flag always takes priority over `#define`.
|
|
|
|
### Unicode table (default)
|
|
|
|
```
|
|
╔════════════════════════════════════════╗
|
|
║ TSSR MATCH RESULT ║
|
|
╠════════════════════════════════════════╣
|
|
║ Man Page City 2 - 1 Libre Pool║
|
|
╠════════════════════════════════════════╣
|
|
║ ║
|
|
║ Shots 10 8 ║
|
|
║ On Target 4 3 ║
|
|
║ Possession 55% 45% ║
|
|
║ ║
|
|
╠════════════════════════════════════════╣
|
|
║ Goals: ║
|
|
║ Man Page City: Marco Smith (23') ║
|
|
║ Libre Pool: Dan Garcia (45') ║
|
|
╚════════════════════════════════════════╝
|
|
```
|
|
|
|
### ASCII table (`#define ASCII_TABLE`)
|
|
|
|
```
|
|
+--------------------------------------------+
|
|
| TSSR MATCH RESULT |
|
|
+--------------------------------------------+
|
|
| Man Page City 2 - 1 Libre Pool |
|
|
+--------------------------------------------+
|
|
| |
|
|
| Shots 10 8 |
|
|
| On Target 4 3 |
|
|
| Possession 55% 45% |
|
|
+--------------------------------------------+
|
|
```
|
|
|
|
### JSON (`--json` or `#define JSON`)
|
|
|
|
```json
|
|
{
|
|
"home": {
|
|
"team": "Man Page City",
|
|
"score": 2,
|
|
"shots": 10,
|
|
"shots_on_target": 4,
|
|
"possession": 55,
|
|
"scorers": [
|
|
{"player": "Marco Smith", "minute": 23}
|
|
]
|
|
},
|
|
"away": {
|
|
"team": "Libre Pool",
|
|
"score": 1,
|
|
"shots": 8,
|
|
"shots_on_target": 3,
|
|
"possession": 45,
|
|
"scorers": [
|
|
{"player": "Dan Garcia", "minute": 45}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
## Scripting with `.tsrc`
|
|
|
|
`.tsrc` (Tiny Soccer Sim Rust C) is a minimal C-like DSL for defining teams, players, goals, and output mode.
|
|
|
|
### Quick example
|
|
|
|
```tsrc
|
|
#!/usr/bin/env tssr
|
|
#define ASCII_TABLE
|
|
|
|
string home = "GNU United";
|
|
string away = "BSD Athletic";
|
|
|
|
players["Richard Stallman"] = home;
|
|
players["Brian Fox"] = home;
|
|
players["Theo de Raadt"] = away;
|
|
|
|
goal(12, home, "Richard Stallman");
|
|
goal(34, away, "Theo de Raadt");
|
|
goal(78, "BSD Athletic", "Theo de Raadt");
|
|
|
|
match(home, away);
|
|
```
|
|
|
|
### Language syntax
|
|
|
|
| Statement | Purpose |
|
|
|-----------|---------|
|
|
| `string var = "Team";` | Declare a team variable |
|
|
| `players["Player"] = var;` | Assign player to team (variable or literal) |
|
|
| `goal(min, team, "Player");` | Script a goal at a specific minute |
|
|
| `match(home, away);` | Select teams for the match (required) |
|
|
| `#define JSON / ASCII_TABLE / UNICODE_TABLE` | Set output mode |
|
|
|
|
When `goal()` is used, the score becomes deterministic — goals, minutes, and scorers are exact. Shots are floored at the goal count. Without `goal()`, everything is fully randomized.
|
|
|
|
The parser is strict: any inconsistency produces a clear error and exits immediately.
|
|
|
|
See [`docs/tsrc-language/README.md`](docs/tsrc-language/README.md) for the full language reference.
|
|
|
|
## Formatting
|
|
|
|
This project uses a custom style (Anti-GNU, Anti-1TBS) — see [STYLE.md](STYLE.md). rustfmt is disabled.
|
|
|
|
A custom formatter binary is provided:
|
|
|
|
```bash
|
|
cargo fmt-tssr # format all .rs files in src/ and tests/
|
|
cargo fmt-tssr path/to/file.rs # format a specific file
|
|
cargo fmt-tssr src/ dir/ # format specific directories
|
|
```
|
|
|
|
## Testing
|
|
|
|
```bash
|
|
cargo test
|
|
```
|
|
|
|
19 integration tests covering:
|
|
- Goal validation (player on team, minute range, duplicates, undefined variables)
|
|
- Script validation (undefined variables, same team, missing match, no players, duplicate variables)
|
|
- `#define` directive (all modes, duplicate, unknown)
|
|
- Happy paths (valid scripts, variable references, full scripted match)
|
|
|
|
## Documentation
|
|
|
|
| Document | Description |
|
|
|----------|-------------|
|
|
| [`docs/tsrc-language/README.md`](docs/tsrc-language/README.md) | Full `.tsrc` language reference, grammar, validation rules |
|
|
| [`docs/engine/README.md`](docs/engine/README.md) | Engine internals: random generation, scripted override, output formatting |
|
|
|
|
## Performance
|
|
|
|
- Single runtime dependency (`rand`)
|
|
- Release profile uses LTO, single codegen unit, panic=abort, symbol stripping
|
|
- Sub-second cold compilation
|
|
- Binary size: ~340KB stripped
|
|
|
|
## License
|
|
|
|
This project is licensed under the GNU General Public License v3.0 — see [LICENSE](LICENSE).
|