Difference between revisions of "NetHackWiki:Skill table generator"

From NetHackWiki
Jump to navigation Jump to search
m (use <syntaxhighlight>)
Line 1: Line 1:
 
Here's the program I used to generate [[Template:Weapon skill table]], [[Template:Combat skill table]], and [[Template:Spell skill table]]. -- [[User:Killian|Killian]] 07:39, 4 November 2006 (UTC)
 
Here's the program I used to generate [[Template:Weapon skill table]], [[Template:Combat skill table]], and [[Template:Spell skill table]]. -- [[User:Killian|Killian]] 07:39, 4 November 2006 (UTC)
  
<code>modified_skills.h</code> should be a copy of [[skills.h]], modified so that <code>P_BARE_HANDS</code> and <code>P_MARTIAL_ARTS</code> have separate numbers.
+
Modified for 3.6.6 -- [[User:Doran|Doran]] ([[User talk:Doran|talk]]) 08:07, 7 August 2020 (UTC)
 +
 
 +
skillgen.cpp should be modified after #include &lt;skills.h&gt; to insert P_MARTIAL_ARTS, increment the P_NUM_SKILLS, and change the P_LAST_H_TO_H to be P_MARTIAL_ARTS.
  
 
<code>u_init_snippet.inc</code> should be the <code>struct def_skill</code> definitions in [[u_init.c]], from <code>Skill_A[]</code> to <code>Skill_W[]</code>.
 
<code>u_init_snippet.inc</code> should be the <code>struct def_skill</code> definitions in [[u_init.c]], from <code>Skill_A[]</code> to <code>Skill_W[]</code>.
  
This program is designed to work with NetHack 3.4.3. If a new version is released, the program might need changes, and the generated tables might need to be re-generated.
+
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 <tt>skillgen.cpp</tt>, you can compile it with
 
To compile the program, you need a C++ compiler, such as g++. Assuming you've saved the code as <tt>skillgen.cpp</tt>, you can compile it with
  g++ -o skillgen skillgen.c
+
  g++ -o skillgen skillgen.cpp
 +
 
 +
The perl script skillgen.pl can be used to upload the new skill tables.
  
 
{{nethack-343}}
 
{{nethack-343}}
Line 38: Line 42:
 
typedef int Level;
 
typedef int Level;
  
#include "modified_skills.h"
+
#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 {
 
struct Role {
Line 67: Line 78:
 
         case P_POLEARMS:        return "polearms";
 
         case P_POLEARMS:        return "polearms";
 
         case P_SPEAR:            return "spear";
 
         case P_SPEAR:            return "spear";
 +
#ifdef P_JAVELIN
 
         case P_JAVELIN:          return "javelin";
 
         case P_JAVELIN:          return "javelin";
 +
#endif
 
         case P_TRIDENT:          return "trident";
 
         case P_TRIDENT:          return "trident";
 
         case P_LANCE:            return "lance";
 
         case P_LANCE:            return "lance";
Line 263: Line 276:
 
         "this page."
 
         "this page."
 
         << endl << endl
 
         << endl << endl
         << "{{nethack-343}}" << endl
+
         << "{{nethack-"
 +
        << VERSION_MAJOR << VERSION_MINOR << PATCHLEVEL << "}}" << endl
 
         << "[[Category:" << category << "|" << index << "]]" << endl
 
         << "[[Category:" << category << "|" << index << "]]" << endl
 
         << "</noinclude>" << endl;
 
         << "</noinclude>" << endl;
Line 587: Line 601:
 
     }
 
     }
 
}
 
}
 +
</syntaxhighlight>
 +
 +
----
 +
 +
The code for skillgen.pl:
 +
 +
<syntaxhighlight lang="pl">
 +
#!/usr/bin/env perl
 +
 +
use MediaWiki::API;
 +
 +
$user = shift @ARGV;
 +
$pass = shift @ARGV;
 +
 +
print "Using $user $pass\n";
 +
 +
$mw = MediaWiki::API->new();
 +
$mw->{config}->{api_url} = 'https://nethackwiki.com/w/api.php';
 +
 +
$mw->login( { lgname => $user, lgpassword => $pass } )
 +
    || die $mw->{error}->{code} . ': ' . $mw->{error}->{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", # must be here
 +
      "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",
 +
      "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",
 +
);
 +
 +
$mode = 0;
 +
 +
foreach $item (@list) {
 +
  if ($item =~ /Attack spells skill table/) {
 +
      $mode = 1;
 +
  }
 +
 +
  $page = $item;
 +
  $page =~ s/ /_/g;
 +
 +
  $arg = $item;
 +
  $arg =~ s/ skill table$//g;
 +
  $arg =~ s/ spells$//g;
 +
  $arg =~ s/^Template://g;
 +
  if ($mode) {
 +
      $arg = lc($arg);
 +
  }
 +
 +
  print "./skillgen \"$arg\" > \"$page\"\n";
 +
  `./skillgen "$arg" > "$page"`;
 +
  $text = `cat "$page"`;
 +
 +
  print "getting token\n";
 +
  $csrf =
 +
  $mw->api( {
 +
      action => 'query',
 +
      meta => 'tokens'
 +
  } )
 +
    || die $mw->{error}->{code} . ': ' . $mw->{error}->{details};
 +
 +
  print "doing edit\n";
 +
  $mw->edit( {
 +
      action => "edit",
 +
      title => $page,
 +
      text => $text,
 +
      token => $csrf
 +
  } )
 +
    || die $mw->{error}->{code} . ': ' . $mw->{error}->{details};
 +
 +
}
 +
 +
