World space & grids

Grid and direction

grid-and-direction-2

Tests included

This contract defines the behavior of square grids: a tile map, a chess board, or a dungeon map.

This contract decides how your grid is numbered, which cells are neighbors, how distance is measured, and how a rule decides a tie between two cells.

Use in your package

The first button opens the authoring tool with this contract added and its questions unanswered. You can also download the zip file and add the contract later.

What is in the zip file

The zip file holds the contract and its acceptance tests. In the authoring tool, choose Add contract and pick this zip file. If you edit your package outside the authoring tool, unpack the zip file in your package folder. The files of the zip file go into contracts/. Fill in the answers there.

Questions

Up to 10 questions. Some appear only after earlier answers.

  1. Where is this grid's first cell, and which way do its row and column numbers grow?
  2. When you write a cell with two numbers, does the row or column come first?
  3. Do the first row and column use zero or one?
  4. A point lies exactly on the line between two cells. Which cell owns it?
  5. When your rules say nearest or within some distance, how do they measure between cells?
  6. From one cell, which other cells can a thing reach in one step?
  7. When a rule says two cells are next to each other, do corners count?
  8. In what order does an authored layout list its cells?
  9. A rule rates two cells equally and must pick one. The two cells are the candidates. How does the rule decide?
  10. When your rules turn a facing by an amount, which way does the amount go on the grid?

Try the answers

Pick answers to see which rules and tests apply. Nothing is saved here. In the zip file and in the authoring tool, the questions have no answers yet.

Where is this grid's first cell, and which way do its row and column numbers grow?

Choices for Where is this grid's first cell, and which way do its row and column numbers grow?
The first cell is the top-left corner. Column numbers grow right, and row numbers grow down.
The first cell is the bottom-left corner. Column numbers grow right, and row numbers grow up.
There is no first cell or corner. The grid continues through negative and positive numbers. Columns grow right, and rows grow down from the cell your game calls zero.
There is no first cell or corner. The grid continues through negative and positive numbers. Columns grow right, and rows grow up from the cell your game calls zero.
Why this is asked

Every later rule needs one shared reading of row and column numbers. This contract supports only columns that grow to the right. State a grid that grows to the left in your own prose.

When you write a cell with two numbers, does the row or column come first?

Choices for When you write a cell with two numbers, does the row or column come first?
The column comes first. The pair (3, 5) means column 3, row 5.
The row comes first. The pair (3, 5) means row 3, column 5.
The game never names a cell with a pair of numbers. It uses a layout, cell contents, or another name instead.
Why this is asked

Swapping the two numbers turns rows into columns. A grid with the same number of rows and columns can hide that mistake, so the order must be clear.

Do the first row and column use zero or one?

Asked when
Origin and axes is Top left or Bottom left.
Choices for Do the first row and column use zero or one?
The first row and column are numbered zero. A ten-cell row runs from zero through nine.
The first row and column are numbered one. A ten-cell row runs from one through ten.
Why this is asked

Starting at zero or one changes every written cell address. Naming the start prevents a build in which every address is one cell away from the design.

A point lies exactly on the line between two cells. Which cell owns it?

Choices for A point lies exactly on the line between two cells. Which cell owns it?
The cell with the larger number owns the line. The line where cell five ends and cell six begins belongs to cell six, on either side of zero.

Divide the position by cell-size and round down, then add one when counting-starts-at is one. A boundary position belongs to the cell with the larger coordinate.

The cell farther from zero owns the line. The zero cell reaches one cell-size to each side, so it is twice as wide as the others. The name of this answer describes the calculation: the fraction is cut off, which moves the number toward zero.

Divide the position by cell-size and truncate toward zero, then add one when counting-starts-at is one. Coordinate zero spans one cell-size on each side.

Whole numbers mark cell centers. A point halfway between two centers belongs to the cell with the larger number.

Cell coordinates name centers. Divide the position by cell-size and round to the nearest whole coordinate, taking an exact half toward the larger coordinate, then add one when counting-starts-at is one.

Your rules only use whole cells, never positions between them. No boundary choice is needed.
Why this is asked

A boundary point needs one owner. Different rounding rules can place the same point in different cells, especially beside zero.

