# `.tsrc` Language Reference `.tsrc` (Tiny Soccer Sim Rust C) is a minimal C-like DSL for scripting soccer matches in tssr. It lets you define teams, assign players, script goals, and control output format — all from a plain text file. ## Invocation ### Via `--script` flag ```bash tssr --script match.tsrc # table output tssr --script match.tsrc --json # JSON output ``` ### Via shebang Add this as the first line of your `.tsrc` file: ```tsrc #!/usr/bin/env tssr ``` Then make it executable and run directly: ```bash chmod +x match.tsrc ./match.tsrc ``` ## Language Syntax ### Team Declaration ```tsrc string var_name = "Team Name"; ``` Declares a variable that holds a team name. Variables can be referenced later in `players[]`, `goal()`, and `match()`. **Rules:** - Variable names must be alphanumeric + underscores - Duplicate variable names are an error - The string value is the team's display name ### Player Assignment ```tsrc players["Player Name"] = var_name; players["Player Name"] = "Team Name"; ``` Assigns a player to a team. The right side can be either a previously declared variable or a string literal. **Rules:** - The team (right side) must resolve to a team that appears in `match()` - The same player cannot be added to the same team twice - Variables must be declared before use (forward references are an error) ### Scripted Goals ```tsrc goal(minute, team, player); ``` Scripts a specific goal event. When any `goal()` appears, the match score becomes deterministic. | Argument | Type | Description | |----------|------|-------------| | `minute` | integer (1-90) | Minute the goal was scored | | `team` | variable or string | The team that scored | | `player` | string literal | The player who scored | **Rules:** - Minute must be between 1 and 90 - Team must be one of the two match teams - Player must be on that team's roster - The same player cannot score at the same minute twice - Variables must be declared before use ### Match Selection ```tsrc match(var_home, var_away); ``` Selects which two teams play. **Required** — every `.tsrc` script must contain exactly one `match()`. **Rules:** - Both variables must be previously declared - Home and away must be different teams - Both teams must have at least one player assigned ### Output Mode Directive ```tsrc #define JSON #define ASCII_TABLE #define UNICODE_TABLE ``` Sets the output format for the match. Optional — defaults to `UNICODE_TABLE`. **Rules:** - Only one output mode `#define` per script - Must be one of the three known modes - Unknown names produce an error The `--json` CLI flag always overrides the script's `#define`. ### Situation Mode Directive ```tsrc #define SITUATION ``` Enables hybrid deterministic/stochastic mode. Scripted goals are guaranteed, but additional random goals (up to 5 per team) are also generated. **Rules:** - Requires at least one `goal()` call — error without one - Can be combined with any output mode `#define` - Scripted goals always appear; random goals vary per run - Random goals are drawn from the team's roster (or name pools if roster is empty) ## Complete Grammar ``` script = statement* statement = define | string_decl | player_insert | goal_stmt | match_stmt define = "#define" (identifier | "SITUATION") string_decl = "string" identifier "=" string_literal ";" player_insert = "players" "[" string_literal "]" "=" (identifier | string_literal) ";" goal_stmt = "goal" "(" number "," (identifier | string_literal) "," string_literal ")" ";" match_stmt = "match" "(" identifier "," identifier ")" ";" comment = "//" .* "\n" shebang = "#!" .* "\n" ``` ## Output Modes | Mode | `#define` | CLI flag | Description | |------|-----------|----------|-------------| | Unicode table | `UNICODE_TABLE` (default) | — | Box-drawing characters (`╔`, `═`, `║`) | | ASCII table | `ASCII_TABLE` | — | ASCII-only (`+`, `-`, `|`) | | JSON | `JSON` | `--json` | Machine-readable JSON | ## Match Generation Modes | Mode | `#define` | Behavior | |------|-----------|----------| | Random | (none) | Everything is randomized | | Scripted | (none, with `goal()`) | Goals, scorers, minutes are exact | | Situation | `SITUATION` | Scripted goals guaranteed + 0-5 random extra goals per team | ## Scripted Goals Behavior ### Deterministic Mode (goal() without SITUATION) When `goal()` is used, the match becomes deterministic: | Field | Behavior with `goal()` | Behavior without | |-------|------------------------|-------------------| | Score | Count of goals per team | Random 0-5 | | Scorers | Exact `(player, minute)` from script | Random names + minutes | | Shots | `max(score, random(4..=18))` — floor = goals | Random 4-18 | | Shots on target | `max(score, random(1..shots))` | Random | | Possession | Still random | Random | ### Situation Mode (goal() with SITUATION) Hybrid deterministic/stochastic — scripted goals are guaranteed, plus random extras: | Field | Behavior in SITUATION mode | |-------|---------------------------| | Score | Scripted goals + 0-5 random extra per team | | Scorers | Exact scripted goals + random from roster/name pools | | Shots | `max(total_score, random(4..=18))` | | Shots on target | `max(total_score, random(1..shots))` | | Possession | Still random | ## Variable Resolution Variables are resolved at parse time by looking up their declared value: ```tsrc string home = "GNU United"; string away = "BSD Athletic"; players["Richard Stallman"] = home; // resolves to "GNU United" players["Theo de Raadt"] = away; // resolves to "BSD Athletic" goal(12, home, "Richard Stallman"); // goal for "GNU United" goal(34, "BSD Athletic", "Theo de Raadt"); // literal also works match(home, away); // GNU United vs BSD Athletic ``` You can mix variables and literals freely. Forward references (using a variable before its `string` declaration) are an error. ## Validation The parser is strict. Any inconsistency produces an error and exits immediately. ### Error Conditions | Error | Cause | |-------|-------| | `undefined variable 'x'` | Variable used before `string x = ...` declaration | | `variable 'x' redefined` | Two `string x = ...` declarations | | `players[...] = "X" which is not a match team` | Roster team isn't in `match()` | | `duplicate player "X" on team "Y"` | Same player added to same team twice | | `"X" has no players assigned` | A match team has zero players | | `match() has the same team for both home and away` | Both `match()` args resolve to same team | | `no match() directive found` | Missing `match()` | | `goal() minute must be between 1 and 90` | Invalid minute | | `player "X" is not on team "Y"` | Goal references unregistered player | | `duplicate goal` | Same player scores same minute twice | | `undefined variable 'x' in goal()` | Goal uses undeclared team variable | | `duplicate #define` | More than one output mode `#define` | | `unknown #define 'FOO'` | Unrecognized output mode | | `#define SITUATION requires at least one goal() call` | SITUATION defined without any `goal()` | ## Full Example ```tsrc #!/usr/bin/env tssr // GNU United vs BSD Athletic #define ASCII_TABLE string home = "GNU United"; string away = "BSD Athletic"; players["Richard Stallman"] = home; players["Brian Fox"] = home; players["Linus Torvalds"] = home; players["Theo de Raadt"] = away; players["Jordan Hubbard"] = away; players["Ken Thompson"] = away; goal(12, home, "Richard Stallman"); goal(34, away, "Theo de Raadt"); goal(56, home, "Brian Fox"); goal(78, "BSD Athletic", "Ken Thompson"); goal(89, home, "Linus Torvalds"); match(home, away); ``` **Output:** ``` +------------------------------------+ + TSSR MATCH RESULT + +------------------------------------+ + GNU United 3-2 BSD Athletic + +------------------------------------+ + + +Shots 4 5 + +On Target 3 2 + +Possession 48% 52% + + + +Goals: + +GNU United: Richard Stallman (12') + +GNU United: Brian Fox (56') + +GNU United: Linus Torvalds (89') + +BSD Athletic: Theo de Raadt (34') + +BSD Athletic: Ken Thompson (78') + +------------------------------------+ ``` ## Situation Mode Example ```tsrc #!/usr/bin/env tssr // Hybrid mode: some goals guaranteed, others random #define SITUATION string home = "GNU United"; string away = "BSD Athletic"; players["Richard Stallman"] = home; players["Brian Fox"] = home; players["Linus Torvalds"] = home; players["Theo de Raadt"] = away; players["Jordan Hubbard"] = away; players["Ken Thompson"] = away; goal(12, home, "Richard Stallman"); // guaranteed goal(67, away, "Theo de Raadt"); // guaranteed // Up to 5 additional random goals per team will be added match(home, away); ``` Each run produces different scores — scripted goals always appear, random goals vary: ``` Run 1: GNU United 2-3 BSD Athletic Run 2: GNU United 1-2 BSD Athletic Run 3: GNU United 2-2 BSD Athletic ```