Managing Tiebreakers
MockScores lets organizers control how teams are ranked when results are published. You can start with a template or build a custom ranking system using either the visual Blockly editor or through a text based editor.
Both editors allow for the same levels of configuration. The visual editor is recommended for most organizers, while the text editor is useful for managing complex rules and copying over configurations.
Try it now
Section titled “Try it now”The playground below is the same editor used in the app, running entirely in your browser on sample data. Drag blocks or edit the configuration text, and watch the standings re-rank in real time. Nothing here is saved.
Open the tiebreaker editor
Section titled “Open the tiebreaker editor”- Open your tournament from the organizer dashboard.
- Select Structure
- Select Manage Tiebreakers.
The editor is divided into three parts:
- Dummy Data Preview shows a preview of what the standings would look like if applied on dummy data. The preview will update automatically as the configuration is changed.
- Statistics define the values MockScores calculates for each team.
- Tiebreaker order determines which statistics are compared first.
How rankings are calculated
Section titled “How rankings are calculated”MockScores compares teams using the statistics in your tiebreaker order.
There are 2 types of statistics that are calculated.
- Intermediate Statistics are calculated at the pairing level. This is useful for determining if a round was a win for one side.
- Aggregated Statistics are calculated across the whole tournament. These statistics could be things like the total number of points a team has scored or a team’s Competitive Strength.
Tiebreakers are only valid on Aggregated Statistics.
MockScores will look at the defined tiebreakers. It will then sort the teams based on the first tiebreaker. It will go through all the tiebreakers until there are none remaining. Teams will be sorted at the end by their team code.
For example, a tournament might rank teams by:
- Ballots won
- Total points
- Presider selections
- Point differential
In this configuration, total points are only considered when two teams have the same number of ballots won. Presider selections are only considered when both ballots won and total points are tied.
Use a template
Section titled “Use a template”Templates provide a ready-to-use configuration for common tournament rules.
- Select the template that most closely matches your tournament’s rules.
- Review the statistics and their order.
- Use the preview to confirm that the results are ranked as expected.
- Save the configuration.
You can customize a template after selecting it. This is often easier than starting from an empty configuration.
Build tiebreakers with Blockly
Section titled “Build tiebreakers with Blockly”Blockly is a block based programming tool similar to MIT’s Scratch.
MockScores allows organizers to define tiebreakers through Blockly, allowing for an easy-to-use and interactive experience.
Blockly lets you create a configuration by connecting visual blocks.
Blocks only connect where their values are compatible, which helps prevent invalid rules.
Define a statistic
Section titled “Define a statistic”Create one statistic for each value that should appear in the standings or be used to rank teams.
- Add a statistic block.
- Give the statistic a clear name, such as Ballots Won or Point Differential.
- Connect the blocks that calculate its value.
Statistics can use information recorded for each pairing, including:
- Points earned by the team
- Points earned by the opposing team
- The number of scorers assigned to the pairing
- Whether the team received the presider’s tiebreaker selection
Depending on your tournament’s rules, you can total values across rounds, calculate averages or percentages, compare values, and combine multiple calculations.
Set the tiebreaker order
Section titled “Set the tiebreaker order”After defining your statistics, add them to the tiebreaker list in priority order. The statistic at the top is compared first.
Use clear display labels because these names also help organizers and coaches understand the published standings.
Use the DSL editor
Section titled “Use the DSL editor”The DSL editor provides direct access to the same configuration created by Blockly. It uses a Lisp-like syntax, where each expression is enclosed in parentheses and begins with an operation.
Use the DSL editor when you need precise control, want to review the generated configuration, or are comfortable editing structured text.
Keep these rules in mind:
- Parentheses must remain balanced.
- Give each statistic a unique name.
- Define every statistic referenced by a column or tiebreaker.
- The order of the ranking rules determines their priority.
- Display labels should be understandable to people viewing the standings.
Changes made in either editor affect the same configuration. After editing the DSL, return to the visual view to confirm that the rules still appear as intended.
DSL grammar reference
Section titled “DSL grammar reference”This section documents the serialized DSL for advanced users. The DSL is the source of truth stored by MockScores. Blockly translates between visual blocks and this format, while standings calculations interpret the DSL directly.
Lexical rules
Section titled “Lexical rules”- Whitespace between tokens is ignored.
- Parentheses delimit S-expressions.
- Keywords, field names, aggregation names, and sort directions are bare atoms.
- Statistic names and labels should be quoted strings so they can contain spaces.
- Numbers are bare numeric atoms, such as
0,3,-1, or2.5. - String literals cannot be used as numeric expressions.
- Names produced by the serializer escape embedded quotation marks and backslashes.
Although the parser accepts a bare atom wherever it expects a name, quoted names are recommended for consistent round-tripping.
Formal grammar
Section titled “Formal grammar”The notation below uses * for zero or more repetitions, + for one or more repetitions, | for alternatives, and brackets for an optional value.
config ::= "(" "config" config-entry* ")"
config-entry ::= stat-def | team-stat-def | intermediate-def | trimmed-def | columns-def | tiebreakers-def
stat-def ::= "(" "stat" name aggregation expression ")"team-stat-def ::= "(" "team-stat" name expression ")"intermediate-def ::= "(" "intermediate" name aggregation expression ")"trimmed-def ::= "(" "trimmed" name aggregation number expression ")"
columns-def ::= "(" "columns" column-def* ")"column-def ::= "(" "column" name [name] ")"
tiebreakers-def ::= "(" "tiebreakers" [rank-method] tiebreaker-def* ")"tiebreaker-def ::= "(" "by" name order ")" | "(" "h2h" name order ")" | "(" "alpha" alpha-field order ")" | "(" "when-tied" integer integer tiebreaker-def+ ")"
aggregation ::= "sum" | "avg" | "max" | "min" | "count"order ::= "asc" | "desc"rank-method ::= "average" | "min" | "max" | "first" | "dense"alpha-field ::= "code" | "name"integer ::= non-negative-integername ::= quoted-string | bare-atom
expression ::= number | "(" "pairing" bare-atom ")" | "(" "ballot" bare-atom ")" | "(" "team" bare-atom ")" | "(" "stat" name ")" | "(" "intermediate" name ")" | "(" "opponent" name ")" | "(" binary-op expression expression ")" | "(" "if" expression expression expression ")" | "(" unary-op expression ")"
binary-op ::= "+" | "-" | "*" | "/" | "**" | "=" | "!=" | "<" | "<=" | ">" | ">=" | "and" | "or"
unary-op ::= "sqrt" | "abs" | "neg" | "ln" | "log10" | "exp" | "pow10"An empty or whitespace-only document is treated as an empty configuration. Any nonempty document must have (config ...) as its root form.
Configuration forms
Section titled “Configuration forms”| Form | Evaluation | Purpose |
|---|---|---|
(stat "Name" AGG EXPR) |
Evaluates EXPR for each pairing, then applies AGG |
Defines a regular team statistic |
(team-stat "Name" EXPR) |
Evaluates once using the team’s calculated statistics | Defines a statistic derived from team-level values |
(intermediate "Name" AGG EXPR) |
Evaluates per pairing and aggregates, but is intended for reuse rather than display | Stores a reusable intermediate calculation |
(trimmed "Name" AGG TRIM EXPR) |
Removes TRIM values from each end before aggregation |
Defines a trimmed statistic |
(columns ...) |
Does not affect calculation | Selects and labels statistics shown in standings |
(tiebreakers ...) |
Evaluated from first to last | Defines the ranking sequence |
TRIM is parsed as a number. Use a nonnegative integer so the number of values removed from each end is unambiguous.
Aggregations
Section titled “Aggregations”| Aggregation | Result |
|---|---|
sum |
Adds the per-pairing values |
avg |
Returns their arithmetic mean |
max |
Returns the greatest value |
min |
Returns the least value |
count |
Counts the evaluated entries |
A team-stat does not include an aggregation token. Internally, it is marked as team-level and stored with sum as its aggregation value, but its expression is evaluated once against the team’s statistics.
Value and reference expressions
Section titled “Value and reference expressions”| Expression | Meaning |
|---|---|
(pairing FIELD) |
Reads FIELD from the current pairing value |
(ballot FIELD) |
Alias of (pairing FIELD) in the current implementation |
(team FIELD) |
Reads a built-in value from the current team’s statistics |
(stat "Name") |
Reads a previously calculated statistic |
(intermediate "Name") |
Reads an intermediate calculation |
(opponent "Name") |
Reads a named statistic for the opposing team |
FIELD is validated by the parser. (pairing FIELD) and (ballot FIELD) must name a known per-pairing field, and (team FIELD) must name a known team-level field; an unknown field is a parse error, not a silent runtime undefined. The available fields are:
- Per-pairing fields (
(pairing ...)/(ballot ...)):ballots_won,ballots_lost,ballots_tied,points_for,points_against,won_presider_tb,ballot_pf,ballot_pa,ballot_pd,ballot_raw,num_ballots,num_scorers. - Team-level fields (
(team ...)):ballots_won,ballots_lost,ballots_tied,points_for,points_against,won_presider_tb,ballot_pf,ballot_pa,ballot_pd,ballot_raw,num_scorers,num_pairings.
(stat ...), (intermediate ...), and (opponent ...) references are also checked: the name must resolve to a declared statistic (or, for intermediate, a declared intermediate). An undefined reference is a parse error.
References create dependencies between calculations. Define foundational per-pairing statistics and intermediates before team-level formulas that consume them. Cyclic references cannot produce a valid calculation.
Operators
Section titled “Operators”Arithmetic operators use prefix notation:
(+ 4 2)(- (pairing points_for) (pairing points_against))(* (stat "Ballots Won") 2)(/ (stat "Total Points") (team num_pairings))(** (stat "Win Rate") 2)The supported arithmetic operators are +, -, *, /, and **. Each accepts exactly two operands. Use (neg EXPR) for explicit unary negation.
Comparison and logical expressions return the number 1 when true and 0 when false:
(> (pairing points_for) (pairing points_against))(and (> (stat "Ballots Won") 0) (> (stat "Point Differential") 0))(if (= (pairing won_presider_tb) 1) 1 0)The supported comparisons are =, !=, <, <=, >, and >=. Logical expressions support and and or. The conditional form is (if TEST THEN ELSE).
Math functions accept one expression:
| Form | Calculation |
|---|---|
(sqrt x) |
Square root |
(abs x) |
Absolute value |
(neg x) |
Negation |
(ln x) |
Natural logarithm |
(log10 x) |
Base-10 logarithm |
(exp x) |
Euler’s number raised to x |
(pow10 x) |
10 raised to x |
Calculations use JavaScript number semantics. Operations such as division by zero or a logarithm outside its domain can produce Infinity or NaN, which may lead to unexpected standings.
Columns
Section titled “Columns”Columns control presentation separately from calculation:
(columns (column "Ballots Won" "Ballots") (column "Total Points" "Points") (column "Point Differential" "PD"))The first name identifies the statistic. The second name is the displayed column label. If the label is omitted, the statistic name is used. A statistic can participate in ranking without being displayed, and a displayed statistic will not become a tiebreaker unless defined in the tiebreaker order.
Tiebreaker rules
Section titled “Tiebreaker rules”(tiebreakers (by "Ballots Won" desc) (h2h "Ballots Won" desc) (by "Point Differential" desc) (by "Points Allowed" asc) (alpha code asc))Each rule refines the ordering within groups of teams that are still tied after the preceding rules:
(by "Stat" desc)ranks the greater value first.(by "Stat" asc)ranks the smaller value first.(h2h "Stat" ORDER)applies a conditional head-to-head comparison using the named statistic.(alpha code ORDER)/(alpha name ORDER)orders alphabetically by team code or team name. This is useful as a final, deterministic rule.
by may only reference a defined statistic (a stat, trimmed, team-stat,
or team-level builtin). It cannot reference an intermediate, because
intermediates are per-pairing scratch values that are never aggregated onto the
team row — aggregate it into a stat first, or use h2h, which recomputes the
intermediate for the specific head-to-head pairing.
Rules are evaluated in the order written. The serializer preserves this order.
Conditional rules with when-tied
Section titled “Conditional rules with when-tied”(when-tied MIN MAX RULE...) applies its nested rules only to tie-groups whose
size is within the inclusive range [MIN, MAX]. MIN and MAX are
non-negative integers and MAX must be greater than or equal to MIN. This
lets you resolve, say, only two-way ties with head-to-head while leaving larger
groups to a different rule.
(tiebreakers (by "Ballots Won" desc) (when-tied 2 2 (h2h "Ballots Won" desc)) (by "Point Differential" desc))when-tied blocks may nest other rules, including further when-tied blocks,
and must contain at least one rule.
Final rank method
Section titled “Final rank method”(tiebreakers [METHOD] RULE...) accepts an optional leading rank-assignment
method that controls how teams still tied after every rule are given rank
numbers. It is modeled on pandas DataFrame.rank(method=...) and defaults to
first when omitted.
(tiebreakers dense (by "Ballots Won" desc) (by "Point Differential" desc))| Method | Rank assignment for a tie-group |
|---|---|
first |
Tied teams keep input order; ranks are strictly sequential (no shared ranks). This is the default. |
min |
Every tied team gets the lowest rank in its group, leaving gaps after. |
max |
Every tied team gets the highest rank in its group, leaving gaps after. |
average |
Every tied team gets the average of the group’s ranks. |
dense |
Like min, but the next group’s rank is only one higher (no gaps). |
Complete example
Section titled “Complete example”(config (stat "Ballots Won" sum (> (pairing points_for) (pairing points_against)))
(stat "Total Points" sum (pairing points_for))
(stat "Point Differential" sum (- (pairing points_for) (pairing points_against)))
(stat "Presider Selections" sum (pairing won_presider_tb))
(team-stat "Scoring Index" (/ (stat "Total Points") (+ (stat "Ballots Won") 1)))
(columns (column "Ballots Won" "Ballots") (column "Total Points" "Points") (column "Point Differential" "PD") (column "Presider Selections" "Presider"))
(tiebreakers (by "Ballots Won" desc) (by "Total Points" desc) (by "Presider Selections" desc) (by "Point Differential" desc) (alpha code asc)))The field names in this example are drawn from the per-pairing and team-level field sets documented above. An unknown field is rejected at parse time, so the preview will surface the error before you save.
Parser errors and validation boundaries
Section titled “Parser errors and validation boundaries”The parser reports errors for:
- A nonempty document without a
(config ...)root - A config entry that is not a list
- An unknown config entry or expression operator
- An operator or form with the wrong number of operands
- An invalid aggregation, sort direction, rank method, or alpha field
- An unknown per-pairing field in
(pairing ...)/(ballot ...)or team field in(team ...) - A
(stat ...),(intermediate ...), or(opponent ...)reference to a name that was never defined - A
(by ...)rule that references an intermediate rather than a stat - A duplicate statistic or intermediate name
- A
when-tiedblock whosemaxis less than itsmin, or that contains no rules - A non-integer or negative
trimmedcount orwhen-tiedbound - A quoted keyword where a bare keyword is required
- A string literal used directly as a numeric expression
- An empty expression
- A malformed column or tiebreaker entry
If parseDsl returns without throwing, the configuration is guaranteed to be
interpretable: every field, reference, operator arity, and enum token has been
checked. The remaining concerns are semantic rather than structural — the
parser does not verify that dependencies are acyclic or that a calculation
produces a finite number. Preview and test the configuration after every
advanced edit.
Round-trip behavior
Section titled “Round-trip behavior”The serializer produces a canonical (config ...) document with quoted names and labels. It always emits both (columns ...) and (tiebreakers ...), even when either list is empty.
Blockly-generated expressions and DSL expressions share the same internal representation. Converting DSL to blocks and back can normalize formatting, numeric spelling, parentheses, and some equivalent reference forms without changing the calculated value.
Preview the standings
Section titled “Preview the standings”Use the preview before saving. It lets you confirm that the current rules produce the intended order without changing published results.
Check that:
- A team with a better value in the first statistic ranks higher.
- The second statistic is used only when the first statistic is tied.
- Higher and lower values are sorted in the correct direction.
- Averages and percentages behave correctly when pairings have different numbers of scorers.
- Presider selections are counted only when a presider submitted a tiebreaker choice.
- The final column labels are clear.
Try preview data that creates ties at several levels. This makes it easier to verify each rule in the sequence.
Save and verify the configuration
Section titled “Save and verify the configuration”When the preview matches your tournament’s rules, save the configuration. Then open Standings and verify the column names and team order using the rounds you want to include.
If ballots have already been submitted, changing the configuration can change the displayed standings. Review the updated results before publishing them.
Troubleshooting
Section titled “Troubleshooting”The configuration will not save
Section titled “The configuration will not save”Check the editor for an error message. In the DSL editor, look for unmatched parentheses, missing values, duplicate statistic identifiers, or a tiebreaker that refers to an undefined statistic.
Teams are sorted in the wrong direction
Section titled “Teams are sorted in the wrong direction”Review whether the statistic should rank from highest to lowest or lowest to highest. Most totals rank higher values first, while statistics such as points allowed may rank lower values first.
A statistic displays an unexpected value
Section titled “A statistic displays an unexpected value”Confirm which rounds are selected in the standings and whether every pairing has the expected number of submitted ballots. For averages and percentages, verify that the calculation uses the correct divisor.
Teams are still tied
Section titled “Teams are still tied”Add another statistic to the end of the tiebreaker order if your tournament rules provide an additional method for resolving ties. If the rules permit an unresolved tie, no additional configuration is necessary.
The presider tiebreaker is missing
Section titled “The presider tiebreaker is missing”Confirm that a presider was assigned and that the presider submitted a team selection for the pairing. A presider-based statistic cannot count a selection that was never recorded.
Recommended setup process
Section titled “Recommended setup process”Before the tournament begins:
- Compare the configuration with the official tournament rules.
- Preview at least one tie at each level of the tiebreaker order.
- Have another organizer independently review the order and calculations.
- Save the configuration before scorers begin submitting ballots.
- Avoid changing the rules after results have been published unless the tournament rules require a correction.
This review is especially important because the tiebreaker configuration determines the order shown in published standings.