#!/usr/bin/env python3 import argparse import base64 import glob import hashlib import hmac import io import json import os import markdown import random import uuid import zipfile messageChars = b'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' def djb2hash(buf): h = 5381 for c in buf: h = ((h * 33) + c) & 0xffffffff return h def write_kv_pairs(ziphandle, filename, kv): filehandle = io.StringIO() for key in sorted(kv.keys()): if type(kv[key]) == type([]): for val in kv[key]: filehandle.write("%s: %s%s" % (key, val, os.linesep)) else: filehandle.write("%s: %s%s" % (key, kv[key], os.linesep)) filehandle.seek(0) ziphandle.writestr(filename, filehandle.read()) class Puzzle: def __init__(self, stream): self.message = bytes(random.choice(messageChars) for i in range(20)) self.fields = {} self.answers = [] self.hashes = [] body = [] header = True for line in stream: if header: line = line.strip() if not line.strip(): header = False continue key, val = line.split(':', 1) key = key.lower() val = val.strip() self._add_field(key, val) else: body.append(line) self.body = ''.join(body) def _add_field(self, key, val): if key == 'answer': h = djb2hash(val.encode('utf8')) self.answers.append(val) self.hashes.append(h) else: self.fields[key] = val def publish(self): obj = { 'author': self.fields['author'], 'hashes': self.hashes, 'body': markdown.markdown(self.body), } return obj def secrets(self): obj = { 'answers': self.answers, 'summary': self.fields['summary'], } return obj parser = argparse.ArgumentParser(description='Build a puzzle category') parser.add_argument('seed', help='contest seed') parser.add_argument('puzzledir', nargs='+', help='Directory of puzzle source') parser.add_argument('outdir', help='Output directory') args = parser.parse_args() for puzzledir in args.puzzledir: puzzles = {} secrets = {} categoryname = os.path.basename(puzzledir.strip(os.sep)) # build puzzle seed puzzle_seed = hashlib.new('sha1') puzzle_seed.update(categoryname.encode('utf-8')) puzzle_seed.update(args.seed.encode('utf-8')) puzzle_seed = puzzle_seed.hexdigest() # create zipfile zipfilename = os.path.join(args.outdir, "%s.zip" % categoryname) if os.path.exists(zipfilename): # assume blank state for now pass # # open and gather some state # zf = zipfile.ZipFile(zipfilename, 'a') else: # create and read in state zf = zipfile.ZipFile(zipfilename, 'w') # read in puzzle details (will be pflarr in future) for puzzlePath in glob.glob(os.path.join(puzzledir, "*.moth")): filename = os.path.basename(puzzlePath) points, ext = os.path.splitext(filename) points = int(points) puzzle = Puzzle(open(puzzlePath)) puzzles[points] = puzzle # build mapping, answers, and summary mapping = {} answers = {} summary = {} for points in sorted(puzzles): puzzle = puzzles[points] mapping[points] = str(uuid.uuid4()) # random uuid answers[points] = puzzle.answers summary[points] = puzzle.fields['summary'] # write mapping, answers, and summary write_kv_pairs(zf, os.path.join(categoryname, 'mapping.txt'), mapping) write_kv_pairs(zf, os.path.join(categoryname, 'answers.txt'), answers) write_kv_pairs(zf, os.path.join(categoryname, 'summary.txt'), summary) print(mapping) print(answers) print(summary) # write out puzzles for points in sorted(puzzles): puzzle = puzzles[points] #vim:py