# NetHackWiki:Skill table generator
Program used to generate Template:Weapon skill table, Template:Combat skill table, and Template:Spell skill table. Original by Killian, modified for 3.6.6 by Doran.

Usage
u_init_snippet.inc should be the struct def_skill definitions in u_init.c, from Skill_A[] to Skill_W[].
This program is designed to work with NetHack 3.6.6. If a new version is released, the program might need changes, and the generated tables might need to be re-generated.
To compile the program, you need a C++ compiler, such as g++. Assuming you've saved the code as skillgen.cpp, you can compile it with

g++ -o skillgen skillgen.cpp

The perl script skillgen.pl can be used to upload the new skill tables.

skillgen.cpp
#include 

<string>
#include <iostream>
#include <cctype>
#include <vector>
#include <stdlib.h>

using std::string;
using std::cout;
using std::endl;
using std::toupper;
using std::vector;

#define STEED
#define TOURIST

typedef signed char xchar;

typedef int Skill;
typedef int Level;

#include "include/patchlevel.h"
#include "include/skills.h"
#undef P_MARTIAL_ARTS
#define P_MARTIAL_ARTS 39
#undef P_NUM_SKILLS
#define P_NUM_SKILLS 40
#undef P_LAST_H_TO_H
#define P_LAST_H_TO_H P_MARTIAL_ARTS

struct Role {
    const char *long_name;
    const char *short_name;
    const def_skill *skills;
};

const char *
skill_raw_name(const Skill &amp;s) {
    switch (s) {
        case P_DAGGER:           return "dagger";
        case P_KNIFE:            return "knife";
        case P_AXE:              return "axe";
        case P_PICK_AXE:         return "pick-axe";
        case P_SHORT_SWORD:      return "short sword";
        case P_BROAD_SWORD:      return "broadsword"; // !!!
        case P_LONG_SWORD:       return "long sword";
        case P_TWO_HANDED_SWORD: return "two-handed sword";
        case P_SCIMITAR:         return "scimitar";
        case P_SABER:            return "saber";
        case P_CLUB:             return "club";
        case P_MACE:             return "mace";
        case P_MORNING_STAR:     return "morning star";
        case P_FLAIL:            return "flail";
        case P_HAMMER:           return "hammer";
        case P_QUARTERSTAFF:     return "quarterstaff";
        case P_POLEARMS:         return "polearms";
        case P_SPEAR:            return "spear";
#ifdef P_JAVELIN
        case P_JAVELIN:          return "javelin";
#endif
        case P_TRIDENT:          return "trident";
        case P_LANCE:            return "lance";
        case P_BOW:              return "bow";
        case P_SLING:            return "sling";
        case P_CROSSBOW:         return "crossbow";
        case P_DART:             return "dart";
        case P_SHURIKEN:         return "shuriken";
        case P_BOOMERANG:        return "boomerang";
        case P_WHIP:             return "whip";
        case P_UNICORN_HORN:     return "unicorn horn";

        case P_ATTACK_SPELL:      return "attack";
        case P_HEALING_SPELL:     return "healing";
        case P_DIVINATION_SPELL:  return "divination";
        case P_ENCHANTMENT_SPELL: return "enchantment";
        case P_CLERIC_SPELL:      return "clerical";
        case P_ESCAPE_SPELL:      return "escape";
        case P_MATTER_SPELL:      return "matter";

        case P_BARE_HANDED_COMBAT: return "bare hands";
        case P_MARTIAL_ARTS:       return "martial arts";
        case P_TWO_WEAPON_COMBAT:  return "two weapon combat";
        case P_RIDING:             return "riding";

        default:                 return "<strange skill="">";
    }
}

const string
init_cap(const string &amp;c) {
    string s(c);

    string::iterator i = s.begin();

    if (i != s.end()) { *i = toupper(*i); }

    return s;
}

const string
all_caps(const string &amp;c) {
    string s(c);

    string::iterator i = s.begin();

    while (i != s.end()) { *i = toupper(*i); ++i; }

    return s;
}

