NetHack/nh-timeline.py
< NetHack
The MediaWiki timeline facility seems to be broken. This program will update the file nh-timeline.svg in the meantime.
After running this, upload nh-timeline.svg as File:nh-timeline.svg and imagemap.txt as Template:Timeline of NetHack.
#!/usr/bin/env python
import datetime
# Raw data for the timeline
# datetime.date takes parameters as year, month, day
# The second element is text that appears in the image
# The third element is a vertical adjustment to keep the texts from overlapping
# Positive adjustments go down
# The elements do not need to be in any particular order
#
#Sources:
#https://nethack.org/download/
#https://groups.google.com/forum/#!searchin/net.sources/Hack%7Csort:relevance
#https://groups.google.com/forum/#!searchin/net.sources.games/Hack%7Csort:relevance
#https://groups.google.com/forum/#!searchin/comp.sources.games/NetHack%7Csort:relevance
#https://groups.google.com/forum/#!searchin/comp.sources.games.bugs/NetHack%7Csort:relevance
##http://web.archive.org/web/20120211120040/www.spod-central.org/~psmith/nh/anhftime.html (www.google.com → groups.google.com)
##http://www.juiblex.co.uk/nethack/source.html
date_list = [
( datetime.date(1984, 1, 17), 'HACK', 0 ),
( datetime.date(1984, 9, 22), 'Hack 1.21', +11 ), #placeholder date
( datetime.date(1984, 12, 12), 'Hack 1.0', +6 ),
( datetime.date(1985, 1, 21), 'Hack 1.0.1', -3 ),
( datetime.date(1985, 2, 10), 'Hack for PDP-11', -14 ),
( datetime.date(1985, 4, 1), 'Hack 1.0.2', -22 ),
( datetime.date(1985, 5, 28), 'PC/IX Hack', -29 ),
( datetime.date(1985, 7, 23), 'Hack 1.0.3', -37 ),
( datetime.date(1987, 7, 28), 'NetHack 1.3d', +14 ),
( datetime.date(1987, 8, 14), 'NetHack 1.4f', +2 ),
( datetime.date(1987, 12, 3), 'NetHack 2.2a', 0 ),
( datetime.date(1988, 4, 9), 'NetHack 2.3e', 0 ),
( datetime.date(1989, 7, 23), 'NetHack 3.0.0', +32-7 ),
( datetime.date(1989, 8, 3), 'NetHack 3.0.1', +20-7 ),
( datetime.date(1989, 8, 18), 'NetHack 3.0.2', +9-7 ),
( datetime.date(1989, 9, 7), 'NetHack 3.0.3', -2-7 ),
( datetime.date(1989, 9, 29), 'NetHack 3.0.4', -13-7 ),
( datetime.date(1989, 10, 17), 'NetHack 3.0.5', -24-7 ),
( datetime.date(1989, 11, 22), 'NetHack 3.0.6', -34-7 ),
( datetime.date(1990, 2, 23), 'NetHack 3.0.7', -38-7 ),
( datetime.date(1990, 6, 5), 'NetHack 3.0.8', -41-7 ),
( datetime.date(1990, 6, 28), 'NetHack 3.0.9', -51-7 ),
( datetime.date(1991, 2, 8), 'NetHack 3.0.10', -42-7 ),
( datetime.date(1993, 1, 28), 'NetHack 3.1.0', 0 ),
( datetime.date(1993, 3, 5), 'NetHack 3.1.1', -10 ),
( datetime.date(1993, 6, 10), 'NetHack 3.1.2', -13 ),
( datetime.date(1993, 7, 20), 'NetHack 3.1.3', -22 ),
( datetime.date(1996, 4, 18), 'NetHack 3.2.0', +4 ),
( datetime.date(1996, 6, 16), 'NetHack 3.2.1', -3 ),
( datetime.date(1996, 12, 12), 'NetHack 3.2.2', 0 ),
( datetime.date(1999, 12, 12), 'NetHack 3.2.3', +7 ),
( datetime.date(1999, 12, 12), 'NetHack 3.3.0', -6 ),
( datetime.date(2000, 8, 10), 'NetHack 3.3.1', 0 ),
( datetime.date(2002, 3, 21), 'NetHack 3.4.0', 0 ),
( datetime.date(2003, 2, 24), 'NetHack 3.4.1', 0 ),
( datetime.date(2003, 8, 31), 'NetHack 3.4.2', 0 ),
( datetime.date(2003, 12, 9), 'NetHack 3.4.3', -3 ),
( datetime.date(2015, 12, 7), 'NetHack 3.6.0', 0 ),
( datetime.date(2018, 4, 27), 'NetHack 3.6.1', 0 ),
( datetime.date(2019, 5, 9), 'NetHack 3.6.2', 0 ),
( datetime.date(2019, 12, 5), 'NetHack 3.6.3', +7 ),
( datetime.date(2019, 12, 18), 'NetHack 3.6.4', -4 ),
( datetime.date(2020, 1, 27), 'NetHack 3.6.5', -13 ),
( datetime.date(2020, 3, 8), 'NetHack 3.6.6', -22 ),
( datetime.date(2023, 2, 16), 'NetHack 3.6.7', 0 ),
( datetime.date(2026, 5, 2), 'NetHack 5.0.0', 0 ),
]
# Get a range of years to determine the vertical scale
year_min = 9999
year_max = 0
for date, text, y_adj in date_list:
year = date.year
if year_min > year:
year_min = year
if year_max < year:
year_max = year
year_max += 1
# Constants for placement of text and graphics
month_step = 3
year_step = month_step*12
top_margin = 24
width = 210
rect_left = 50
rect_width = 26
text_spacing = 14
# Generate the document
rect_height = (year_max-year_min)*year_step
all_height = rect_height + top_margin*2
text_left = rect_left + rect_width + text_spacing
imagemap = []
with open('nh-timeline.svg', 'w') as fp:
fp.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n')
fp.write('<!--To change this, please edit NetHack/nh-timeline.py-->\n')
fp.write('<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n')
fp.write('<svg width="%d" height="%d" viewBox="0 0 %d %d" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">\n'
% (width, all_height, width, all_height))
# Styles for drawing text
fp.write('<style type="text/css">\n')
fp.write('.year {\n')
fp.write('font-family: Arial, Helvetica, sans-serif;\n')
fp.write('font-style: normal;\n')
fp.write('font-size: 15px;\n')
fp.write('}\n')
fp.write('.version {\n')
fp.write('font-family: Arial, Helvetica, sans-serif;\n')
fp.write('font-style: normal;\n')
fp.write('font-size: 15px;\n')
fp.write('fill: blue;\n')
fp.write('}\n')
fp.write('</style>\n')
# The big rectangle down the left side
fp.write('<rect fill="#000" stroke="#000" x="%d" y="%d" width="%d" height="%d"/>\n'
% (rect_left, top_margin, rect_width, rect_height))
# The years and the little lines that mark the years and the months
for year in range(year_min, year_max+1):
y = top_margin + year_step*(year_max-year)
fp.write('<text x="%d" y="%d" class="year">%d</text>\n' % (8, y+4, year))
fp.write('<line x1="%d" y1="%d" x2="%d" y2="%d" stroke="#000" stroke-width="%d"/>\n'
% (rect_left-4, y, rect_left, y, 3))
if year < year_max:
for month in range(1, 12):
y2 = y - month*month_step
fp.write('<line x1="%d" y1="%d" x2="%d" y2="%d" stroke="#000" stroke-width="%d"/>\n'
% (rect_left-2, y2, rect_left, y2, 1))
# month_len[i] is the length of month i
month_len = [ None, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]
# Place the texts
for date, text, y_adj in date_list:
y = top_margin + year_step*(year_max-date.year)
y -= (date.month-1)*month_step
mlen = month_len[date.month]
if date.month == 2 and date.year%4 == 0:
mlen += 1
y -= (date.day-1)*month_step/mlen
# Stroke across the big rectangle
fp.write('<line x1="%d" y1="%f" x2="%d" y2="%f" stroke="#fff" stroke-width="%d"/>\n'
% (rect_left, y, rect_left+rect_width, y, 1))
# From the rectangle to the text
y2 = y+y_adj
fp.write('<line x1="%d" y1="%f" x2="%d" y2="%f" stroke="#000" stroke-width="%d"/>\n'
% (rect_left+rect_width, y, text_left, y2, 1))
# The text
link = text.replace(' ', '_')
fp.write('<a xlink:href="https://nethackwiki.com/wiki/%s">\n' % link)
fp.write('<text x="%d" y="%f" class="version">%s</text>\n'
% (text_left, y2+4, text))
fp.write('</a>\n')
imagemap.append((y2+4, text))
fp.write('</svg>\n')
with open('imagemap.txt', 'w') as fp:
fp.write('<noinclude>\n')
fp.write('To change this, edit the script at [[NetHack/nh-timeline.py]] ')
fp.write('and upload nh-timeline.svg as [[:File:nh-timeline.svg]] and ')
fp.write('imagemap.txt as this template.\n')
fp.write('</noinclude>\n')
fp.write('<div class="thumb tright hoverlist">')
fp.write('<imagemap>\n')
fp.write('Image:nh-timeline.svg|Timeline of NetHack\n')
for y, text in imagemap:
fp.write('rect %d %d %d %d [[%s]]\n' % (text_left, y-13, width-10, y, text))
fp.write('</imagemap>\n')
fp.write('</div>\n')