# Range Run: how it is made

A courier on the Ruler Hills. The ground, the towns, the railway and the prizes all come out of `sitoolz`; `game.js` (about 80 lines) only paints a window of the world and moves one dot across it.

## What it imports

```html
<script src="../../../sitoolz/js/rangeblends.js"></script>   <!-- the indexed hash and the river network -->
<script src="../../../sitoolz/js/rulerland.js"></script>     <!-- the ruler laws: hills, prizes, warp codes -->
<script src="../../../sitoolz/js/terrain.js"></script>       <!-- SITerrain.window: the chunk export -->
<script src="../../../sitoolz/js/citylaw.js"></script>       <!-- CityLaw.sitesIn, plan, streetAt -->
<script src="../../../sitoolz/js/rails.js"></script>         <!-- SIRails.railway -->
```

Functions used: `SITerrain.window(world, x0, z0, w, h)` for heights, colour classes, the region's prizes (each with its `code`, a warp address from `RulerLand.warpCode`); `SITerrain.height` and `SITerrain.lane` for the railway's ground and each settlement's lane; `CityLaw.sitesIn(g0, a, b)` for every settlement of the region, `CityLaw.plan(kind, g)` for its half width and well, `CityLaw.streetAt(plan, dx, dz)` to know which cells of a city are streets; `SIRails.railway(sites, ground, m, region, water)` for the trunk, the station and the sand path up to the city.

## The twenty lines that make it

```js
const T = SITerrain, C = CityLaw, Rl = SIRails, WORLD = "ruler-hills";
const X0 = 2912, Z0 = -140, W = 240, H = 352, M = Math.floor(X0/T.REGION);
const win = T.window(WORLD, X0, Z0, W, H);                          // H[i][j] heights, C[i][j] colour classes, prizes, gate
const sites = C.sitesIn(T.G0, M*T.REGION, (M + 1)*T.REGION - 1)      // every settlement of region M, by rank
  .map(s => { const P = C.plan(s.kind, s.g); return Object.assign(s, {z: T.lane(WORLD, s.g), half: P.half, plan: P}); });
const ground = (x, z) => Math.trunc(T.height(WORLD, x, z));
const net = Rl.railway(sites, ground, M, T.REGION, win.water);        // net.cells "x,z" -> [x, z, h, tool], net.stations
const inWin = (x, z) => x >= X0 && x < X0 + W && z >= Z0 && z < Z0 + H;
const towns = sites.filter(s => inWin(s.x, s.z)), prizes = win.prizes.filter(p => inWin(p.x, p.z));
const station = net.stations.find(s => inWin(s.x, s.z)), city = towns.reduce((a, s) => !a || s.rank > a.rank ? s : a, null);
function open(x, z){                                                 // where a courier can stand
  if (!inWin(x, z) || win.C[x - X0][z - Z0] === 0) return false;     // the window's edge and water block
  const t = towns.find(s => Math.abs(x - s.x) <= s.half && Math.abs(z - s.z) <= s.half);
  return !t || !!net.cells[x + "," + z] || C.streetAt(t.plan, x - t.x, z - t.z) > 0 || Math.abs(x - t.x) === t.half || Math.abs(z - t.z) === t.half;
}
let me = {x: station.x, z: station.z - 1};                           // start on the platform
function move(dx, dz){ if (open(me.x + dx, me.z + dz)){ me.x += dx; me.z += dz; } }
// draw: one 3px square per cell tinted by win.C and shaded by the slope of win.H, the rail cells by their tool,
// a gold dot per prize, a ring on the parcel's pick-up or drop, a white (or pink, carrying) dot for the courier.
```

The delivery legs are the station to the city's well and the city's well to the hamlet's well; both wells are where `CityLaw.plan` puts them (`plan.well.ox/oz` from the settlement's centre). A tap on the map walks there by a breadth-first search over `open` cells.

## How to change the law

Every knob is in `terrain.js`'s `WORLDS` table, the same table as `sitoolz/terrain.py`. Change `WORLD` to `"ruler-alps"` and the same code runs on higher, sharper hills (spread 160, kmax 11, combine max); change it to `"dyadic-downs"` and the settlements still stand where the ruler puts them, but on the lattice. To move the courier to another part of the world, change `X0` and `Z0`: prizes come every 32 columns where the rank is five or more and the square remainder is even, settlements every 128 columns (rank 7 and up), so any window a few hundred cells wide has both. To put the railway somewhere else, change the sites you hand `railway`: the line runs twelve cells south of the southernmost big town in the list.

Example: a shorter, meaner run on the canyons.

```js
const WORLD = "ruler-canyons", X0 = 1760, Z0 = -160, W = 160, H = 260;   // the canyons are the hills inverted (mul -1)
```

## How to verify it

Everything the game stands on is a fixture the Python wrote and the JavaScript reproduces:

- `sitoolz/fixtures/terrain.json`, checked by `node tests/twins-terrain.test.cjs`: heights, colour classes and windows of every world, prizes and the gate of region 0, identical on both sides.
- `sitoolz/fixtures/city-law.json`, `node tests/twins-city.test.cjs`: the settlements of a region, their plans, streets and wells.
- `sitoolz/fixtures/places.json`, `node tests/twins-places.test.cjs`: the railway and roads of region 0 of the Ruler Hills, cell for cell.

`npm test` in `sitoolz` runs them all; `python3 tests/test_sitoolz.py` runs the Python side. This game's own smoke, `node smoke.cjs`, opens the page, picks the sack up at the station, walks it through the city to the well, delivers it, picks up the lanterns and collects a prize, then saves the screenshot below.

## What was missing

Nothing the game needed. Two things it did itself that a later `sitoolz` could carry: a `walkable(world, x, z)` beside `terrain.height` (water, buildings and streets decided in one place, so every game agrees on where a courier can stand), and a settlement's `name` (the game makes one from its kind and column).

![](shot.png)