const string
skill_full_name(const Skill &amp;s) {
    if (P_FIRST_WEAPON &lt;= s &amp;&amp; s &lt;= P_LAST_WEAPON) {
        //return skill_raw_name(s) + string(" skill");
        return skill_raw_name(s);
    }

    if (P_FIRST_SPELL &lt;= s &amp;&amp; s &lt;= P_LAST_SPELL) {
        return skill_raw_name(s) + string(" spells");
    }

    return string(skill_raw_name(s));
}

const char *
level_char(const Level &amp;l) {
    switch (l) {
        case P_ISRESTRICTED:   return "-";
        case P_BASIC:        return "b";
        case P_SKILLED:      return "''S''";
        case P_EXPERT:       return "'''E'''";
        case P_MASTER:       return "'''''M'''''";
        case P_GRAND_MASTER: return "'''''GM'''''";
        default:             return "???";
    }
}

const char *
level_full(const Level &amp;l) {
    switch (l) {
        case P_ISRESTRICTED:   return "''(restricted)''";
        case P_BASIC:        return "Basic";
        case P_SKILLED:      return "Skilled";
        case P_EXPERT:       return "Expert";
        case P_MASTER:       return "Master";
        case P_GRAND_MASTER: return "Grand Master";
        default:             return "???";
    }
}

const char *
level_span_class(const Level &amp;l) {
    switch (l) {
        case P_ISRESTRICTED:   return "class=\"restricted\"";
        case P_BASIC:        return "class=\"basic\"";
        case P_SKILLED:      return "class=\"skilled\"";
        case P_EXPERT:       return "class=\"expert\"";
        case P_MASTER:       return "class=\"master\"";
        case P_GRAND_MASTER: return "class=\"grandmaster\"";
        default:             return "class=\"unknown_skill\"";
    }
}

#include "u_init_snippet.inc"

const int NUM_ROLES = 13;
const Role ROLES[NUM_ROLES] = {
    { "Archeologist", "Arc", Skill_A },
    { "Barbarian",    "Bar", Skill_B },
    { "Caveman",      "Cav", Skill_C },
    { "Healer",       "Hea", Skill_H },
    { "Knight",       "Kni", Skill_K },
    { "Monk",         "Mon", Skill_Mon },
    { "Priest",       "Pri", Skill_P },
    { "Rogue",        "Rog", Skill_R },
    { "Ranger",       "Ran", Skill_Ran },
    { "Samurai",      "Sam", Skill_S },
    { "Tourist",      "Tou", Skill_T },
    { "Valkyrie",     "Val", Skill_V },
    { "Wizard",       "Wiz", Skill_W },
};

bool
role_has_skill_level(const Role &amp;r, const Skill &amp;s, const Level &amp;l) {
    for (const def_skill *sp = r.skills; sp-&gt;skill != P_NONE; ++sp) {
        if (sp-&gt;skill == s) {
            return sp-&gt;skmax == l;
        }
    }
    return l == P_ISRESTRICTED;
}

bool
is_restricted(const Role &amp;r, const Skill &amp;s) {
    return role_has_skill_level(r, s, P_ISRESTRICTED);
}

bool
role_has_level(const Role &amp;r, const Level &amp;l) {
    for (const def_skill *sp = r.skills; sp-&gt;skill != P_NONE; ++sp) {
        if (sp-&gt;skmax == l) {
            return true;
        }
    }
    return l == P_ISRESTRICTED;
}

void
print_header(void) {
    cout &lt;&lt; "! {{diagonal split header|[[Skill|Skill]]|[[Role|Role]]}}";

    for (int i = 0; i &lt; NUM_ROLES; i++) {
        const Role &amp;r = ROLES[i];

        cout &lt;&lt; " !! [[" &lt;&lt; r.long_name &lt;&lt; "|" &lt;&lt; r.short_name &lt;&lt; "]]";
    }

    cout &lt;&lt; endl;
}

void
print_role_skill_level(
    const Role &amp;r,
    const Skill &amp;s,
    const char *stringifier(const Skill &amp;)
) {
    for (const def_skill *sp = r.skills; sp-&gt;skill != P_NONE; ++sp) {
        if (sp-&gt;skill == s) {
            cout &lt;&lt; stringifier(sp-&gt;skmax);
            return;
        }
    }

    cout &lt;&lt; stringifier(P_ISRESTRICTED);
}