When your rules say nearest or within some distance, how do they measure between cells?

Choices for When your rules say nearest or within some distance, how do they measure between cells?
Add the steps across and down the grid (Manhattan). A cell two across and one up is three steps away.
Count the larger of the across and down steps (Chebyshev). A cell two across and one up is two steps away.
Straight steps cost one, and diagonal steps cost the square root of two (octile). The distance is the cheapest mix of those steps. Your game's comparison rule decides when two distances count as equal.

For absolute coordinate differences dx and dy, multiply the smaller difference by the square root of two minus one (about 0.414), then add the result to the larger difference. Equal means equal at whatever precision this game compares its distances with. For straight-line and octile distance, the adoption must state that comparison rule in the distance test's verification scope.

Measure a straight line between the cell centers (Euclidean). Use position units when positions exist; otherwise use cells. Your game's comparison rule decides when two distances count as equal.

Equal means equal at whatever precision this game compares its distances with. For straight-line and octile distance, the adoption must state that comparison rule in the distance test's verification scope.

No rule measures distance between cells. Rules may still use neighbors, named shapes, or links.
Why this is asked

Games often use several distance rules. This answer only controls what your written rules mean by nearest or within a distance.

From one cell, which other cells can a thing reach in one step?

Choices for From one cell, which other cells can a thing reach in one step?
Only the four cells sharing an edge are one step away. A diagonal cell cannot be reached in one step.
All eight surrounding cells are one step away. A thing may move across an edge or diagonally across a corner.
Nothing moves one cell at a time. Things are placed directly or move freely while the grid only reads their final cell.
Why this is asked

Movement neighbors do not follow automatically from distance or effect range. A game can use different cells for each.

When a rule says two cells are next to each other, do corners count?

Choices for When a rule says two cells are next to each other, do corners count?
Only cells sharing an edge count as neighbors. Two cells touching only at a corner are not next to each other.
All eight surrounding cells count as neighbors. Cells touching only at a corner are still next to each other.
No rule uses next to by itself. Each rule names the cells it means, or no rule asks whether cells are neighbors.
Why this is asked

Movement and effects can count neighbors differently. This answer keeps words such as next to from changing meaning between rules.

In what order does an authored layout list its cells?

Choices for In what order does an authored layout list its cells?
List every cell in one row before starting the next row. Start with the smallest row and column numbers. A text map with one line per row already uses this order.
List every cell in one column before starting the next column. Start with the smallest row and column numbers.
List cells inside blocks such as chunks, regions, or screens. Your game's prose states block size, block order, and the order inside each block.
No authored layout lists the grid's cells. No order is needed for the material designers write and read.
Why this is asked

Drawing, updates, and save files may use different orders. This answer covers only layouts designers write and read.

A rule rates two cells equally and must pick one. The two cells are the candidates. How does the rule decide?

Choices for A rule rates two cells equally and must pick one. The two cells are the candidates. How does the rule decide?
The rule picks the cell with the smaller column number. Between a cell in column 2 and a cell in column 4, the rule picks the cell in column 2. If the column numbers are equal, the rule picks the cell with the smaller row number.
The rule picks the cell with the smaller row number. Between a cell in row 2 and a cell in row 4, the rule picks the cell in row 2. If the row numbers are equal, the rule picks the cell with the smaller column number.
The rule tries the grid-fixed direction words in the order that you give them. It picks the tied neighbor in the first direction that has one. Use another answer when the tied cells are not neighbors.
The rule picks the candidate that arrived first. Your game names whether arrival means creation, placement, storage, or entering play. Replaying the same run gives the same choice. After a save and a reload, your persistence rules decide. No automatic check covers this answer. A person who reviews the design checks it.
The rule picks the candidate whose kind is listed first in your game's priority order. Cell position never decides.
The rule may pick either candidate, and players must not depend on which one. Replaying the same run repeats the choice, but loading a save may not.
The rule picks one candidate at random from a random stream that your game declares. A random stream is a series of random numbers that starts from a seed. The same run and its replay pick the same candidate. No automatic check covers this answer. A person who reviews the design checks it.
The player chooses the candidate. If the game cannot ask, the rule does nothing instead of guessing. No automatic check covers this answer. A person who reviews the design checks it.
No rule ever chooses one of two equal candidates. The situation cannot happen, or the rule affects every tied candidate.
Why this is asked

