tiny-soccersim-rust/docs/tsrc-language
2026-09-24 23:40:17 -04:00
..
README.md Update docs, add tests for SITUATION #define flag 2026-09-24 23:40:17 -04:00

.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

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:

#!/usr/bin/env tssr

Then make it executable and run directly:

chmod +x match.tsrc
./match.tsrc

Language Syntax

Team Declaration

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

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

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

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

#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

#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:

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

#!/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

#!/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