void
print_role_skill_level_char(const Role &amp;r, const Skill &amp;s) {
    print_role_skill_level(r, s, level_char);
}

void
print_role_skill_level_full(const Role &amp;r, const Skill &amp;s) {
    print_role_skill_level(r, s, level_full);
}

void
print_role_skill_level_span_class(const Role &amp;r, const Skill &amp;s) {
    print_role_skill_level(r, s, level_span_class);
}

void
print_warning(void) {
    cout &lt;&lt; "<!-- GENERATED PAGE; DO NOT EDIT DIRECTLY -->" &lt;&lt; endl;
}

void
print_noinclude(const char *category, const char *index) {
    cout &lt;&lt; "<noinclude>" &lt;&lt; endl;

    cout &lt;&lt;
        "This page was generated using [[NetHackWiki:Skill table generator]]. "
        "Instead of editing this page, edit that program, and re-generate "
        "this page."
        &lt;&lt; endl &lt;&lt; endl
        &lt;&lt; "{{nethack-"
        &lt;&lt; VERSION_MAJOR &lt;&lt; VERSION_MINOR &lt;&lt; PATCHLEVEL &lt;&lt; "}}" &lt;&lt; endl
        &lt;&lt; "[[Category:" &lt;&lt; category &lt;&lt; "|" &lt;&lt; index &lt;&lt; "]]" &lt;&lt; endl
        &lt;&lt; "</noinclude>" &lt;&lt; endl;
}

string
skill_link(const Skill &amp;s) {
    return string("[[") + skill_full_name(s)
        + "|" + skill_raw_name(s) + "]]";
}

void
print_skill_link(const Skill &amp;s) {
    cout &lt;&lt; skill_link(s);
}

void
print_role_skills(const Role &amp;r, const Skill &amp;first, const Skill &amp;last) {
    for (Skill s = first; s &lt;= last; ++s) {
        if (is_restricted(r, s)) { continue; }

        cout &lt;&lt; "|-" &lt;&lt; endl;
        cout &lt;&lt; "| ";

        print_skill_link(s);

        cout &lt;&lt; " || ";
        print_role_skill_level_full(r, s);
        cout &lt;&lt; endl;
    }
}

void
do_skill_table(const char *index, const Skill &amp;first, const Skill &amp;last) {
    print_warning();

    cout &lt;&lt; "{| class=\"prettytable " &lt;&lt; index &lt;&lt; "-skilltable\"" &lt;&lt; endl;

    print_header();

    for (Skill s = first; s &lt;= last; ++s) {

        if (s == (P_LAST_WEAPON/2 + 1)) {
            cout &lt;&lt; "|-" &lt;&lt; endl;
            print_header();
        }

        cout &lt;&lt; "|-" &lt;&lt; endl;
        cout &lt;&lt; "|";

        print_skill_link(s);

        for (int i = 0; i &lt; NUM_ROLES; i++) {
            const Role &amp;r = ROLES[i];

            cout &lt;&lt; "\n|";
	    print_role_skill_level_span_class(r, s);
	    cout &lt;&lt; "| ";
            print_role_skill_level_char(r, s);
        }

        cout &lt;&lt; endl;

    }

    cout &lt;&lt; "|}\n";
    print_noinclude("Skill tables", index);
}

void
do_role_skill_table(const Role &amp;r) {
    print_warning();

    cout &lt;&lt; "{| class=\"prettytable\"" &lt;&lt; endl;

    cout &lt;&lt; "! colspan=\"2\" style=\"font-size:larger\" | "
         &lt;&lt; r.long_name &lt;&lt; " skills" &lt;&lt; endl;

    cout &lt;&lt; "|-" &lt;&lt; endl;
    cout &lt;&lt; "! [[Skill|Skill]] !! Maximum level" &lt;&lt; endl;

    cout &lt;&lt; "|-" &lt;&lt; endl;
    cout &lt;&lt; "| colspan=\"2\" align=\"center\" | ''Weapon skills''" &lt;&lt; endl;

    print_role_skills(r, P_FIRST_WEAPON, P_LAST_WEAPON);

    cout &lt;&lt; "|-" &lt;&lt; endl;
    cout &lt;&lt; "| colspan=\"2\" align=\"center\" | ''Combat skills''" &lt;&lt; endl;

    print_role_skills(r, P_FIRST_H_TO_H, P_LAST_H_TO_H);

    cout &lt;&lt; "|-" &lt;&lt; endl;
    cout &lt;&lt; "| colspan=\"2\" align=\"center\" | ''Spell skills''" &lt;&lt; endl;

    print_role_skills(r, P_FIRST_SPELL, P_LAST_SPELL);

    cout &lt;&lt; "|}";

    print_noinclude("Skill tables", r.long_name);
}

