{
  "contract": "grid-and-direction",
  "version": 2,
  "templates": [
    {
      "id": "axes-grow",
      "title": "the numbers grow one way",
      "type": "scenario",
      "expand": "once",
      "bindings": {
        "growth-phrase": {
          "flag": "origin-and-axes",
          "map": {
            "top-left": "right across rows and down columns",
            "bottom-left": "right across rows and up columns",
            "unbounded-right-and-down": "right across rows and down columns",
            "unbounded-right-and-up": "right across rows and up columns"
          }
        }
      },
      "test": {
        "type": "scenario",
        "given": "{{instance}} as this package lays it out, with at least two rows and at least two columns",
        "when": [
          "a cell is located in the layout",
          "the cells one step from it across and down are located"
        ],
        "then": [
          "the numbers grow {{bind:growth-phrase}}",
          "moving one cell that way raises exactly one of the two numbers by one and lowers neither",
          "moving one cell the opposite way lowers exactly one of them by one"
        ],
        "diagnostics": [
          "{{instance}}-cell-addresses",
          "{{instance}}-declared-layout"
        ]
      },
      "text": "In {{instance}} the two numbers grow {{bind:growth-phrase}}, one per cell. This test uses the layout that this package declares, not the finished picture. A build may draw the grid in any way that its presentation chapter allows, and must still number it this way."
    },
    {
      "id": "the-first-cell-is-in-one-corner",
      "title": "the grid has a first cell, in one named corner",
      "type": "scenario",
      "expand": "once",
      "when": {
        "flag": {
          "origin-and-axes": [
            "top-left",
            "bottom-left"
          ]
        }
      },
      "bindings": {
        "corner-phrase": {
          "flag": "origin-and-axes",
          "map": {
            "top-left": "the top-left corner of the grid as this package lays it out",
            "bottom-left": "the bottom-left corner of the grid as this package lays it out"
          }
        }
      },
      "test": {
        "type": "scenario",
        "given": "{{instance}} as this package lays it out",
        "when": [
          "the cell whose two numbers are both the smallest on the grid is located in the layout"
        ],
        "then": [
          "that cell is {{bind:corner-phrase}}",
          "no other corner of {{instance}} holds a smaller pair of numbers",
          "the grid is bounded: every cell of it lies inside the declared number of rows and columns"
        ],
        "diagnostics": [
          "{{instance}}-cell-addresses",
          "{{instance}}-declared-layout"
        ]
      },
      "text": "{{instance}} is a bounded rectangle whose first cell — the one whose two numbers are both smallest — is {{bind:corner-phrase}}. A build that numbers from the opposite corner fails this test even when every layout still loads."
    },
    {
      "id": "the-grid-has-no-first-cell",
      "title": "the grid continues in every direction",
      "type": "scenario",
      "expand": "once",
      "when": {
        "flag": {
          "origin-and-axes": [
            "unbounded-right-and-down",
            "unbounded-right-and-up"
          ]
        }
      },
      "bindings": {},
      "test": {
        "type": "scenario",
        "given": "{{instance}} and the cell this package puts at zero on both axes",
        "when": [
          "cells are addressed on every side of that cell, including at negative numbers on each axis",
          "a rule of this game reads or writes one of those cells"
        ],
        "then": [
          "every one of those cells is a legal address and is handled the same way as any other",
          "no rule of this game assumes a first cell, a last cell, or a corner",
          "nothing shifts the numbers to make them positive before a rule of this game reads them"
        ],
        "diagnostics": [
          "{{instance}}-cell-addresses",
          "{{instance}}-negative-coordinate-trace"
        ]
      },
      "text": "{{instance}} has no first cell. Its numbers are signed and the grid continues in every direction. A cell at a negative number is handled the same way as any other cell, and no rule may assume a corner to count from."
    },
    {
      "id": "coordinate-reading",
      "title": "a written pair names the cell this package means",
      "type": "scenario",
      "expand": "once",
      "when": {
        "flag": {
          "coordinate-order": [
            "column-first",
            "row-first"
          ]
        }
      },
      "bindings": {
        "pair-phrase": {
          "flag": "coordinate-order",
          "map": {
            "column-first": "the first number is the column, counted across the grid, and the second is the row, counted down it",
            "row-first": "the first number is the row, counted down the grid, and the second is the column, counted across it"
          }
        },
        "start-phrase": {
          "flag": "counting-starts-at",
          "map": {
            "zero": "the first row and the first column are numbered 0",
            "one": "the first row and the first column are numbered 1"
          }
        }
      },
      "test": {
        "type": "scenario",
        "given": "{{instance}} with a different number of rows and columns, so that reading a pair in the wrong order cannot name an existing cell by accident, or a stated reason why no such grid exists in this game",
        "when": [
          "the build resolves a cell that this package's own material writes as a pair of numbers"
        ],
        "then": [
          "{{bind:pair-phrase}}",
          "{{bind:start-phrase}}",
          "the cell the build resolves is the cell this package's material means",
          "the same pair with its two numbers swapped resolves to a different cell, or to no cell at all"
        ],
        "diagnostics": [
          "{{instance}}-resolved-cell",
          "{{instance}}-source-reference"
        ]
      },
      "text": "In {{instance}}, {{bind:pair-phrase}}, and {{bind:start-phrase}}. The test asks for a grid whose rows and columns differ in number. On a grid with the same number of rows and columns, a build that reads the pair in the wrong order can pass every test and still swap rows and columns.\n\nThis test checks the numbers that this package's own material writes."
    },
    {
      "id": "every-position-has-one-cell",
      "title": "every position belongs to exactly one cell",
      "type": "general",
      "expand": "once",
      "when": {
        "flag": {
          "cell-ownership": [
            "next-cell-along",
            "truncate-toward-zero",
            "cell-centers-are-whole-numbers"
          ]
        }
      },
      "bindings": {
        "conversion-phrase": {
          "flag": "cell-ownership",
          "map": {
            "next-cell-along": "dividing by {{value-cite:cell-size}} and rounding down — then adding 1 where this grid counts from 1 — the same way on both sides of zero",
            "truncate-toward-zero": "dividing by {{value-cite:cell-size}} and cutting the fraction off — then adding 1 where this grid counts from 1 — so that the cell at zero is twice as wide as the rest",
            "cell-centers-are-whole-numbers": "dividing by {{value-cite:cell-size}} and rounding to the nearest whole number — then adding 1 where this grid counts from 1 — the cell numbers naming centers"
          }
        }
      },
      "test": {
        "type": "general",
        "diagnostics": [
          "{{instance}}-position-to-cell-trace",
          "first-position-owned-twice"
        ],
        "holds": "the position belongs to exactly one cell of {{instance}}, found by {{bind:conversion-phrase}}; and converting that cell back to a position inside it and converting again returns the same cell",
        "seeds": "{{inputs:seeds}}",
        "scope": "{{inputs:scope}}"
      },
      "text": "Within this adoption's verification scope — {{inputs:scope}} — every position of {{instance}} belongs to exactly one cell, never to two and never to none, and the cell is found by {{bind:conversion-phrase}}. Converting the cell back to a position and converting again gives the same cell.\n\nThis adoption supplies the scope and the seeds through its verification inputs. The contract supplies the check and the expected result. Rounding down and cutting off the fraction give the same result for positive numbers and different results below zero. The scope should therefore include both sides of zero wherever this game has them.",
      "inputs": {
        "seeds": {
          "default": [
            "grid-position-a",
            "grid-position-b"
          ]
        },
        "scope": {
          "default": "positions of every moving thing over one run, including positions on both sides of zero"
        }
      }
    },
    {
      "id": "boundary-belongs-to-one-cell",
      "title": "a position on a shared line belongs to one cell",
      "type": "scenario",
      "expand": "once",
      "when": {
        "flag": {
          "cell-ownership": [
            "next-cell-along",
            "truncate-toward-zero",
            "cell-centers-are-whole-numbers"
          ]
        }
      },
      "bindings": {
        "positive-case-phrase": {
          "flag": "cell-ownership",
          "map": {
            "next-cell-along": "cell 6, the cell with the larger number",
            "truncate-toward-zero": "cell 6, the cell with the larger number, these numbers being positive",
            "cell-centers-are-whole-numbers": "cell 6, a position exactly halfway between two centers going to the larger number"
          }
        },
        "negative-case-phrase": {
          "flag": "cell-ownership",
          "map": {
            "next-cell-along": "the cell with the larger number, by exactly the same rule as on the other side of zero",
            "truncate-toward-zero": "the cell farther from zero, which on this side is the cell with the smaller number; this is where cutting off the fraction gives a different result from rounding down",
            "cell-centers-are-whole-numbers": "the cell with the larger number, a position exactly halfway between two centers going the same way as on the other side of zero"
          }
        }
      },
      "test": {
        "type": "scenario",
        "given": "the cells numbered 5 and 6 on one axis of {{instance}}, and any position your rules read that lies below zero on either axis",
        "when": [
          "a position is placed exactly on the line between cells 5 and 6",
          "a position below zero is placed exactly on a line between two cells",
          "a position is placed exactly where four cells meet"
        ],
        "then": [
          "the position between cells 5 and 6 is in {{bind:positive-case-phrase}}",
          "wherever a position your rules read lies below zero, the position on the line there is in {{bind:negative-case-phrase}}",
          "neither of those positions is in any other cell",
          "the position where four cells meet is in the single cell picked by applying that same rule on both axes at once",
          "every rule in the build that asks which cell any of those positions is in gets the same answer"
        ],
        "diagnostics": [
          "{{instance}}-boundary-owner-report"
        ]
      },
      "text": "In {{instance}}, a position exactly on the line between cells 5 and 6 is in {{bind:positive-case-phrase}}, and a position on a line below zero is in {{bind:negative-case-phrase}}. The test names real cell numbers because the answers to this question are easy to state in the wrong direction. The test therefore states the correct cell as a worked case. Cells 5 and 6 exist whether this grid counts from 0 or from 1. Where counting starts at 1, the conversion adds 1 and this case is unchanged.\n\nThe second expected result asks about positions below zero, not about cells at negative numbers. The difference matters: a bounded board numbered from zero has no negative cells, but where the cell numbers name centers, the outer half-cell band of that same board is at negative positions. That is where two of the three conversions give different results. Where no rule of this game ever reads a position below zero on either axis, this test checks nothing about positions below zero.\n\nThe last expected result requires one answer everywhere in the build, not one answer for each system."
    },
    {
      "id": "distance-is-measured",
      "title": "distance between two cells is measured one way",
      "type": "general",
      "expand": "once",
      "when": {
        "flag": {
          "distance-metric": [
            "no-diagonals",
            "diagonals-count-the-same",
            "octile",
            "straight-line"
          ]
        }
      },
      "bindings": {
        "metric-phrase": {
          "flag": "distance-metric",
          "map": {
            "no-diagonals": "the steps across plus the steps down, as a whole number of cells",
            "diagonals-count-the-same": "the larger of the steps across and the steps down, as a whole number of cells",
            "octile": "the larger of the two differences plus the result of multiplying the smaller difference by the square root of two minus one (about 0.414)",
            "straight-line": "the straight-line distance between the two cell centers"
          }
        },
        "worked-case-phrase": {
          "flag": "distance-metric",
          "map": {
            "no-diagonals": "a cell 10 across is at 10, and a cell 8 across and 5 down is at 13",
            "diagonals-count-the-same": "a cell 10 across is at 10, and a cell 8 across and 5 down is at 8",
            "octile": "a cell 10 across is at exactly 10, and a cell 8 across and 5 down is at 8 plus 5 times the square root of two minus one, which is greater than 10; a build that uses 1.4 in place of the square root of two makes those two distances equal, and this test fails for that build",
            "straight-line": "a cell 10 across is at 10 cell widths, and a cell 8 across and 5 down is at the square root of 89, which is less than 10"
          }
        }
      },
      "test": {
        "type": "general",
        "diagnostics": [
          "{{instance}}-distance-trace",
          "first-disagreeing-rule"
        ],
        "holds": "the distance the rule uses between the two cells is {{bind:metric-phrase}}; the distance from a cell to itself is zero; it is the same whichever cell is asked about first; every rule in {{inputs:scope}} returns the same number for the same pair; and the worked case holds — measured from one cell, {{bind:worked-case-phrase}}",
        "seeds": "{{inputs:seeds}}",
        "scope": "{{inputs:scope}}"
      },
      "text": "Where a rule of {{instance}} says nearest, in range, or within, the distance it means is {{bind:metric-phrase}}. The distance is zero from a cell to itself, it does not depend on which cell is asked about first, and every such rule returns the same number for the same pair. One worked case states the exact arithmetic, so that an approximate value fails: measured from one cell, {{bind:worked-case-phrase}}.\n\nA game may measure distance in more than one way. This test does not forbid that: a route planner's own estimate is the builder's choice and is outside {{inputs:scope}}. The test forbids two rules that give a player two different distances for the same two cells.",
      "inputs": {
        "seeds": {
          "default": [
            "grid-distance-a",
            "grid-distance-b"
          ]
        },
        "scope": {
          "default": "every rule of this game that says nearest, in range, or within, over this game's declared grid"
        }
      }
    },
    {
      "id": "equal-distance-is-decided",
      "title": "two distances count as equal by a declared rule",
      "type": "scenario",
      "expand": "once",
      "when": {
        "flag": {
          "distance-metric": [
            "straight-line",
            "octile"
          ]
        }
      },
      "bindings": {},
      "test": {
        "type": "scenario",
        "given": [
          "the rule this game declares for deciding that two distances are the same: {{inputs:scope}}",
          "two candidates whose distances from one cell are the same under that rule, and two more whose distances that rule tells apart"
        ],
        "when": [
          "a rule of this game ranks each pair by distance",
          "the same two pairs are ranked again with the two distances calculated in the opposite order"
        ],
        "then": [
          "the first pair reaches the tie-break and the second does not",
          "the rule supplied in this adoption's verification scope is the only comparison used: no other tolerance, rounding or nearly-equal test appears anywhere in the build",
          "both rankings are the same, whichever order the two distances were calculated in"
        ],
        "diagnostics": [
          "{{instance}}-distance-trace",
          "{{instance}}-tie-break-report"
        ]
      },
      "text": "{{instance}} measures distance with a rule whose results are not whole numbers of cells. The game must therefore decide when two distances count as equal, and this game declares how:\n\n{{inputs:scope}}\n\nThat rule decides which pairs reach the tie-break and which pairs are decided before it. It is the only comparison that the build may use.",
      "inputs": {
        "scope": {
          "default": "compare squared distances as whole numbers; two are equal exactly when those numbers are equal, and no tolerance is used"
        }
      }
    },
    {
      "id": "one-step-moves",
      "title": "one step reaches exactly the neighboring cells",
      "type": "general",
      "expand": "once",
      "when": {
        "flag": {
          "adjacency-for-movement": [
            "four-neighbors",
            "eight-neighbors"
          ]
        }
      },
      "bindings": {
        "movement-neighbors-phrase": {
          "flag": "adjacency-for-movement",
          "map": {
            "four-neighbors": "the four cells sharing an edge with it",
            "eight-neighbors": "the eight cells sharing an edge or a corner with it"
          }
        }
      },
      "test": {
        "type": "general",
        "diagnostics": [
          "{{instance}}-reachable-in-one-step",
          "first-unexpected-destination"
        ],
        "holds": "of the cells on the grid, the ones a thing can move to from this cell in one step, when nothing blocks the step, are exactly {{bind:movement-neighbors-phrase}} — no more and no fewer",
        "seeds": "{{inputs:seeds}}",
        "scope": "{{inputs:scope}}"
      },
      "text": "From any cell of {{instance}}, one step reaches exactly {{bind:movement-neighbors-phrase}}, when nothing blocks the step. Other parts of this package say what happens when a step would leave the grid, and whether something in the way blocks a step. This test checks only which cells a step can reach.",
      "inputs": {
        "seeds": {
          "default": [
            "grid-steps-a",
            "grid-steps-b"
          ]
        },
        "scope": {
          "default": "every cell of this game's declared grid, with each kind of moving thing placed on it in turn"
        }
      }
    },
    {
      "id": "next-to-means",
      "title": "next to means the same cells in every rule",
      "type": "general",
      "expand": "once",
      "when": {
        "flag": {
          "adjacency-for-effects": [
            "four-neighbors",
            "eight-neighbors"
          ]
        }
      },
      "bindings": {
        "effect-neighbors-phrase": {
          "flag": "adjacency-for-effects",
          "map": {
            "four-neighbors": "the four cells sharing an edge with it",
            "eight-neighbors": "the eight cells sharing an edge or a corner with it"
          }
        }
      },
      "test": {
        "type": "general",
        "diagnostics": [
          "{{instance}}-neighbor-set-per-rule",
          "first-disagreeing-rule"
        ],
        "holds": "wherever a rule of this game treats one cell as next to another, the cells it means are exactly {{bind:effect-neighbors-phrase}}, and the answer is the same in every such rule",
        "seeds": "{{inputs:seeds}}",
        "scope": "{{inputs:scope}}"
      },
      "text": "Wherever a rule of {{instance}} says one cell is next to another, it means {{bind:effect-neighbors-phrase}}, and it means the same cells in every rule that says it. This question is separate from the movement question, because a game can answer the two differently.",
      "inputs": {
        "seeds": {
          "default": [
            "grid-neighbors-a",
            "grid-neighbors-b"
          ]
        },
        "scope": {
          "default": "every rule of this game that treats two cells as next to each other, over this game's declared grid"
        }
      }
    },
    {
      "id": "authored-order-lists-every-cell",
      "title": "this package's own material lists every cell once",
      "type": "general",
      "expand": "once",
      "when": {
        "flag": {
          "enumeration-order": [
            "rows-then-columns",
            "columns-then-rows",
            "block-tiled"
          ]
        }
      },
      "bindings": {
        "order-phrase": {
          "flag": "enumeration-order",
          "map": {
            "rows-then-columns": "one whole row at a time, counting from the smallest numbers upward",
            "columns-then-rows": "one whole column at a time, counting from the smallest numbers upward",
            "block-tiled": "block by block, and cell by cell within a block, as this package's own prose describes the blocks"
          }
        }
      },
      "test": {
        "type": "general",
        "scope": "an authored layout of {{instance}}, with every cell distinguishable from every other, read from the first cell to the last as designers write it",
        "holds": "the material lists every cell of {{instance}} exactly once, {{bind:order-phrase}}, and a board that is written out and read back has the same cells in the same places",
        "diagnostics": [
          "{{instance}}-authored-order-sequence",
          "first-cell-out-of-order",
          "cells-listed-twice-or-never"
        ]
      },
      "text": "An authored layout of {{instance}} lists every cell exactly once, {{bind:order-phrase}}, and reading that layout back gives the same cells in the same places.\n\nThis does not set save-file order, drawing order, update order, or the order in which rules visit cells. Naming the authored order prevents a rule from depending on one of those other orders without stating it."
    },
    {
      "id": "block-layout-is-declared",
      "title": "the blocks this material is written in are declared",
      "type": "scenario",
      "expand": "once",
      "when": {
        "flag": {
          "enumeration-order": [
            "block-tiled"
          ]
        }
      },
      "bindings": {},
      "test": {
        "type": "scenario",
        "given": "an authored layout of {{instance}}, and the block size and the two orders as {{inputs:scope}} states them",
        "when": [
          "the material is read back cell by cell"
        ],
        "then": [
          "the blocks come in the order that is stated at that place, and the cells inside each block come in the order that is stated there",
          "every block holds the number of cells that is stated at that place, apart from any partial block that is also described there",
          "reading the same material twice gives the same sequence"
        ],
        "diagnostics": [
          "{{instance}}-authored-order-sequence",
          "{{instance}}-block-boundaries"
        ]
      },
      "text": "{{instance}} is written in blocks. A reader needs three facts before the material can be read: how big a block is, in what order the blocks come, and in what order the cells inside a block come. {{inputs:scope}} states them, and this test compares the material with what is stated there.\n\nThis contract records where the block layout is stated. A reviewer reads that place.",
      "inputs": {
        "scope": {}
      }
    },
    {
      "id": "tie-break-settles",
      "title": "a tie between two cells is decided the same way every time",
      "type": "scenario",
      "expand": "once",
      "when": {
        "flag": {
          "tie-break-between-cells": [
            "lower-column-wins",
            "lower-row-wins",
            "direction-order",
            "named-priority-list"
          ]
        }
      },
      "bindings": {
        "tie-phrase": {
          "flag": "tie-break-between-cells",
          "map": {
            "lower-column-wins": "the candidate with the smaller column number, and if those are equal the one with the smaller row number",
            "lower-row-wins": "the candidate with the smaller row number, and if those are equal the one with the smaller column number",
            "direction-order": "the candidate found first when the rule tries this game's grid-fixed direction words in their declared order, starting from the thing that chooses",
            "named-priority-list": "the candidate whose kind comes first in the order this game declares in its own prose; the cells do not decide it"
          }
        }
      },
      "test": {
        "type": "scenario",
        "given": "a rule of this game that must pick one cell, and the worked pair of equally ranked candidates described here: {{inputs:scope}}",
        "when": [
          "the rule runs",
          "the same situation is set up again, with the two candidates created in the opposite order and drawn in the opposite order"
        ],
        "then": [
          "the rule picks {{bind:tie-phrase}}",
          "it picks the same candidate both times",
          "the order the candidates were created in, the order they are drawn in, where the pointer is, and how the animation runs change nothing"
        ],
        "diagnostics": [
          "{{instance}}-tie-break-report",
          "{{instance}}-candidate-set"
        ]
      },
      "text": "When a rule of {{instance}} must pick between two candidates it ranks equally, it picks {{bind:tie-phrase}}. The adoption demonstrates that answer with this worked case: {{inputs:scope}}. The test repeats the case after reversing creation and drawing order. A choice that changed would be a different answer to this question.",
      "inputs": {
        "scope": {}
      }
    },
    {
      "id": "tie-break-follows-arrival",
      "title": "in a tie between two cells, the rule picks the candidate that came first",
      "type": "scenario",
      "expand": "once",
      "when": {
        "flag": {
          "tie-break-between-cells": [
            "arrival-order"
          ]
        }
      },
      "bindings": {},
      "test": {
        "type": "scenario",
        "given": "a rule of this game that must pick one cell, and two candidates the rule ranks equally, one of which came first by the moment {{inputs:scope}} names",
        "when": [
          "the rule runs",
          "the same run is played again from its starting state with the same inputs"
        ],
        "then": [
          "the rule picks the candidate that came first by that moment, and not by any other moment in the two candidates' histories",
          "the replayed run picks the same candidate, because it builds the two candidates in the same order",
          "the positions of the candidates on the grid do not change the choice"
        ],
        "diagnostics": [
          "{{instance}}-tie-break-report",
          "{{instance}}-arrival-order-log"
        ]
      },
      "text": "In a tie in {{instance}}, the rule picks the candidate that came first, by the moment that {{inputs:scope}} names. The adoption names one moment, and the build uses only that moment. It does not use whichever of creation, placement, or storage is convenient in the code.\n\nThis test checks the part that a test can check: a run that is replayed from its starting state makes the same choice. This game's own persistence rules decide whether the order of arrival is kept through a save and a reload. This contract records where that is stated and does not check what is stated there.",
      "inputs": {
        "scope": {}
      }
    },
    {
      "id": "tie-break-is-arbitrary",
      "title": "a tie between two cells is decided the same way in a replay",
      "type": "scenario",
      "expand": "once",
      "when": {
        "flag": {
          "tie-break-between-cells": [
            "deliberately-arbitrary"
          ]
        }
      },
      "bindings": {},
      "test": {
        "type": "scenario",
        "given": "a rule of this game that must pick one cell, and two candidates the rule ranks equally",
        "when": [
          "the rule runs",
          "the same run is played again from its starting state with the same inputs"
        ],
        "then": [
          "one of the two candidates is picked, and the run continues",
          "the replayed run picks the same candidate",
          "no rule of this game reports which candidate was picked as a promise a player or another rule may rely on"
        ],
        "diagnostics": [
          "{{instance}}-tie-break-report",
          "{{instance}}-tie-occurrence-log"
        ]
      },
      "text": "{{instance}} promises no order between two candidates that it ranks equally: the rule may pick either one. That is a deliberate decision.\n\nThis test checks only what a test can check: one run, replayed from its starting state with the same inputs, picks the same candidate. The answer itself records the main claim of the design, that nothing a player sees depends on which candidate was picked. This test cannot check that claim, because nothing can run the same moment again with the other candidate picked. Nothing more is promised: after a save and a reload, the rule may pick differently."
    },
    {
      "id": "tie-break-is-drawn",
      "title": "a tie between two cells is decided by a draw from a declared stream",
      "type": "scenario",
      "expand": "once",
      "when": {
        "flag": {
          "tie-break-between-cells": [
            "random-from-a-declared-seed"
          ]
        }
      },
      "bindings": {},
      "test": {
        "type": "scenario",
        "given": "a rule of this game that must pick one cell, two candidates the rule ranks equally, and a run started from a named seed",
        "when": [
          "the rule runs",
          "the same run is replayed from the same seed with the same inputs",
          "the same situation is reached in a run started from a different seed"
        ],
        "then": [
          "the candidate picked is drawn from the stream this game declares for it, and from no other source of randomness",
          "the replay picks the same candidate",
          "the draw consumes from that stream exactly as this game's own determinism rules say it does"
        ],
        "diagnostics": [
          "{{instance}}-tie-break-report",
          "{{instance}}-stream-consumption-log"
        ]
      },
      "text": "A tie between two candidates in {{instance}} is decided by a random draw from a stream that this game declares, so the same seed and the same inputs pick the same candidate every time. Random here means reproducible. A draw from an undeclared source would make two correct builds of this design disagree, and a declared stream prevents that."
    },
    {
      "id": "tie-break-asks-the-player",
      "title": "the player decides a tie between two cells",
      "type": "scenario",
      "expand": "once",
      "when": {
        "flag": {
          "tie-break-between-cells": [
            "player-chooses"
          ]
        }
      },
      "bindings": {},
      "test": {
        "type": "scenario",
        "given": "a rule of this game that must pick one cell, and two candidates the rule ranks equally",
        "when": [
          "the rule runs and the player can be asked",
          "the rule runs where the player cannot be asked"
        ],
        "then": [
          "in the first case the player is asked which candidate they mean, and the game picks neither candidate by itself",
          "in the second case the rule does not run and nothing about the game state changes",
          "no candidate is ever picked because of the order things were created in, the order they are drawn in, or where the pointer is"
        ],
        "diagnostics": [
          "{{instance}}-tie-break-prompt-log",
          "{{instance}}-state-before-after"
        ]
      },
      "text": "{{instance}} does not decide a tie between two equally ranked candidates by itself: the player is asked. Where a rule cannot ask — an automatic step, a replay, a resolution that no player watches — it does not guess. It does not run, and nothing changes."
    },
    {
      "id": "no-tie-ever-arises",
      "title": "no rule ever has to decide a tie between two cells",
      "type": "general",
      "expand": "once",
      "when": {
        "flag": {
          "tie-break-between-cells": [
            "not-applicable"
          ]
        }
      },
      "bindings": {},
      "test": {
        "type": "general",
        "diagnostics": [
          "{{instance}}-tie-occurrence-log",
          "first-unresolved-tie"
        ],
        "holds": "no rule of this game reaches a point where two candidates are ranked equally and one of them has to be chosen: either the situation cannot arise, or the rule affects every tied candidate alike",
        "seeds": "{{inputs:seeds}}",
        "scope": "{{inputs:scope}}"
      },
      "text": "{{instance}} answers the tie-break question with not-applicable, and this test checks that answer: across {{inputs:scope}}, no rule ever has to choose between two candidates that it ranks equally. Where a tie does occur, the rule affects both candidates in the same way and picks neither.\n\nThis test looks for one error: a rule that takes the candidate that is first in some list, without a stated reason.",
      "inputs": {
        "seeds": {
          "default": [
            "grid-tie-a",
            "grid-tie-b"
          ]
        },
        "scope": {
          "default": "every rule of this game that picks a cell or a target, over whole runs of ordinary play"
        }
      }
    },
    {
      "id": "a-positive-turn-goes",
      "title": "a positive turn goes one way",
      "type": "scenario",
      "expand": "once",
      "when": {
        "flag": {
          "turn-direction": [
            "clockwise",
            "counter-clockwise"
          ]
        }
      },
      "bindings": {
        "turn-phrase": {
          "flag": "turn-direction",
          "map": {
            "clockwise": "clockwise",
            "counter-clockwise": "counter-clockwise"
          }
        }
      },
      "test": {
        "type": "scenario",
        "given": "something standing on {{instance}} that has a facing, with that facing recorded",
        "when": [
          "it turns by a positive amount",
          "it then turns by the same amount the other way"
        ],
        "then": [
          "the first turn moves its facing {{bind:turn-phrase}} on the grid as this package lays it out",
          "the second turn brings the facing back exactly to where it started",
          "every rule of this game that turns something turns it the same way for a positive amount"
        ],
        "diagnostics": [
          "{{instance}}-facing-before-after",
          "{{instance}}-turn-log"
        ]
      },
      "text": "In {{instance}}, a positive turn moves a facing {{bind:turn-phrase}} on the declared grid, and turning back by the same amount returns the facing exactly to where it started."
    },
    {
      "id": "grid-direction-points",
      "title": "{{row.id}} points {{bind:points-phrase}}",
      "type": "scenario",
      "expand": "per-row",
      "collection": "directions",
      "when": {
        "row": {
          "frame": [
            "grid"
          ]
        }
      },
      "bindings": {
        "points-phrase": {
          "row_field": "points",
          "map": {
            "up": "toward the top of the declared grid",
            "down": "toward the bottom of the declared grid",
            "left": "toward the left of the declared grid",
            "right": "toward the right of the declared grid",
            "up-left": "diagonally toward the top-left of the declared grid",
            "up-right": "diagonally toward the top-right of the declared grid",
            "down-left": "diagonally toward the bottom-left of the declared grid",
            "down-right": "diagonally toward the bottom-right of the declared grid"
          }
        }
      },
      "test": {
        "type": "scenario",
        "given": "a cell of {{instance}} with a cell on every side of it",
        "when": [
          "a rule of this game moves something, points something, or reads a cell {{row.id}} of that cell"
        ],
        "then": [
          "the cell it uses is the one {{bind:points-phrase}}",
          "the word {{row.id}} means that same direction in every rule of this package and never another",
          "the direction does not depend on the thing that uses it"
        ],
        "diagnostics": [
          "{{instance}}-direction-resolution",
          "{{instance}}-cell-addresses"
        ]
      },
      "text": "In {{instance}}, the word {{row.id}} means the direction {{bind:points-phrase}}. It is fixed to the grid: it points the same way whoever uses it, and it never follows the facing of a thing. Naming the direction does not say whether anything may move that way. The movement question decides that. A game whose pieces move to four neighbors may still name a diagonal for an effect or a line of sight.\n\nThe word means this direction in every rule of this package and never another direction."
    },
    {
      "id": "direction-walk-order",
      "title": "{{row.id}} is tried at place {{row.order}} in the direction order",
      "type": "scenario",
      "expand": "per-row",
      "collection": "directions",
      "when": {
        "flag": {
          "tie-break-between-cells": [
            "direction-order"
          ]
        },
        "row": {
          "frame": [
            "grid"
          ]
        }
      },
      "bindings": {
        "points-phrase": {
          "row_field": "points",
          "map": {
            "up": "toward the top of the declared grid",
            "down": "toward the bottom of the declared grid",
            "left": "toward the left of the declared grid",
            "right": "toward the right of the declared grid",
            "up-left": "diagonally toward the top-left of the declared grid",
            "up-right": "diagonally toward the top-right of the declared grid",
            "down-left": "diagonally toward the bottom-left of the declared grid",
            "down-right": "diagonally toward the bottom-right of the declared grid"
          }
        }
      },
      "test": {
        "type": "scenario",
        "given": "a rule of this game that decides a tie by trying the directions in order, and a tied candidate that lies {{bind:points-phrase}} from the thing that chooses",
        "when": [
          "the rule tries the directions"
        ],
        "then": [
          "{{row.id}} is tried at place {{row.order}} in the order, after every direction with a smaller number and before every direction with a larger one",
          "the rule picks the candidate that lies in that direction exactly when no direction tried earlier has a tied candidate",
          "the rule tries the directions in this order every time, however the situation was created"
        ],
        "diagnostics": [
          "{{instance}}-tie-break-report",
          "{{instance}}-direction-walk-trace"
        ]
      },
      "text": "When {{instance}} decides a tie by trying its directions in order, {{row.id}} — the direction {{bind:points-phrase}} — is tried at place {{row.order}}. The number is on the row, so the order does not depend on the order of the rows in the list."
    },
    {
      "id": "piece-direction-follows-facing",
      "title": "{{row.id}} is read from the facing of the thing that uses it",
      "type": "scenario",
      "expand": "per-row",
      "collection": "directions",
      "when": {
        "row": {
          "frame": [
            "piece"
          ]
        }
      },
      "bindings": {},
      "test": {
        "type": "scenario",
        "given": "a thing standing on a cell of {{instance}}, with a facing and a cell on every side of it",
        "when": [
          "a rule of this game moves it, points it, or reads a cell {{row.id}} of it",
          "the thing turns, and the same rule runs again"
        ],
        "then": [
          "the direction the word {{row.id}} names is read from that thing's own facing, not from the grid",
          "after the turn, {{row.id}} names a different direction on the grid, turned by exactly as much as the facing was",
          "two things with different facings, standing on the same cell, resolve {{row.id}} differently"
        ],
        "diagnostics": [
          "{{instance}}-direction-resolution",
          "{{instance}}-facing-before-after"
        ]
      },
      "text": "In {{instance}}, the word {{row.id}} is read from the facing of the thing that uses it. It points in a different direction after that thing turns, and it can mean two different directions for two things that stand on the same cell."
    }
  ]
}
