Roguelike 1d

Problem #367

Tags: c-1 implementation games random simulation

Who solved this?

No translations... yet
quest for glory - fighting goblins

Let's get acquainted with "combat mechanics" found in many role-playing games. We briefly touched the basics of dice-throwing process in D&D dice, and now we'll learn how it is applied.

We are to play extremely simplified "one-dimensional" version of the classic Rogue game: the sequence of monsters (and some stuff) is generated - and we want to "replay" every combat using dice-throwing with predefined random generator and tell how long the hero survives. Screenshot above is from the different game ("Quest for Glory I, VGA version" by Sierra) but situation (and mechanics) is quite similar!

Use Engel's Randomizer. I.e. given initial seed every next value is generated by adding 3.1415926, raising to the 8-th power - and then taking fractional part (itself truncated to first 7 digits after the decimal point). To simulate die-throwing divide 0..1 range to necessary sections corresponding to die sides, just as in Dice Rolling task.

Combat Mechanics

When the hero and some monster are engaged in a fight, they attack each other in turns (except that some monsters have 2 or 3 attacks which happen one by one on monster's turn). Every attack is performed in two steps:

Hit is calculated by throwing a single 1d20 die and checking if the result is above the threshold. Threshold is calculated as 21 minus attacker's level and minus defender's armor class (AC). The latter is confusing: armor class is "the lower the better" (historically). Besides this attacker may have weapon with magical bonus (to-hit modifier) which adds to the value shown by the die.

Damage calculation is simpler, it is just throwing of dice defined by weapon type, adding any magical bonus (to-damage modifier) and, in case when attack is performed by hero, adding bonus due to strength (monsters seemingly has no strength, or rather no bonus due to it).

Let's assume the strength gives +1 when it is 15 or more, +2 when 18 or more, -1 when below 12 - in other words int(strength / 3) - 4.

Consider an example: Hero at the 3 level of experience (and strength 16) meets Hobgoblin with AC 5. Hero is wielding a mace with +1 to-hit and +1 to-damage. Hero attacks first, hitting threshold is

21 - attacker_level - defender_AC   =   21 - 3 - 5   =   13

Suppose the throw of 1d20 die yields 12, we add +1 to-hit modifier and so we just meet the threshold - attack hits.

Now the mace general damage is 2d4, we throw two dice, say, giving 3+2=5. To this we add +1 of to-dam bonus and +1 bonus for strength - thus Hobgoblin's life points (also called "hit points") are reduced by 7.

Suppose the monster survives and delivers response.

Hero is wearing armor improving AC by 4 from default value of 10, so resulting AC is 6. Hobgoblin itself is level 1 creature without hit bonus. So the hitting threshold is 21-1-6 = 14. If 1d20 die is cast to give 14 or higher, the damage is then calculated as 1d8 (damage type is simply defined by monster type and they have no to-damage bonus).

Monster Types

For convenience we have prepared monster list in a form which could be easily copy-pasted into the code in most scripting languages:

[
  # name, exp, $lvl, $ac, $dmg, $letter, $special
  ['Bat', 1, 1, 3, '1d2'],
  ['Snake', 3, 1, 5, '1d3'],
  ['Jackal', 2, 1, 7, '1d2'],
  ['Kobold', 1, 1, 7, '1d4'],
  ['Hobgoblin', 3, 1, 5, '1d8'],
  ['Floating Eye', 5, 1, 9, '1d1', 'E'], # can freeze player
  ['Giant Ant', 10, 2, 3, '1d6', 'A'], # can poison to reduce strength
  ['Gnome', 8, 2, 5, '1d6'],
  ['Leprechaun', 10, 3, 8, '1d1'], # can steal money
  ['Orc', 5, 2, 6, '1d6/1d6'],
  ['Zombie', 7, 3, 8, '1d8'],
  ['Nymph', 40, 3, 9, '1d2'], # can steal inventory
  ['Rust Monster', 25, 5, 2, '0d0/0d0'], # can rust (weaken) armor
  ['Quasit', 35, 3, 2, '1d2/1d2/1d4'],
  ['Centaur', 15, 4, 4, '1d6/1d6'],
  ['Yeti', 40, 4, 6, '1d6/1d6'],
  ['Troll', 55, 6, 4, '1d6/2d4'],
  ['Wraith', 55, 5, 4, '1d6'], # can reduce experience
  ['Umber Hulk', 130, 5, 2, '3d4/2d5'],
  ['Vampire', 250, 6, 1, '1d10'],
  ['Dragon', 500, 7, 3, '1d8/1d8/3d6'],
]

Every monster is denoted by the first letter of its name, except cases where optional field "letter" is present. So G is for gnome and A is for giant ant.

Ignore special abilities except for Giant Ant and Rust Monster. For them, in case of hit is successful, cast additional 1d4 die and if it shows 4 either decrement hero's strength or increment AC respectively. Do this before damage is calculated/inflicted.

Monster hit points are calculated at the beginning of the fight as (monster_level)d8 (e.g. sum of casting several 1d8 dice).

When the monster is defeated, its Exp value is added to heroe's experience. This may increase hero's level. Next level is assigned on reaching 10, 20, 40, 80, 160 points (and so on, doubling).

Hero's health is expressed by current hit-points value and is limited by maxHP, which starts at 14 and is increased by throwing 1d10 die on every level andvancement.

As a way to replenish current hit-points let us add defeated monster's exp (capped by maxHP) after every fight is completed (and possible level advance happens).

Initial hero's strength is 16. For monsters it is always 12 (so they have no damage bonus).

Hero's weapon (its damage) and armor (its AC) are defined at start of each run randomly:

weapons = [
  ['rock', '1d2'],
  ['dagger', '1d6'],
  ['mace', '2d4'],
  ['sword', '1d10'],
  ['zweihander', '3d6'],
  ['spear', '1d8'],
];
armors = [
  ['leather-armor', 8],
  ['ring-mail', 7],
  ['scale-mail', 6],
  ['chain-mail', 5],
  ['splint-mail', 4],
  ['plate-armor', 3],

Besides monsters hero may encounter some useful items, represented with the following symbols:

Few more subtle details (thanks to our colleague Clive): hero's starting level is 1, initial hit-points are at maximum, player or monster dies at HP = 0 or less (actually some other games may treat this as "unconsious" state, from which it is yet possible to rehabilitate); there are no special restrictions on AC - it may potentially go over 10 or become negative (and in some games it often does). Strength is also not "capped" at least in our implementation (it is not the same with original game as it uses non-linear dependence with damage).

Input data provide N for the number of test-cases following.
Next N lines each start with randomizer seed, weapon and armor choice and then a long sequence of monsters and items the hero is going to encounter.

Answer calculate by which monster the hero is defeated on each run and put this into the answer as a pair of values - letter denoting the monster and numeric index showing how far the hero travelled (how many symbols of the monsters/items line were passed successfully). Concatenate everything with spaces. In case when hero conquers all monsters, put & symbol in the answer (instead of the monster's name) - it means the hero found the amulet of Yendor (or some chest of gold etc).

Example

input:
5
0.258461 dagger scale-mail BSKKHS:)SKB(HASLALG)BHALE?HOLNZRHZCKL?OZLTS?YZZ:R!:LH?QBLNOGO
0.679051 rock scale-mail S)JSHJJEJKSA:(JJAA!SEZJG)N!JNL:S!JNRCKJ((NBKOA(YET?GJ:ZDTGASCWO
0.763629 mace leather-armor BS(JHHJHEBEHB(JLSA:O!SE(AZK(!J?ZLZG:EOBHOO!KEGJ)NL:!?)K)RGB?EJ?CLGVR
0.363055 mace ring-mail SJJHSJHE:A?B!KEHKKGGZLOS)BHOK!HKB?ECYE!)EHNB!ERGHUEUGTJ)Z(T(E(R
0.527506 sword scale-mail B(SJHH:JJBAKJJJHB(JOH)AOJ?JAJGBAH:EAJOT?Q)C)YAULZ??WBUJGQG(!!SLH

answer:
T 41 H 4 & 68 T 53 T 38

The debug-output of this example could be found by this link - let's try to decipher few first lines:

New Run                     -- correspondint to 1st testcase, 0.258461 dagger scale-mail BSKK
    rnd: 0.1913744          -- next random value (after 0.258461) - seemingly generating Bat's hit-points
Attack: hero(14) Bat(2)     -- values in parentheses show remaining health
    rnd: 0.1837846          -- next random value - testing whether hero hits the Bat on attack
  hit: 4 vs 17              -- (no, not hit)
Attack: Bat(2) hero(14)
    rnd: 0.96505
  hit: 20 vs 14             -- bat is more successful
    rnd: 0.0797763
  dam: 1,2=1                -- but only inflicts small damage

... a number of lines skipped till the last attack with this Bat

Attack: hero(10) Bat(2)
    rnd: 0.8982159
  hit: 18 vs 17
    rnd: 0.1906181
  dam: 1,6=3                -- Bat has 2 hit-points and so is defeated by damage 3
killed Bat
Exp: 1 (+1)                 -- hero's experience is increased
    rnd: 0.5617105          -- next monster's health is generated
Attack: hero(11) Snake(5)
    rnd: 0.4353387
  hit: 9 vs 15

... and so on
You need to login to get test data and submit solution.