void
push_matches(
    const Role &amp;r,
    const Skill &amp;first,
    const Skill &amp;last,
    const Level &amp;l,
    vector<string> &amp;v
) {
        for (Skill s = first; s &lt;= last; ++s) {
            if (role_has_skill_level(r, s, l)) {
                v.push_back(skill_link(s));
            }
        }
}

void
print_matches(const char *heading, vector<string> &amp;v) {
    if (!v.empty()) {
        cout &lt;&lt; "* ''" &lt;&lt; heading &lt;&lt; ":'' ";
        for (vector<string>::iterator i = v.begin(); i != v.end(); ++i) {
            if (i != v.begin()) {
                cout &lt;&lt; ", ";
            }

            cout &lt;&lt; *i;
        }
        cout &lt;&lt; endl;
    }
}

void
do_role_skill_table_2(const Role &amp;r) {
    print_warning();

    cout &lt;&lt; "{| class=\"prettytable\"" &lt;&lt; endl;

    cout &lt;&lt; "! colspan=\"2\" style=\"font-size:larger\" | "
         &lt;&lt; r.long_name &lt;&lt; " skills" &lt;&lt; endl;

    cout &lt;&lt; "|-" &lt;&lt; endl;
    cout &lt;&lt; "! Max !! [[Skill|Skills]]" &lt;&lt; endl;

    for (Level l = P_BASIC; l &lt;= P_GRAND_MASTER; l++) {
        if (!role_has_level(r, l)) { continue; }

        cout &lt;&lt; "|-" &lt;&lt; endl;
        cout &lt;&lt; "| " &lt;&lt; level_full(l) &lt;&lt; endl;

        cout &lt;&lt; "|" &lt;&lt; endl;

        vector<string> weapon;
        push_matches(r, P_FIRST_WEAPON, P_LAST_WEAPON, l, weapon);
        print_matches("Weapons", weapon);

        vector<string> combat;
        push_matches(r, P_FIRST_H_TO_H, P_LAST_H_TO_H, l, combat);
        print_matches("Combat", combat);

        vector<string> spell;
        push_matches(r, P_FIRST_SPELL, P_LAST_SPELL, l, spell);
        print_matches("Spells", spell);
    }

    cout &lt;&lt; "|}";

    print_noinclude("Skill tables", r.long_name);
}

void
do_skill_role_table(const Skill &amp;s) {
    print_warning();

    cout &lt;&lt; "{| class=\"prettytable\"" &lt;&lt; endl;

    cout &lt;&lt; "! colspan=\"2\" style=\"font-size:larger\" | "
         &lt;&lt; init_cap(skill_full_name(s)) &lt;&lt; endl;

    cout &lt;&lt; "|-" &lt;&lt; endl;
    cout &lt;&lt; "! [[Role|Role]] !! Maximum level" &lt;&lt; endl;

    for (int i = 0; i &lt; NUM_ROLES; ++i) {
        const Role &amp;r = ROLES[i];

        if (is_restricted(r, s)) { continue; }

        cout &lt;&lt; "|-" &lt;&lt; endl;
        cout &lt;&lt; "| [[" &lt;&lt; r.long_name &lt;&lt; "]]";
        cout &lt;&lt; " || ";
        print_role_skill_level_full(r, s);
        cout &lt;&lt; endl;
    }

    cout &lt;&lt; "|}";

    string index(init_cap(skill_full_name(s)));
    print_noinclude("Skill-specific skill tables", index.c_str());
}

bool
some_role_has_level(const Skill &amp;s, const Level &amp;l) {
    for (int i = 0; i &lt; NUM_ROLES; ++i) {
        const Role &amp;r = ROLES[i];

        for (const def_skill *sp = r.skills; sp-&gt;skill != P_NONE; ++sp) {
            if (sp-&gt;skill == s &amp;&amp; sp-&gt;skmax == l) {
                return true;
            }
        }
    }

    return l == P_ISRESTRICTED;
}