Equal choices are common and rarely written down. A fixed answer stops two builds from choosing different cells.

When your rules turn a facing by an amount, which way does the amount go on the grid?

Choices for When your rules turn a facing by an amount, which way does the amount go on the grid?
A positive turn goes clockwise on the grid. A rotated view may show it differently, but the grid rule stays the same.
A positive turn goes counter-clockwise on the grid. A rotated view may show it differently, but the grid rule stays the same.
No rule turns a facing by an amount. A piece may still turn at once to face its movement or a target.
Why this is asked

Axis direction does not decide turn direction. Naming clockwise or counter-clockwise prevents equivalent formulas from reading as opposite rules.

Numbers and rules1 number, 1 rule

Numbers

Each setting takes a number, or a reference to a decided number in your tuning. It cannot reference an open number. Stay inside any range shown.

Cell size

cell-size

The length of one cell in your position units. For center-based cells, measure from one center to the next. Use a positive size when rules read positions. Use zero when they only read whole cells. Changing this number changes which cell holds a position.

Rules

  • cell-size >= 0
Lists1 list

Some settings are lists of rows.

Directions

directions

List every direction word your game uses. Each row says whether it follows the grid or a piece. A grid direction also says where it points. An empty list means the game names no directions. If the tie-break answer is direction-order, list every grid-fixed direction in which a tied neighbor can lie. No automatic check covers this. A person who reviews the design checks it.

Each row is: id, frame, points, order.

Every field
FieldKindWhen it appearsMeaning
id string Required The direction word your game uses, such as north, forward, or starboard. Write it in lowercase with hyphens between words.
frame choice: grid, piece Required Whether the direction follows the grid or a piece. A grid direction always points the same way: north stays north. A piece direction changes when that piece turns: forward, left, and behind turn with it.
points choice: up, down, left, right, up-left, up-right, down-left, down-right Present when row frame is Grid. Where this grid-fixed direction points on the grid. Omit it for a direction that follows a piece. This does not decide whether movement is allowed that way.
order integer Present when Tie break between cells is Direction order and row frame is Grid. The place of this direction in the order in which the tie-break tries the directions. The direction with number one is tried first. Give each grid-fixed direction a different number. Use this field only when the tie-break answer is direction-order.

For builders

Exact wording for builders and 22 pack tests

Exact wording for builders

The mechanism is one grid of equal square cells. A row runs across it, and a column runs down it. When a game names a cell with two whole numbers, coordinate-order says which one comes first; a layout may name cells by their contents instead. The grid may be a bounded rectangle with a first cell in one corner, or continue in every direction with signed numbers and no first cell; the first question says which.

This paragraph defines the grid's own frame. Every answer below refers to the grid as this package declares it: the layout your content writes out, or your own chapter's sentence about which way the numbers grow. Up, down, left and right mean those directions on the declared grid. On a bounded grid where content writes the grid out row by row, the first written row is nearest the declared origin corner: the top row for top-left, and the bottom row for bottom-left. These directions never follow the finished picture, which a build may draw any way its presentation chapter allows. A Delegated view can never change a Fixed answer.

Where a game also has positions, a thing can be between cells and not only on a cell. Zero is the same place for cells and for positions. Where a cell-ownership option makes the cell at zero a different width, that option says so. Two neighboring cells share a line, and cell-ownership says which cell the line belongs to. Every conversion below divides by the cell size and, where the grid counts from 1, adds 1. Each cell-ownership option that uses positions states how position zero and shared lines are treated.

This contract does not cover these related decisions: the grid's edge, two things in one cell, corner-cutting (a diagonal step past the corner of a blocked cell), pathfinding, a third axis, cells that are not squares, and whether a pass over the whole grid reads the changes that the same pass has already made.

Verification pack

sha256:6f0b75c6c274b81fc83da206fdab2ed800c8447599764b0b04634acfca585fe9

