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.
Find a file
2026-09-24 21:05:10 -04:00
src Add goal scripting, as well as unit testing for the app. 2026-09-24 21:05:10 -04:00
tests Add goal scripting, as well as unit testing for the app. 2026-09-24 21:05:10 -04:00
.gitignore Initial Commit 2026-09-24 18:04:07 -04:00
Cargo.lock Add goal scripting, as well as unit testing for the app. 2026-09-24 21:05:10 -04:00
Cargo.toml Add goal scripting, as well as unit testing for the app. 2026-09-24 21:05:10 -04:00
gnu-bsd-soccer.tsrc Add scripting language capabilities to tssr for setting up soccer games programmatically, add some example scripts in files suffixed .tsrc (tiny soccer sim rust C) 2026-09-24 20:02:13 -04:00
README.md Add goal scripting, as well as unit testing for the app. 2026-09-24 21:05:10 -04:00
scripted_match.tsrc Add goal scripting, as well as unit testing for the app. 2026-09-24 21:05:10 -04:00
test_match.tsrc Add scripting language capabilities to tssr for setting up soccer games programmatically, add some example scripts in files suffixed .tsrc (tiny soccer sim rust C) 2026-09-24 20:02:13 -04:00

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.

Build

cargo build --release

The binary will be at target/release/tssr.

Usage

./tssr                          # random match (table output)
./tssr --json                   # random match (JSON output)
./tssr --script match.tsrc      # scripted match (table output)
./tssr --script match.tsrc --json  # scripted match (JSON output)
./tssr --help                   # show usage info

Shebang

.tsrc scripts can be run directly with a shebang:

#!/usr/bin/env tssr
string home = "Team A";
string away = "Team B";
// ...
match(home, away);
chmod +x match.tsrc
./match.tsrc

Output

Table mode (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')      ║
║  Man Page City: Leo Brown (67')        ║
║  Libre Pool: Dan Garcia (45')          ║
╚════════════════════════════════════════╝

JSON mode (--json)

{
  "home": {
    "team": "Man Page City",
    "score": 2,
    "shots": 10,
    "shots_on_target": 4,
    "possession": 55,
    "scorers": [
      {"player": "Marco Smith", "minute": 23},
      {"player": "Leo Brown", "minute": 67}
    ]
  },
  "away": {
    "team": "Libre Pool",
    "score": 1,
    "shots": 8,
    "shots_on_target": 3,
    "possession": 45,
    "scorers": [
      {"player": "Dan Garcia", "minute": 45}
    ]
  }
}

.tsrc Scripting Language

.tsrc (Tiny Soccer Sim Rust C) is a minimal C-like DSL for defining teams, players, and match events programmatically.

Language Reference

// Declare a team variable
string var_name = "Team Name";

// Assign a player to a team (by variable or literal)
players["Player Name"] = var_name;
players["Player Name"] = "Team Name";

// Script a goal: minute (1-90), team (var or literal), player (literal)
goal(45, var_name, "Player Name");
goal(45, "Team Name", "Player Name");

// Select teams for the match (required)
match(var_home, var_away);

Rules

  • Variables must be declared with string before use
  • match() directive is required — references variable names
  • players[] references team names (values), not variable names — but variables are resolved automatically
  • goal() arguments: minute, team, player — team can be variable or literal
  • All semicolons are required
  • // for single-line comments
  • Strict validation: any inconsistency produces an error with a helpful message and exits immediately

Validation Errors

The parser is strict — it will fail with a clear message if:

  • A variable is used before being declared
  • A variable is defined twice
  • A player is added to a team that isn't in match()
  • A player is added to the same team twice
  • A goal references a player not on that team
  • A goal references a team not in the match
  • A goal minute is outside 1–90
  • The same player scores at the same minute twice
  • A match team has no players assigned
  • match() references the same team for both home and away

Scripted Goals

When goal() is used, the match becomes deterministic:

  • Score is derived from the count of scripted goals per team
  • Goal scorers and minutes are exactly as specified
  • Shots and shots-on-target are floored at the goal count
  • Possession remains randomized

Without goal(), everything is fully randomized.

Team Names (Random Mode)

When no script is provided, teams are randomly selected from PES-style parodies referencing free software and open source:

  • Man Page City, Libre Pool, ArsenAL, Shellsea, TomThumb FC
  • Real Madread, BarcelON, BayErR, Door Mount, Inter Mission
  • Man Unixited, A-JaX, CellTick, RanGERS, LaZIO, RomaAEM
  • Port OS, BenFICA, Fine Node, AC M1Lan, PSGROOT
  • Free BSD Athletic, Kernel Panic, Tor Valdes, Debian United
  • Arch Lisbon, NixOS City, Fork Enoord, Branchand FC, Merge Conflict

Stats Included

  • Score
  • Shots & shots on target
  • Possession percentage
  • Goal scorers with minute of goal

Example Files

File Description
scripted_match.tsrc GNU United vs BSD Athletic — 5 scripted goals with mixed variable/literal team references
gnu-bsd-soccer.tsrc Variable-reference players, random goals
test_match.tsrc Intentionally broken — demonstrates error handling

Testing

cargo test

13 integration tests cover:

  • Goal validation (player on team, minute range, duplicates)
  • Script validation (undefined variables, same team, missing match, no players)
  • Happy paths (valid script, variable references, full scripted match)

Performance

  • Single runtime dependency (rand)
  • tempfile is a dev-dependency only (for tests)
  • Release profile uses LTO, single codegen unit, panic=abort, and symbol stripping
  • Designed for sub-second cold compilation and instant execution
  • Binary size: ~340KB stripped