mirror of https://github.com/dirtbags/moth.git
Merge pull request #17 from dirtbags/neale
Puzzle.randword, don't escape HTML, change mono font
This commit is contained in:
commit
d42229f3c5
|
@ -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.
|
|
@ -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 |
|
@ -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.
|
@ -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.
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue