The Overfull Mind of Magus Ebbrecht
Magus Ebbrecht of the Carrowdane Circle sets out at first thaw to hunt the Gloaming Hart through the Sedgewater Fens. A battlemage can hold only so many prepared spells in mind at once, and the Circle has pinned a commission to the Bitwick board: craft the mind-stone enchantment that decides which spells the Magus keeps and which are forgotten.
Implement the class Grimoire. It is constructed with one argument, capacity, a non-negative integer: the most spells that may be held at once.
Each held spell has: - a name: a non-empty string of lowercase ASCII letters, unique among held spells; - a power: a positive integer; - charges: a positive integer while the spell is held; - a bound flag, false when the spell is first stored.
A spell is used when it is successfully prepared (new or again) or successfully cast and not forgotten by that cast. *Least recently used* means the spell whose most recent use came earliest. bind, unbind, held and any call that fails do not count as a use.
Methods:
- prepare(name, power, charges) returns a boolean. 1. If name is already held: set its power to power and its charges to charges, keep its bound flag unchanged, count it as used, and return true. This never forgets another spell. 2. Otherwise, if fewer than capacity spells are held: store the new spell (unbound) and return true. 3. Otherwise, consider only the held spells that are not bound. If there are none, return false. Among them, the candidate is the one with the lowest power; ties go to the least recently used. If the candidate's power is greater than power, return false. Otherwise forget the candidate, store the new spell (unbound), and return true. When prepare returns false, nothing changes. - cast(name) returns an integer. If name is not held, return -1 and change nothing. Otherwise reduce its charges by one. If that leaves zero charges, the spell is forgotten (even if bound) and the call returns 0. Otherwise it counts as used and the call returns the remaining charges. - bind(name) returns a boolean. If name is held, set its bound flag (it may already be set) and return true; otherwise return false. - unbind(name) returns a boolean. If name is held, clear its bound flag and return true; otherwise return false. - held() returns a list of the names of all held spells, sorted in ascending alphabetical order.
examples
- case 1
capacity 3; prepare frost (power 2, 3 charges), gale (power 2, 2 charges), blaze (power 5, 1 charge), then ward (power 3, 2 charges); then held()every prepare returns true; frost and gale tie on power, frost was used earlier, so frost is forgotten; held() gives ["blaze", "gale", "ward"] - case 2
capacity 2; prepare blaze (power 5, 1 charge), storm (power 4, 2 charges), then spark (power 3, 4 charges)the third prepare returns false: the weakest unbound spell, storm, is stronger than spark, so nothing changes - case 3
capacity 1; prepare ward (power 2, 2 charges), bind ward, then cast ward three timesthe casts return 1, then 0 (ward is forgotten even though it is bound), then -1
- reward
- 540 XP
- signature
Grimoire
TypeScript, run as ES2022 — types are stripped, not checked. If a JS engine would run it, so will we.
- evicts the weakest, least recently used spell
- casting counts as a use
- a weaker spell cannot displace a stronger one, an equal one can
- bound spells are skipped when choosing whom to forget
- the last charge forgets even a bound spell
- zero capacity holds nothing
- re-preparing replaces power and charges and keeps the binding
- re-preparing counts as a use
- binding and failed calls do not count as uses