mirror of https://github.com/dirtbags/moth.git
Oops, stuff that should have been in last commit
This commit is contained in:
parent
a90d673b01
commit
06e0bddb1a
|
@ -29,7 +29,6 @@ for details.
|
|||
Getting Started Developing
|
||||
-------------------------------
|
||||
|
||||
$ git clone $your_puzzles_repo puzzles
|
||||
$ python3 tools/devel-server.py
|
||||
|
||||
Then point a web browser at http://localhost:8080/
|
||||
|
|
|
@ -13,17 +13,17 @@ Puzzle categories are laid out on the filesystem:
|
|||
├─3
|
||||
│ └─puzzle.py
|
||||
├─10
|
||||
│ └─puzzle.py
|
||||
│ └─puzzle.moth
|
||||
└─100
|
||||
└─puzzle.moth
|
||||
└─puzzle.py
|
||||
|
||||
In this example,
|
||||
there are puzzles with point values 1, 2, 3, 10, and 100.
|
||||
|
||||
Puzzles 1, 2, and 100 are "static" puzzles:
|
||||
Puzzles 1, 2, and 10 are "static" puzzles:
|
||||
their content was written by hand.
|
||||
|
||||
Puzzles 3 and 10 are "dynamic" puzzles:
|
||||
Puzzles 3 and 100 are "dynamic" puzzles:
|
||||
they are generated from a Python module.
|
||||
|
||||
To create a static puzzle, all you must have is a
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
Author: neale
|
||||
Summary: Static puzzle resource files
|
||||
File: salad.jpg
|
||||
Answer: salad
|
||||
|
||||
You can include additional resources in a static puzzle,
|
||||
|
|
|
@ -14,7 +14,14 @@ def make(puzzle):
|
|||
puzzle.body.write("(Participants don't like it when puzzles and answers change.)\n")
|
||||
puzzle.body.write("\n")
|
||||
|
||||
puzzle.add_file('salad.jpg')
|
||||
puzzle.body.write("Here are some more pictures of salad:\n")
|
||||
puzzle.body.write("<img src='salad.jpg' alt='Markdown lets you insert raw HTML if you want'>")
|
||||
puzzle.body.write("![salad](salad.jpg)")
|
||||
puzzle.body.write("\n\n")
|
||||
|
||||
number = puzzle.rand.randint(20, 500)
|
||||
puzzle.log("One is the loneliest number, but {} is the saddest number.".format(number))
|
||||
|
||||
puzzle.body.write("The answer for this page is {}.\n".format(answer))
|
||||
puzzle.body.write("The answer for this page is `{}`.\n".format(answer))
|
||||
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# To pick up any changes to this file without restarting anything:
|
||||
# while true; do ./tools/devel-server.py --once; done
|
||||
# It's kludgy, but it gets the job done.
|
||||
# Feel free to make it suck less, for example using the `tcpserver` program.
|
||||
|
||||
import glob
|
||||
import html
|
||||
import http.server
|
||||
|
@ -26,7 +31,6 @@ sys.dont_write_bytecode = True
|
|||
# XXX: This will eventually cause a problem. Do something more clever here.
|
||||
seed = 1
|
||||
|
||||
|
||||
def page(title, body):
|
||||
return """<!DOCTYPE html>
|
||||
<html>
|
||||
|
@ -58,6 +62,8 @@ class ThreadingServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
|
|||
|
||||
|
||||
class MothHandler(http.server.SimpleHTTPRequestHandler):
|
||||
puzzles_dir = "puzzles"
|
||||
|
||||
def handle_one_request(self):
|
||||
try:
|
||||
super().handle_one_request()
|
||||
|
@ -122,7 +128,7 @@ you are a fool.
|
|||
puzzle = None
|
||||
|
||||
try:
|
||||
fpath = os.path.join("puzzles", parts[2])
|
||||
fpath = os.path.join(self.puzzles_dir, parts[2])
|
||||
points = int(parts[3])
|
||||
except:
|
||||
pass
|
||||
|
@ -135,15 +141,16 @@ you are a fool.
|
|||
if not cat:
|
||||
title = "Puzzle Categories"
|
||||
body.write("<ul>")
|
||||
for i in sorted(glob.glob(os.path.join("puzzles", "*", ""))):
|
||||
body.write('<li><a href="{}">{}</a></li>'.format(i, i))
|
||||
for i in sorted(glob.glob(os.path.join(self.puzzles_dir, "*", ""))):
|
||||
bn = os.path.basename(i.strip('/\\'))
|
||||
body.write('<li><a href="/puzzles/{}">puzzles/{}/</a></li>'.format(bn, bn))
|
||||
body.write("</ul>")
|
||||
elif not puzzle:
|
||||
# List all point values in a category
|
||||
title = "Puzzles in category `{}`".format(parts[2])
|
||||
body.write("<ul>")
|
||||
for points in cat.pointvals:
|
||||
body.write('<li><a href="/puzzles/{cat}/{points}">puzzles/{cat}/{points}</a></li>'.format(cat=parts[2], points=points))
|
||||
body.write('<li><a href="/puzzles/{cat}/{points}/">puzzles/{cat}/{points}/</a></li>'.format(cat=parts[2], points=points))
|
||||
body.write("</ul>")
|
||||
elif len(parts) == 4:
|
||||
# Serve up a puzzle
|
||||
|
@ -175,7 +182,7 @@ you are a fool.
|
|||
try:
|
||||
pfile = puzzle.files[parts[4]]
|
||||
except KeyError:
|
||||
self.send_error(HTTPStatus.NOT_FOUND, "File not found")
|
||||
self.send_error(HTTPStatus.NOT_FOUND, "File not found. Did you add it to the Files: header or puzzle.add_stream?")
|
||||
return
|
||||
ctype = self.guess_type(pfile.name)
|
||||
self.send_response(HTTPStatus.OK)
|
||||
|
@ -226,10 +233,22 @@ you are a fool.
|
|||
self.wfile.write(payload)
|
||||
|
||||
|
||||
def run(address=('localhost', 8080)):
|
||||
def run(address=('localhost', 8080), once=False):
|
||||
httpd = ThreadingServer(address, MothHandler)
|
||||
print("=== Listening on http://{}:{}/".format(address[0], address[1]))
|
||||
httpd.serve_forever()
|
||||
if once:
|
||||
httpd.handle_request()
|
||||
else:
|
||||
httpd.serve_forever()
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="MOTH puzzle development server")
|
||||
parser.add_argument('--puzzles', default='puzzles',
|
||||
help="Directory containing your puzzles")
|
||||
parser.add_argument('--once', default=False, action='store_true',
|
||||
help="Serve one page, then exit. For debugging the server.")
|
||||
args = parser.parse_args()
|
||||
MothHandler.puzzles_dir = args.puzzles
|
||||
run(once=args.once)
|
||||
|
|
|
@ -74,7 +74,7 @@ class Puzzle:
|
|||
|
||||
def log(self, msg):
|
||||
"""Add a new log message to this puzzle."""
|
||||
self.logs.append(msg)
|
||||
self.logs.append(str(msg))
|
||||
|
||||
def read_stream(self, stream):
|
||||
header = True
|
||||
|
@ -116,12 +116,12 @@ class Puzzle:
|
|||
except FileNotFoundError:
|
||||
puzzle_mod = None
|
||||
|
||||
if puzzle_mod:
|
||||
with pushd(path):
|
||||
with pushd(path):
|
||||
if puzzle_mod:
|
||||
puzzle_mod.make(self)
|
||||
else:
|
||||
with open(os.path.join(path, 'puzzle.moth')) as f:
|
||||
self.read_stream(f)
|
||||
else:
|
||||
with open('puzzle.moth') as f:
|
||||
self.read_stream(f)
|
||||
|
||||
def random_hash(self):
|
||||
"""Create a file basename (no extension) with our number generator."""
|
||||
|
@ -146,12 +146,17 @@ class Puzzle:
|
|||
name = self.random_hash()
|
||||
self.files[name] = PuzzleFile(stream, name, visible)
|
||||
|
||||
def add_file(self, filename, visible=True):
|
||||
fd = open(filename, 'rb')
|
||||
name = os.path.basename(filename)
|
||||
self.add_stream(fd, name=name, visible=visible)
|
||||
|
||||
def randword(self):
|
||||
"""Return a randomly-chosen word"""
|
||||
|
||||
return self.rand.choice(ANSWER_WORDS)
|
||||
|
||||
def make_answer(self, word_count, sep=' '):
|
||||
def make_answer(self, word_count=4, sep=' '):
|
||||
"""Generate and return a new answer. It's automatically added to the puzzle answer list.
|
||||
:param int word_count: The number of words to include in the answer.
|
||||
:param str|bytes sep: The word separator.
|
||||
|
|
Loading…
Reference in New Issue