Merge pull request #17 from dirtbags/neale

Puzzle.randword, don't escape HTML, change mono font
This commit is contained in:
magic-hat-trick 2016-11-28 16:43:19 -07:00 committed by GitHub
commit e68f607bd9
17 changed files with 99 additions and 17 deletions

View File

@ -0,0 +1,45 @@
Author: neale
Summary: static puzzles
Answer: puzzle.moth
Puzzle categories are laid out on the filesystem:
example/
├─1
│ └─puzzle.moth
├─2
│ ├─puzzle.moth
│ └─salad.jpg
├─3
│ └─puzzle.py
├─10
│ └─puzzle.py
└─100
└─puzzle.moth
In this example,
there are puzzles with point values 1, 2, 3, 10, and 100.
Puzzles 1, 2, and 100 are "static" puzzles:
their content was written by hand.
Puzzles 3 and 10 are "dynamic" puzzles:
they are generated from a Python module.
To create a static puzzle, all you must have is a
`puzzle.moth` file in the puzzle's directory.
This file is in the following format:
Author: [name of the person who wrote this puzzle]
Summary: [brief description of the puzzle]
Answer: [answer to this puzzle]
Answer: [second acceptable answer to this puzzle]
This is the puzzle body.
It is Markdown formatted:
you can read more about Markdown on the Internet.
To move to the next puzzle in a category,
someone on some team must provide an answer to the highest-point puzzle in that category.
The answer to this puzzle is the name of the file required to make a static puzzle.

View File

@ -0,0 +1,14 @@
Author: neale
Summary: Static puzzle resource files
Answer: salad
You can include additional resources in a static puzzle,
by just dropping them in the directory.
You can refer to them directly in your Markdown,
or use them however else you see fit.
They will appear in the same directory on the web server once the exercise is running.
![Leafy Green Deliciousness](salad.jpg)
The answer for this page is what is featured in the photograph.

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,20 @@
#!/usr/bin/python3
def make(puzzle):
puzzle.author = 'neale'
puzzle.summary = 'dynamic puzzles'
answer = puzzle.randword()
puzzle.answers.append(answer)
puzzle.body.write("To generate a dynamic puzzle, you need to write a Python module.\n")
puzzle.body.write("\n")
puzzle.body.write("The passed-in puzzle object provides some handy methods.\n")
puzzle.body.write("In particular, please use the `puzzle.rand` object to guarantee that rebuilding a category\n")
puzzle.body.write("won't change puzzles and answers.\n")
puzzle.body.write("(Participants don't like it when puzzles and answers change.)\n")
puzzle.body.write("\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))

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +0,0 @@
@font-face {
font-family: 'Dosis';
font-style: normal;
font-weight: 400;
src: local('Dosis Regular'), local('Dosis-Regular'), url(Dosis-Regular.ttf) format('truetype');
}

Binary file not shown.

Binary file not shown.

View File

@ -17,10 +17,10 @@
src: local('Lato Italic'), local('Lato-Italic'), url(Lato-Italic.ttf) format('truetype'); src: local('Lato Italic'), local('Lato-Italic'), url(Lato-Italic.ttf) format('truetype');
} }
@font-face { @font-face {
font-family: 'Dosis'; font-family: 'Inconsolata';
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Dosis Regular'), local('Dosis-Regular'), url(Dosis-Regular.ttf) format('truetype'); src: local('Inconsolata Regular'), local('Inconsolata-Regular'), url(Inconsolata-Regular.ttf) format('truetype');
} }
@ -38,7 +38,7 @@ body {
} }
pre, tt { pre, tt {
font-family: 'Dosis', monospace; font-family: Inconsolata, monospace;
background-color: rgba(0, 0, 0, 0.3); background-color: rgba(0, 0, 0, 0.3);
} }

View File

@ -21,6 +21,8 @@ except ImportError:
NOT_FOUND = 404 NOT_FOUND = 404
INTERNAL_SERVER_ERROR = 500 INTERNAL_SERVER_ERROR = 500
sys.dont_write_bytecode = True
# XXX: This will eventually cause a problem. Do something more clever here. # XXX: This will eventually cause a problem. Do something more clever here.
seed = 1 seed = 1
@ -48,7 +50,7 @@ def mdpage(body):
title = "Result" title = "Result"
title = title.lstrip("#") title = title.lstrip("#")
title = title.strip() title = title.strip()
return page(title, mistune.markdown(body)) return page(title, mistune.markdown(body, escape=False))
class ThreadingServer(socketserver.ThreadingMixIn, http.server.HTTPServer): class ThreadingServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
@ -162,11 +164,12 @@ you are a fool.
body.write("</ul>") body.write("</ul>")
body.write("<h2>Author</h2><p>{}</p>".format(puzzle.author)) body.write("<h2>Author</h2><p>{}</p>".format(puzzle.author))
body.write("<h2>Summary</h2><p>{}</p>".format(puzzle.summary)) body.write("<h2>Summary</h2><p>{}</p>".format(puzzle.summary))
body.write("<h2>Debug Log</h2>") if puzzle.logs:
body.write('<ul class="log">') body.write("<h2>Debug Log</h2>")
for l in puzzle.logs: body.write('<ul class="log">')
body.write("<li>{}</li>".format(html.escape(l))) for l in puzzle.logs:
body.write("</ul>") body.write("<li>{}</li>".format(html.escape(l)))
body.write("</ul>")
elif len(parts) == 5: elif len(parts) == 5:
# Serve up a puzzle file # Serve up a puzzle file
try: try:

View File

@ -146,6 +146,11 @@ class Puzzle:
name = self.random_hash() name = self.random_hash()
self.files[name] = PuzzleFile(stream, name, visible) self.files[name] = PuzzleFile(stream, name, 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, sep=' '):
"""Generate and return a new answer. It's automatically added to the puzzle answer list. """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 int word_count: The number of words to include in the answer.
@ -153,7 +158,8 @@ class Puzzle:
:returns: The answer string :returns: The answer string
""" """
answer = sep.join(self.rand.sample(ANSWER_WORDS, word_count)) words = [self.randword() for i in range(word_count)]
answer = sep.join(words)
self.answers.append(answer) self.answers.append(answer)
return answer return answer
@ -162,7 +168,7 @@ class Puzzle:
def html_body(self): def html_body(self):
"""Format and return the markdown for the puzzle body.""" """Format and return the markdown for the puzzle body."""
return mistune.markdown(self.get_body()) return mistune.markdown(self.get_body(), escape=False)
def hashes(self): def hashes(self):
"Return a list of answer hashes" "Return a list of answer hashes"