void
push_matching_roles(const Skill &amp;s, const Level &amp;l, vector<string> &amp;v) {
    for (int i = 0; i &lt; NUM_ROLES; ++i) {
        const Role &amp;r = ROLES[i];

        if (role_has_skill_level(r, s, l)) {
            v.push_back(string("[[") + r.long_name + "]]");
        }
    }
}

void
print_matching_roles(vector<string> &amp;v) {
    if (!v.empty()) {
        cout &lt;&lt; "* ";

        for (vector<string>::iterator i = v.begin(); i != v.end(); ++i) {
            if (i != v.begin()) {
                cout &lt;&lt; ", ";
            }

            cout &lt;&lt; *i;
        }
        cout &lt;&lt; endl;
    }
}

void
do_skill_role_table_2(const Skill &amp;s) {
    print_warning();

    cout &lt;&lt; "{| class=\"prettytable\"" &lt;&lt; endl;

    cout &lt;&lt; "! colspan=\"2\" style=\"font-size:larger\" | "
         &lt;&lt; init_cap(skill_full_name(s)) &lt;&lt; endl;

    cout &lt;&lt; "|-" &lt;&lt; endl;
    cout &lt;&lt; "! Max !! [[Role|Role]]" &lt;&lt; endl;

    for (Level l = P_BASIC; l &lt;= P_GRAND_MASTER; l++) {
        if (!some_role_has_level(s, l)) { continue; }

        cout &lt;&lt; "|-" &lt;&lt; endl;
        cout &lt;&lt; "| " &lt;&lt; level_full(l) &lt;&lt; endl;

        cout &lt;&lt; "|" &lt;&lt; endl;

        vector<string> roles;
        push_matching_roles(s, l, roles);
        print_matching_roles(roles);
    }

    cout &lt;&lt; "|}";

    string index(init_cap(skill_full_name(s)));
    print_noinclude("Skill-specific skill tables", index.c_str());
}

void
print_usage(const char *name) {
    cout &lt;&lt; "Usage:" &lt;&lt; endl &lt;&lt; endl;

    cout &lt;&lt; "  " &lt;&lt; name &lt;&lt; " weapon|combat|spell" &lt;&lt; endl;
    cout &lt;&lt; "        ==&gt; overall weapon/combat/spell matrix" &lt;&lt; endl &lt;&lt; endl;

    cout &lt;&lt; "  " &lt;&lt; name &lt;&lt; " Role" &lt;&lt; endl;
    cout &lt;&lt; "        ==&gt; role-specific table (type A)" &lt;&lt; endl &lt;&lt; endl;

    cout &lt;&lt; "  " &lt;&lt; name &lt;&lt; " Rol" &lt;&lt; endl;
    cout &lt;&lt; "        ==&gt; role-specific table (type B)" &lt;&lt; endl &lt;&lt; endl;

    cout &lt;&lt; "  " &lt;&lt; name &lt;&lt; " \"skill\"" &lt;&lt; endl;
    cout &lt;&lt; "        ==&gt; skill-specific table" &lt;&lt; endl;
}

int
main(int argc, const char **argv) {
    if (argc == 2) {
        const string arg = all_caps(argv[1]);

        if (arg == "WEAPON") {
            do_skill_table("Weapon", P_FIRST_WEAPON, P_LAST_WEAPON);
        } else if (arg == "COMBAT") {
            do_skill_table("Combat", P_FIRST_H_TO_H, P_LAST_H_TO_H);
        } else if (arg == "SPELL") {
            do_skill_table("Spell", P_FIRST_SPELL, P_LAST_SPELL);
        } else {
            for (int i = 0; i &lt; NUM_ROLES; ++i) {
                const Role &amp;r = ROLES[i];
                if (arg == all_caps(r.short_name)) {
                    do_role_skill_table(r);
                    exit(0);
                } else if (arg == all_caps(r.long_name)) {
                    do_role_skill_table_2(r);
                    exit(0);
                }
            }
            for (Skill s = 0; s &lt; P_NUM_SKILLS; ++s) {
                if (arg == all_caps(skill_raw_name(s))) {
                    do_skill_role_table_2(s);
                    exit(0);
                }
            }
            print_usage(argv[0]);
        }
    } else {
        print_usage(argv[0]);
    }
}