print "logout\n";
 +
$mw->logout()
 +
    || die $mw->{error}->{code} . ': ' . $mw->{error}->{details};
 +
 
</syntaxhighlight>
 
</syntaxhighlight>

Revision as of 08:07, 7 August 2020

Here's the program I used to generate Template:Weapon skill table, Template:Combat skill table, and Template:Spell skill table. -- Killian 07:39, 4 November 2006 (UTC)

Modified for 3.6.6 -- Doran (talk) 08:07, 7 August 2020 (UTC)

skillgen.cpp should be modified after #include <skills.h> to insert P_MARTIAL_ARTS, increment the P_NUM_SKILLS, and change the P_LAST_H_TO_H to be P_MARTIAL_ARTS.

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.


This page may need to be updated for the current version of NetHack.

It may contain text specific to NetHack 3.4.3. Information on this page may be out of date.

Editors: After reviewing this page and making necessary edits, please change the {{nethack-343}} tag to the current version's tag or {{noversion}} as appropriate.


The code for 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 &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 &c) {
    string s(c);

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

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

    return s;
}

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

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

    return string(skill_raw_name(s));
}

const char *
level_char(const Level &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 &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 &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 &r, const Skill &s, const Level &l) {
    for (const def_skill *sp = r.skills; sp->skill != P_NONE; ++sp) {
        if (sp->skill == s) {
            return sp->skmax == l;
        }
    }
    return l == P_ISRESTRICTED;
}

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

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

void
print_header(void) {
    cout << "! Skill \\ Role";

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

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

    cout << endl;
}

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

    cout << stringifier(P_ISRESTRICTED);
}

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

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

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


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

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

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

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

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


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

        cout << "|-" << endl;
        cout << "| ";

        print_skill_link(s);

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

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

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

    print_header();

    for (Skill s = first; s <= last; ++s) {

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

        cout << "|-" << endl;
        cout << "|";

        print_skill_link(s);

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

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

        cout << endl;

    }

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

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

    cout << "{| class=\"prettytable\"" << endl;

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

    cout << "|-" << endl;
    cout << "! Skill !! Maximum level" << endl;

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

    print_role_skills(r, P_FIRST_WEAPON, P_LAST_WEAPON);

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

    print_role_skills(r, P_FIRST_H_TO_H, P_LAST_H_TO_H);

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

    print_role_skills(r, P_FIRST_SPELL, P_LAST_SPELL);

    cout << "|}";

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

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

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

            cout << *i;
        }
        cout << endl;
    }
}

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

    cout << "{| class=\"prettytable\"" << endl;

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

    cout << "|-" << endl;
    cout << "! Max !! Skills" << endl;

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

        cout << "|-" << endl;
        cout << "| " << level_full(l) << endl;

        cout << "|" << 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 << "|}";

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

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

    cout << "{| class=\"prettytable\"" << endl;

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

    cout << "|-" << endl;
    cout << "! Role !! Maximum level" << endl;

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

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

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

    cout << "|}";

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

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

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

    return l == P_ISRESTRICTED;
}

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

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

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

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

            cout << *i;
        }
        cout << endl;
    }
}

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

    cout << "{| class=\"prettytable\"" << endl;

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

    cout << "|-" << endl;
    cout << "! Max !! Role" << endl;

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

        cout << "|-" << endl;
        cout << "| " << level_full(l) << endl;

        cout << "|" << endl;

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

    cout << "|}";

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

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

    cout << "  " << name << " weapon|combat|spell" << endl;
    cout << "        ==> overall weapon/combat/spell matrix" << endl << endl;

    cout << "  " << name << " Role" << endl;
    cout << "        ==> role-specific table (type A)" << endl << endl;

    cout << "  " << name << " Rol" << endl;
    cout << "        ==> role-specific table (type B)" << endl << endl;

    cout << "  " << name << " \"skill\"" << endl;
    cout << "        ==> skill-specific table" << endl;
}

int
main(int argc, const char **argv) {
    if (argc == 2) {
        const string arg(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 < NUM_ROLES; ++i) {
                const Role &r = ROLES[i];
                if (arg == r.short_name) {
                    do_role_skill_table(r);
                    exit(0);
                } else if (arg == r.long_name) {
                    do_role_skill_table_2(r);
                    exit(0);
                }
            }
            for (Skill s = 0; s < P_NUM_SKILLS; ++s) {
                if (arg == skill_raw_name(s)) {
                    do_skill_role_table_2(s);
                    exit(0);
                }
            }
            print_usage(argv[0]);
        }
    } else {
        print_usage(argv[0]);
    }
}

The code for skillgen.pl:

#!/usr/bin/env perl

use MediaWiki::API;

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

print "Using $user $pass\n";

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

$mw->login( { lgname => $user, lgpassword => $pass } )
    || die $mw->{error}->{code} . ': ' . $mw->{error}->{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", # must be here
      "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",
      "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",
);

$mode = 0;

foreach $item (@list) {
   if ($item =~ /Attack spells skill table/) {
      $mode = 1;
   }

   $page = $item;
   $page =~ s/ /_/g;

   $arg = $item;
   $arg =~ s/ skill table$//g;
   $arg =~ s/ spells$//g;
   $arg =~ s/^Template://g;
   if ($mode) {
      $arg = lc($arg);
   }

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

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

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

}

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