From 5ecd480f98c4067fad06502574f66a15d998737b Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Wed, 13 Apr 2022 08:34:34 -0700 Subject: [PATCH] Add script to import a Nethack logfile --- scripts/import-nethack-logfile.py | 119 ++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100755 scripts/import-nethack-logfile.py diff --git a/scripts/import-nethack-logfile.py b/scripts/import-nethack-logfile.py new file mode 100755 index 0000000..90226ce --- /dev/null +++ b/scripts/import-nethack-logfile.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +# Eryn Wells + +''' +Converts a Nethack logfile (or record file) to JSON for easier parsing by my +website's templating engine. + +See https://nethackwiki.com/wiki/Logfile for information about the format of these +logfiles. +''' + +import argparse +import datetime +import json +import os.path +import sys + +DUNGEONS = { + 0: 'The Dungeons of Doom', + 1: 'Gehennom', + 2: 'The Gnomish Mines', + 3: 'The Quest', + 4: 'Sokoban', + 5: 'Fort Ludios', + 6: "Vlad's Tower", + 7: 'The Elemental Planes', +} + +# The "dungeon level" field is normally positive, but negatives indicate one of these +# levels. +SPECIAL_DUNGEON_LEVELS = { + -5: 'Astral Plane', + -4: 'Plane of Water', + -3: 'Plane of Fire', + -2: 'Plane of Air', + -1: 'Plane of Earth', +} + +RACES = { + 'Hum': 'Human', +} + +ROLES = { + 'Mon': 'Monk', + 'Rog': 'Rogue', + 'Sam': 'Samurai', +} + +GENDERS = { + 'Fem': 'Female', +} + +ALIGNMENTS = { + 'Law': 'Lawful', + 'Neu': 'Neutral', + 'Cha': 'Chaotic', +} + +def parse_args(argv, *a, **kw): + parser = argparse.ArgumentParser(*a, **kw) + parser.add_argument('logfile', help='Path to the Nethack log file to convert') + args = parser.parse_args(argv) + return args + +def main(argv): + args = parse_args(argv[1:], prog=argv[0]) + + if not os.path.isfile(args.logfile): + print('Given path is not a real file!', file=sys.stderr) + return -1 + + records = [] + with open(args.logfile) as logfile: + for record in logfile.readlines(): + fields = record.split(maxsplit=15) + + dungeon_number = int(fields[2]) + dungeon_name = DUNGEONS[dungeon_number] + + dungeon_level = int(fields[3]) + if dungeon_level > 0: + dungeon_level_descriptive = f"Level {dungeon_level}" + else: + dungeon_level_descriptive = SPECIAL_DUNGEON_LEVELS[dungeon_level] + + start_date = datetime.datetime.strptime(fields[9], '%Y%m%d').strftime('%Y-%m-%d') + end_date = datetime.datetime.strptime(fields[8], '%Y%m%d').strftime('%Y-%m-%d') + + name, reason = fields[15].split(',', maxsplit=1) + + records.append({ + 'nethack_version': fields[0], + 'score': int(fields[1]), + 'dungeon_number': int(fields[2]), + 'dungeon_name': dungeon_name, + 'level': dungeon_level, + 'level_descriptive': dungeon_level_descriptive, + 'max_level': int(fields[4]), + 'hit_points': int(fields[5]), + 'max_hit_points': int(fields[6]), + 'number_of_deaths': int(fields[7]), + 'end_date': end_date, + 'start_date': start_date, + 'user_id': int(fields[10]), + 'role': ROLES[fields[11]], + 'race': RACES[fields[12]], + 'gender': GENDERS[fields[13]], + 'alignment': ALIGNMENTS[fields[14]], + 'name': name.strip(), + 'reason_for_death': reason.strip(), + }) + + json.dump(records, sys.stdout, indent=2) + print('\n') + +if __name__ == '__main__': + import sys + result = main(sys.argv) + sys.exit(0 if not result else result)