The format calls an adoption that has its matching pack Checked. The tests are included, but this does not mean that a game has passed them. An adoption without the pack is Promised. The builder must still build the chosen behavior.

22 pack tests

Placeholders are filled from the adoption's answers, numbers, rows, and test inputs.

the numbers grow one way

axes-grow

scenarioonce

Applies for every adoption

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.

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
the grid has a first cell, in one named corner

the-first-cell-is-in-one-corner

scenarioonce

Applies when Origin and axes is Top left or Bottom left.

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.

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
the grid continues in every direction

the-grid-has-no-first-cell

scenarioonce

Applies when Origin and axes is Unbounded right and down or Unbounded right and up.

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.

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
a written pair names the cell this package means

coordinate-reading

scenarioonce

Applies when Coordinate order is Column first or Row first.

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. This test checks the numbers that this package's own material writes.

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
every position belongs to exactly one cell

every-position-has-one-cell

generalonce

Applies when Cell ownership is Next cell along or Truncate toward zero or Cell centers are whole numbers.

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. This 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.

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

a position on a shared line belongs to one cell

boundary-belongs-to-one-cell

scenarioonce

Applies when Cell ownership is Next cell along or Truncate toward zero or Cell centers are whole numbers.

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. The 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. The last expected result requires one answer everywhere in the build, not one answer for each system.

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
distance between two cells is measured one way

distance-is-measured

generalonce

Applies when Distance metric is No diagonals or Diagonals count the same or Octile or Straight line.

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. A 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.

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

two distances count as equal by a declared rule

equal-distance-is-decided

scenarioonce

Applies when Distance metric is Straight line or Octile.

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: Inputs scope That 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.

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
one step reaches exactly the neighboring cells

one-step-moves

generalonce

Applies when Adjacency for movement is Four neighbors or Eight neighbors.

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.

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

next to means the same cells in every rule

next-to-means

generalonce

Applies when Adjacency for effects is Four neighbors or Eight neighbors.

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.

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

this package's own material lists every cell once

authored-order-lists-every-cell

generalonce

Applies when Enumeration order is Rows then columns or Columns then rows or Block tiled.

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. This 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.

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
the blocks this material is written in are declared

block-layout-is-declared

scenarioonce

Applies when Enumeration order is Block tiled.

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. This contract records where the block layout is stated. A reviewer reads that place.

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
a tie between two cells is decided the same way every time

tie-break-settles

scenarioonce

Applies when Tie break between cells is Lower column wins or Lower row wins or Direction order or Named priority list.

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.

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
in a tie between two cells, the rule picks the candidate that came first

tie-break-follows-arrival

scenarioonce

Applies when Tie break between cells is Arrival order.

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. This 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.

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
a tie between two cells is decided the same way in a replay

tie-break-is-arbitrary

scenarioonce

Applies when Tie break between cells is Deliberately arbitrary.

Instance promises no order between two candidates that it ranks equally: the rule may pick either one. That is a deliberate decision. This 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.

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
a tie between two cells is decided by a draw from a declared stream

tie-break-is-drawn

scenarioonce

Applies when Tie break between cells is Random from a declared seed.

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.

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
the player decides a tie between two cells

tie-break-asks-the-player

scenarioonce

Applies when Tie break between cells is Player chooses.

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.

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
no rule ever has to decide a tie between two cells

no-tie-ever-arises

generalonce

Applies when Tie break between cells is Not applicable.

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. This test looks for one error: a rule that takes the candidate that is first in some list, without a stated reason.

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

a positive turn goes one way

a-positive-turn-goes

scenarioonce

Applies when Turn direction is Clockwise or Counter clockwise.

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.

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
Row.id points Bind points phrase

grid-direction-points

scenarioper directions row

Applies when row frame is Grid.

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. The word means this direction in every rule of this package and never another direction.

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
Row.id is tried at place Row.order in the direction order

direction-walk-order

scenarioper directions row

Applies when Tie break between cells is Direction order and row frame is Grid.

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.

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
Row.id is read from the facing of the thing that uses it

piece-direction-follows-facing

scenarioper directions row

Applies when row frame is Piece.

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.

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

Use this contract in your package ↑