skillgen.pl
#!/usr/bin/env perl

use MediaWiki::API;

$user = shift @ARGV;
$pass = shift @ARGV;

print "Using $user $pass\n";

$mw = MediaWiki::API-&gt;new();
$mw-&gt;{config}-&gt;{api_url} = 'https://nethackwiki.com/w/api.php';

$mw-&gt;login( { lgname =&gt; $user, lgpassword =&gt; $pass } )
    || die $mw-&gt;{error}-&gt;{code} . ': ' . $mw-&gt;{error}-&gt;{details};

@list = (
      "Template:Archeologist skill table",
      "Template:Barbarian skill table",
      "Template:Caveman skill table",
      "Template:Healer skill table",
      "Template:Knight skill table",
      "Template:Monk skill table",
      "Template:Priest skill table",
      "Template:Ranger skill table",
      "Template:Rogue skill table",
      "Template:Samurai skill table",
      "Template:Tourist skill table",
      "Template:Valkyrie skill table",
      "Template:Wizard skill table",
# roles above here, others below
      "Template:Attack spells skill table",
      "Template:Axe skill table",
      "Template:Bare hands skill table",
      "Template:Boomerang skill table",
      "Template:Bow skill table",
      "Template:Broadsword skill table",
      "Template:Clerical spells skill table",
      "Template:Club skill table",
      "Template:Combat skill table",
      "Template:Crossbow skill table",
      "Template:Dagger skill table",
      "Template:Dart skill table",
      "Template:Divination spells skill table",
      "Template:Enchantment spells skill table",
      "Template:Escape spells skill table",
      "Template:Flail skill table",
      "Template:Hammer skill table",
      "Template:Healing spells skill table",
      #"Template:Javelin skill table", # skill no longer exists since 3.6.0
      "Template:Knife skill table",
      "Template:Lance skill table",
      "Template:Long sword skill table",
      "Template:Mace skill table",
      "Template:Martial arts skill table",
      "Template:Matter spells skill table",
      "Template:Morning star skill table",
      "Template:Pick-axe skill table",
      "Template:Polearms skill table",
      "Template:Quarterstaff skill table",
      "Template:Riding skill table",
      "Template:Saber skill table",
      "Template:Scimitar skill table",
      "Template:Short sword skill table",
      "Template:Shuriken skill table",
      "Template:Sling skill table",
      "Template:Spear skill table",
      "Template:Spell skill table",
      "Template:Trident skill table",
      "Template:Two weapon combat skill table",
      "Template:Two-handed sword skill table",
      "Template:Unicorn horn skill table",
      "Template:Weapon skill table",
      "Template:Whip skill table",
);

foreach $item (@list) {
   $page = $item;
   $page =~ s/ /_/g;

   $arg = $item;
   $arg =~ s/ skill table$//g;
   $arg =~ s/ spells$//g;
   $arg =~ s/^Template://g;

   print "./skillgen \"$arg\" &gt; \"$page\"\n";
   `./skillgen "$arg" &gt; "$page"`;
   $text = `cat "$page"`;

   print "getting token\n";
   $csrf =
   $mw-&gt;api( {
      action =&gt; 'query',
      meta =&gt; 'tokens'
   } )
    || die $mw-&gt;{error}-&gt;{code} . ': ' . $mw-&gt;{error}-&gt;{details};

   print "doing edit\n";
   $mw-&gt;edit( {
      action =&gt; "edit",
      title =&gt; $page,
      text =&gt; $text,
      token =&gt; $csrf
   } )
    || die $mw-&gt;{error}-&gt;{code} . ': ' . $mw-&gt;{error}-&gt;{details};

   sleep 5;
}

print "logout\n";
$mw-&gt;logout()
    || die $mw-&gt;{error}-&gt;{code} . ': ' . $mw-&gt;{error}-&gt;{details};

</string></string></string></string></string></string></string></string></string></string></strange></stdlib.h></vector></cctype></iostream></string>