The Gate-Charters of the Veshari Embassy
The Veshari Embassy stands at the crossroads of the courts of the wild. Fae envoys, fiend-princes and giant-kin all pass through its gates, and every gate obeys the Embassy's charter. The charter is a list of clauses, each granting or refusing passage. They are sometimes written for a single being, sometimes for a whole kind, and sometimes for anyone at all. The Warden-Clerks will pay a bounty for a reckoner that settles, for each being at each gate, which clause prevails.
Write gateVerdicts(lineage, clauses, queries), which returns a list of verdicts.
Arguments
lineage: a mapping from a name (string) to the name of its parent kind (string). A name that is not a key has no parent. There are no cycles. The *ancestors* of a being are its parent, its parent's parent, and so on.clauses: a list of clauses. Each clause is a 5-element array[who, gate, verdict, seal, edict]:who: a string, either a name or"*".gate: a string, either a gate name or"*".verdict:"allow"or"deny".seal: an integer.edict: a boolean.queries: a list of[being, gate]pairs of strings.
When a clause applies
A clause applies to a query only if both of these hold:
- its
whois"*", or equals the query's being, or equals one of that being's ancestors; - its
gateis"*", or equals the query's gate.
A clause naming a *descendant* of the being does not apply.
The *kinship distance* of an applicable clause is:
- 0 if
whoequals the being; - 1 if
whois its parent; - 2 if
whois its grandparent, and so on.
A who of "*" counts as farther than any ancestor.
Which clause prevails
Compare the applicable clauses by these criteria in order. The first criterion on which two clauses differ decides between them.
1. A clause with edict true beats one with edict false. 2. A smaller kinship distance beats a larger one. 3. A clause with a named gate beats one whose gate is "*". 4. A higher seal beats a lower one. 5. "deny" beats "allow".
The verdict for a query is the verdict of the prevailing clause. If no clause applies, the verdict is "deny".
Return a list of strings, each "allow" or "deny", with one entry per query, in the same order as queries.
examples
- case 1
lineage: frost-sprite's parent is sprite, sprite's parent is fae. Clauses: [fae, moon-gate, deny, seal 9, no edict] and [frost-sprite, *, allow, seal 0, no edict]. Query: frost-sprite at moon-gate.["allow"]: kinship distance 0 outranks distance 2, even though the other clause names the gate and has a higher seal. - case 2
Same lineage. Clauses: [frost-sprite, moon-gate, allow, seal 5, no edict] and [*, *, deny, seal 0, edict]. Query: frost-sprite at moon-gate.["deny"]: an edict beats every non-edict clause, however specific the non-edict clause is. - case 3
lineage: ember-imp's parent is imp. Clauses: [imp, garden-arch, allow, seal 3, no edict] and [imp, garden-arch, deny, seal 3, no edict]. Query: ember-imp at garden-arch.["deny"]: the two clauses tie on every other criterion, so deny wins. - case 4
lineage empty. Clauses: [giant, moon-gate, allow, seal 0, no edict]. Query: dwarf at moon-gate.["deny"]: no clause applies, so the verdict defaults to deny.
- reward
- 540 XP
- signature
gateVerdicts
TypeScript, run as ES2022 — types are stripped, not checked. If a JS engine would run it, so will we.
- kind specificity and wildcard fallback
- closeness beats named gate and seal; descendants never apply
- edicts override everything
- seal decides, then deny wins exact ties
- named gate beats higher seal with empty lineage
- no clauses defaults to deny
- no queries
- distant ancestor beats wildcard
- edicts ranked among themselves by closeness
- tied wildcards resolve to deny
- clause for wrong gate does not apply