The Drum-Engine of Duskforge Hold
Bounty of the Delvers' Lodge of Bitwick. Beneath the abandoned dwarf-hold of Duskforge stands a brass engine that still turns. Its drum holds a column of whole numbers, and it is driven by punched tapes of glyphs. A tape that faults seizes the engine, so the Lodge will pay for a reckoner that predicts what any tape will do before it is fed.
Write runTape(tape, slots). tape is a string. slots is a non-negative integer: the most values the drum can hold at once. The drum starts empty. The function returns a string, described under Return.
### Glyphs - space: a separator. It does nothing. - digits: a maximal run of consecutive digits 0-9 is one number, read in decimal (leading zeros allowed). It is pushed onto the drum. There are no negative numbers in a tape: - is always the subtraction glyph. - + - * /: pop the top value b, then pop the next value a, and push a + b, a - b, a * b, or a / b rounded down toward negative infinity (floor division). - :: push a copy of the top value. - $: swap the top two values. - !: discard the top value. - ( ... ): a repeated section. When a ( is reached, pop the top value n. Then run the glyphs between this ( and its matching ) n times in a row, each time on the drum as it currently stands. Then continue after the ). If n is zero or negative, the section is skipped entirely. Sections may nest, and a nested ( pops its own count every time it is reached.
### Faults The whole tape is checked before anything runs. Each of these is a fault: - a character that is not one of the glyphs above; - a bracket with no partner. Brackets pair in the usual nested way.
If there are several such faults, report the one with the lowest index. Only a tape that passes this check is run.
While the tape runs, the first fault ends the run. These are the run-time faults: - Too few values. A glyph needs more values than the drum holds. + - * / $ need two values, and : ! ( need one. - Division by zero. A / whose divisor b is zero. - Full drum. A push happens while the drum already holds slots values. Only a number or : can cause this.
A glyph that is never reached, for example inside a skipped section, cannot cause a run-time fault.
### Return If a fault occurs, return "ERROR i", where i is the 0-based index in tape of the offending character. For a number, that is the index of its first digit.
Otherwise, return the values left on the drum from bottom to top. Write them as decimal integers, with a leading - on negative values, separated by single spaces. If the drum is empty, return "".
All values reached in the tests stay well within safe integer range.
examples
- case 1
tape "1 3(:1+)", slots 8"1 2 3 4". The 3 is popped as the count, and each pass copies the top value and adds one to the copy. - case 2
tape "7 10- 2/", slots 8"-2". 7 - 10 is -3, and -3 divided by 2 rounds down to -2. - case 3
tape "1 3(:)", slots 3"ERROR 4". The third copy by the ':' at index 4 would be a fourth value on a three-slot drum. - case 4
tape "+ 3 x", slots 8"ERROR 4". The stray 'x' is caught before running, so the '+' at index 0 never gets the chance to fault.
- reward
- 540 XP
- signature
runTape
TypeScript, run as ES2022 — types are stripped, not checked. If a JS engine would run it, so will we.
- plain arithmetic
- floor division with negatives
- simple repeated section
- zero and negative counts skip
- nested sections
- section building on drum
- overflow inside a section
- swap and discard
- empty tape
- only spaces
- too few values
- division by zero
- stray character beats run fault
- unmatched closing bracket
- unmatched opening bracket is the outer one
- overflow reported at first digit
- zero slots
- section count missing
- leading zeros
- skipped body cannot fault
- bad character in skipped body
- repeated squaring
- no negative literals
- nested overflow
- nested fill