User:Prowler/Monster difficulty and experience calculation

From NetHackWiki
Jump to navigation Jump to search

This is how I recalculated the table in Monster difficulty page:

First, I patched the game so that instead of starting normally it would print the raw table with experience values calculated under the assumption that monster level is equal to its base level:

patch -p1 -u <<EOF
diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c
index eca90d463..613baa905 100644
--- a/sys/unix/unixmain.c
+++ b/sys/unix/unixmain.c
@@ -65,6 +65,20 @@ main(int argc, char *argv[])
 
     early_init(argc, argv);
 
+    {
+        int i;
+        struct monst mon;
+        memset(&mon, 0, sizeof(mon));
+        gy.youmonst.data = &mons[PM_HUMAN];
+        for (i = 0; i < NUMMONS; i++) {
+            mon.data = &mons[i];
+            mon.m_lev = mons[i].mlevel;
+            printf("%s\t%d\t%d\n", mons[i].pmnames[NEUTRAL],
+                   mons[i].difficulty, experience(&mon, 0));
+        }
+        exit(0);
+    }
+
 #if defined(__APPLE__)
     {
 /* special hack to change working directory to a resource fork when
EOF

This patch was applied against tag NetHack-5.0.0_Release, commit 16ff59115315917b93185d026aeefea06db9b0f4.

I also wrote a python script for converting the output into the wiki table format. Piping the output of the patched game into it produces text that's ready for pasting into the wiki page:

#!/usr/bin/env python

import sys

EXCLUDED = (
    'long worm tail',
)

PLAYER_MONSTERS = (
    'valkyrie',
    'tourist',
    'samurai',
    'rogue',
    'knight',
    'barbarian',
    'archeologist',
    'wizard',
    'ranger',
    'cleric',
    'healer',
    'cave dweller',
    'monk',
)

MONSTER_PARENTHESIZED = (
    'Oracle',
    'sergeant',
    'human',
    'elf',
    'dwarf',
    'gnome',
    'orc',
)

def wiki_format_name(name):
    if name in ('Pestilence', 'Famine', 'Death'):
        return f'[[Riders#{name}|{name}]]'
    elif name in MONSTER_PARENTHESIZED:
        return f'[[{name.capitalize()} (monster)|{name}]]'
    elif name in PLAYER_MONSTERS:
        return f'[[{name.capitalize()} (player monster)|{name}]]'
    elif name == 'acolyte':
        return '[[Acolyte (quest guardian)|acolyte]]'
    return f'[[{name}]]'

if __name__ == '__main__':
    table = []
    for line in sys.stdin:
        name, difficulty, xp = line.strip().split('\t')
        if name not in EXCLUDED:
            table.append([name, int(difficulty), int(xp)])

    table.sort(reverse=True, key=lambda x: (x[1], x[2], x[0]))

    print('{|class="prettytable sortable striped"')
    print('!Name')
    print('!Experience')
    print('!Difficulty')
    for name, difficulty, xp in table:
        print('|-\t')
        print(f'|{wiki_format_name(name)} ||\t{xp}\t||\t{difficulty}')
    print('|}')