A fight's luck only runs one way
Every blow and every guard in a fight is jostled by JOSTLE: A plus a random number from RANDOM, -10 to +10, kept to 0-255. It adds with ADD A,B and takes a carry as going past 255. But a negative number is added as a byte of 246 to 255, which carries whenever the result is fine -- and the routine then sees the negative sign and returns 0. So every downward jostle turns a blow or a guard into nothing. (Below 10 the opposite happens: a sum that really goes under 0 does not carry, and comes out as 246 or more.)
Downward jostles are rare, because RANDOM halves its byte until it is no more than 20, and every byte from 21 up lands in the top half: only a byte under 10 gives a negative number, about one call in 25. So in practice a fight's "give or take ten" is plus 0 to 10, and one time in about 25 the blow or the guard is 0. A blow of 0 is wasted. A guard of 0 loses to any blow over 16 -- a kill. So anyone can kill anyone with luck: the vicious warg can kill the player, the player bare-handed can kill the dragon (read from the code; not tried), and a fight between the strong can end at any blow.
Measured in the simulator: 2000 jostles of 104 came out 104 to 114, or 0 (71 times), and never 94 to 103. Watched in the emulator, with the warg kept beside the player: its blows came out 55 to 62 and the player's guards 65 to 73 -- the warg's blow, 55 give or take ten, could never be more than 16 over 64 give or take ten. The ninth time both came out 0, and the blow was wasted; the twelfth time the guard alone came out 0, and the warg killed the player with one blow. Thorin's blow of 104 was seen come out 0 too. The pokes have a fix.
JUMP ONTO the barrel jumps into a message
The barrel's own JUMP ONTO handler, JUMP_ONTO_BARREL, refuses a jump from anywhere but directly above it. Where a way to the barrel exists but is not down, it does JP NZ,$B301 -- and $B301 is the message "you cannot jump onto the ... from here", not code. It was surely meant to be LD HL,$B301 and JP RUN_MESSAGE, as the line before it does. A quick test from the great halls beside the cellar did not reach it, because the parser will not name a barrel in another room; whether anything can reach it is not worked out.
FILL can never work
The barrel's FILL WITH, DO_FILL, fetches a fresh water and puts it in the barrel with the objects swapped, through PUT IN (DO_PUT_IN). But PUT IN begins with the last part of CAN_LIFT, which refuses a liquid -- and the water is a liquid. So filling the barrel is always refused. Tried in the simulator: at the long lake, with the barrel carried, opened and emptied, FILL BARREL WITH WATER was refused, the refusal naming the objects the other way round -- the swapped PUT IN that failed.
A wound wears the target down erratically
When a blow lands but does not kill, DO_ATTACK takes something off the target's strength and defence for how much stronger the blow was -- a margin of 1 to 16. It halves the margin with RRCA, twice, where a shift was surely meant: a rotate carries the margin's low bit round into bit 7. An even margin then takes about half of itself off the strength, as intended; an odd one takes 129 or more, which a weak target shrugs off (the result would go below zero, so nothing is written) and a strong one loses most of its strength to. The defence goes the same way a step further on. Tried in the simulator, the player attacking Thorin (strength 104, defence 120) with the sword: a margin of 8 left him 99/117, 10 left 98/120, 11 did nothing at all, and 13 took his defence to 52.
The wound messages are read one entry late
The same routine picks the wound's message from WOUNDS with twice the margin as the index. The margins run from 1 to 16, so the table is read from its second entry: its first message is never shown, and a margin of exactly 16 reads the word after the table -- the first two bytes of ONE_PLACE's code, which make an address in the ROM -- and prints the ROM's bytes as a message. Run as one in the simulator, they came out as a few garbled dictionary words.
The forest road kills whatever you do
Arriving on the forest road or in the forest (locations 2 and 3) starts timer 8 (IN_THE_FOREST). For three turns EYES_WARNING says pale eyes are watching and stings to death a player who is anywhere but those two places; on the fourth EYES_STING stings to death a player who is still there. So stepping out kills and staying kills; only walking back and forth between the two, which starts the timer again each time, keeps the player alive. Tried four ways in the simulator, with the results just described. Whether the warning's test was meant the other way round cannot be told from the code.
A LOAD can lose the hidden road
Which road this game shut is kept only in the operand of an instruction in ELROND_READS_MAP, and SAVE writes the variables, the objects, the timers and characters and the rooms -- not that. The rooms carry the shut exit. So a game saved and then loaded after a new game has begun, or after the tape has been loaded again, has its own road shut while the operand names the new game's: when Elrond reads the map he puts back the new game's road, which the loaded rooms already have, and the loaded game's stays shut. Read from the code; not tried with a tape.
BREAK at the end of a tape block restarts the machine
SAVE and LOAD use the ROM's own tape routines, which end by testing BREAK and, if it is held, report an error through the ROM -- with the game's own registers, not the ones the ROM expects. Tried in the simulator: with SPACE held from SAVE's key press on, the first block ended in the ROM's error exit, and a few seconds later the Spectrum was restarting. Pressing SPACE for "press any key" and holding it is enough.
A new game does not reset the characters' scripts
NEW_GAME copies back the objects, the rooms, the timers, the character slots and the variables, but not the scripts themselves, where the game keeps two things it changes: Bard's order, which BARD_TAKES_ORDER writes into a step of his script, and Thorin's one-time remark about the small curious key, which SCRIPT_DO zeroes once made. Both carry over into the next game until the tape is loaded again. Seen in the simulator for Thorin: after QUIT and a new game, the key was back in the goblins' cache and the step still zero.
The score cannot reach 100%
SHOW_SCORE prints the score at SCORE as a percentage with one decimal place, from tenths of a per cent -- so a full game would be 1000. It prints two digits, a point and a third: the tens of per cent, left out when 0, the units and the tenths. There is no hundreds digit. DIGIT counts how many times the place value goes in and adds the count to the character 0, so a score of 1000 would give ten tens and print the character after 9, a colon: watched in the emulator with the score set to 1000, SCORE said ":0.0%". But the only thing that ever adds to the score is MOVE (MOVE), the first time the player reaches a place listed in VISIT_SCORES, and those fourteen places are worth 750 between them: the lower halls 20%, the long lake 10%, the goblins' dungeon 7.5%, the trolls' cave, the dark stuffy passage, the dark dungeon and the smooth straight passage 5% each, and the lonelands, the narrow place, Beorn's house, the smothering forest, the levelled elvish clearing, Dale valley and the side door 2.5% each. Nothing else writes to the score -- no action, no character's script, no treasure -- and winning (CHECK_WON) adds nothing and does not even show it. So the best any game can do is 75.0% -- and the routine could not show a full score even if one were reached. Where the missing quarter was meant to come from -- the treasure, perhaps, or the dragon -- the code does not say.
Code nothing reaches
Five sentence shapes -- PUT ON, TAKE FROM, THROW, CUT and CLIMB -- have patterns in ACTION_PATTERNS but no handler in ACTION_TABLE or in any object's record, so they can never be done (tried: "i cannot do that."), and the PUT ON case in DO_PUT_IN can never be reached. Six stretches of the game read as code but nothing calls them, jumps to them or names them in a table the game uses: an OPEN for the wood elf alone (UNREACHED_ELF_OPEN), a routine that would wipe an exit (UNREACHED_WIPE_EXIT), one that finds what an object holds (UNREACHED_WALK_HELD), and three fragments (UNREACHED_COPY_SIX, UNREACHED_LET_GO, UNREACHED_SAY_BROKEN). A seventh was on this list, special word slot 0's handler, until it turned out to be the quote mark's: SPECIAL_QUOTE. They are left as data here, with what they would do said beside them.