‹ back to the board

Thursday, September 3

Mastermulti-constraint & composite logic

The Tariff Board at Greyford Bridge

The stone toll-board at Greyford Bridge is pinned with decrees, top to bottom, oldest first. Every morning the keeper works down the queue of travellers and names a price for each one. You are given the board and the queue.

Input

rules — a list of board entries in the order they are pinned (the first is topmost). Each entry is a plain object (a dict in Python) and is one of two things:

  • A revocation: its only key is "revoke", holding a tag string. It cancels every tariff pinned above it (earlier in the list) whose tag equals that string. Tariffs with that tag pinned below it are unaffected. A revocation never prices anybody itself.
  • A tariff: keys "tag" (string), "guild" (string), "road" (string), "cargo" (string), "priority" (an integer, possibly negative) and "toll" (a non-negative integer, or the string "bar"). "guild" and "cargo" are each either a name or "*". "road" is either "*" or a path of one or more non-empty segments joined by /. Tags need not be unique.

travellers — a list of objects with keys "guild", "road" and "cargo". Each value is a non-empty string and never "*"; the road is a path of segments joined by /.

Matching. A surviving tariff matches a traveller when all three hold:

  • its guild is "*" or equals the traveller's guild;
  • its cargo is "*" or equals the traveller's cargo;
  • its road is "*", or equals the traveller's road, or the traveller's road begins with the tariff's road followed by /.

Precedence. Among the surviving tariffs that match a traveller, the winner is the one that comes out ahead on the first of these tests that distinguishes them:

1. higher "priority"; 2. greater road depth — the number of segments in the tariff's road, with "*" counting as depth 0; 3. a named guild ahead of "*"; 4. a named cargo ahead of "*"; 5. pinned lower on the board (later in rules). This settles every remaining tie.

Return a list of integers, one per traveller in queue order: the winning tariff's toll, or -1 if the winning tariff's toll is "bar", or 0 if no surviving tariff matches that traveller.

  • rule engines with overrides
  • Master

examples

  1. case 1rules = [tag "base": guild *, road *, cargo *, priority 0, toll 2], [tag "iron": guild "smiths", road "north/harbor/dock", cargo "iron", priority 0, toll 9], [tag "decree": guild *, road "north", cargo *, priority 5, toll 1]; travellers = smiths on "north/harbor/dock" with iron[1] — all three tariffs match, and priority 5 outranks the far deeper road of the iron tariff
  2. case 2the same board; travellers = tinkers on "northgate" with tin, then tinkers on "north" with tin[2, 1] — "north" does not cover "northgate" (a road matches only on segment boundaries), so the first traveller falls back to the base toll
  3. case 3rules = [tag "ban": guild *, road "north", cargo "salt", priority 3, toll "bar"], [revoke "ban"], [tag "ban": guild *, road "north/harbor", cargo "salt", priority 3, toll 7]; travellers = carters on "north/harbor" with salt, then carters on "north" with salt[7, 0] — the revocation cancels only the tariff pinned above it; the later one shares the tag but survives, and it does not reach the traveller on "north"
reward
530 XP
signature
greyfordTolls
not yet run

TypeScript, run as ES2022 — types are stripped, not checked. If a JS engine would run it, so will we.

loading the editor…
  • priority outranks a deeper road
  • road depth and segment boundaries
  • a revocation reaches only upward
  • the barred and the unlisted
  • guild before cargo, then lowest pinned
  • a bare board
  • a board stripped by revocation, and no queue
  • nothing left after the strike
The Tariff Board at Greyford Bridge · bitwick