This project uses Python 3.6 and Django 2.0
Clone the repo, install requirements,
python manage.py makemigrations and
python manage.py migrate, then
python manage.py loaddata puzzle_data.json to add the existing puzzles to the database. (Warning: The
puzzle_data.json file includes spoilers for some puzzles.)
This project uses
python-decouple to hide secret data from version control, so make sure you've created a
.env file at root level with at the very least a secret key. (You can find more examples in the
.env-example file). You may also need to comment out some of the OAuth values from
settings.py (pretty much everything that starts with
SOCIAL_AUTH_), at least at first.
The most important model is
Puzzle. Hopefully, most of the fields are self-explanatory, but some of the more obscure ones:
short_nameis specifically the file name for the template to render to show that puzzle, minus the
meta_orderis used to set the order that all of the puzzles in a given meta will be displayed when using the
as_ultemplate tag. If blank, the puzzles will be sorted alphabetically by title, ignoring articles.
metapuzzlesis a many-to-many, non-symmetric self-join. Note that this allows a puzzle to belong to multiple metapuzzles (as in the Emotions round of the 2018 MIT Mystery Hunt) and for multiple metas to be part of a supermeta.
PuzzleSet model controls what's shown on the main page (
puzzles:index route). Every puzzle set (regardless of how many puzzles, metas, or layers of nested metas there are) should have its own directory in
static, with a
style.css in the latter.
Some possible ideas for improvements:
- Changing answer submission to AJAX seems straightforward enough.
- As of now, correct answers are stored in the database in plain text. I'd rather they be encrypted, just to make hypothetical cheating that much harder; however, I also wanted to include a user's history of answer submissions in the clear (so they could be shown back to them), so the vulnerability's there regardless. It would avoid the spoilers in
puzzle_data.json, at least.
- Also in terms of hypothetical cheating: It would be neat to do some sort of rate limiting, so that a person can't submit every 20,000 answers a second and brute force "guess" the answer.
- In the spirit of DRY, whether an answer is correct or not isn't saved directly in the database; rather, it's a method (technically a Python
@property) that compares the submitted answer to the puzzle's saved answer. It might be more efficient to store that on the answer itself (or have a separate table tracking which users have solved which puzzles), but I haven't tested that yet.
- It would be neat to add an unlock mechanism, where only some puzzles are available at first and solving those opens up new puzzles to solve, but I haven't made any sets large enough for that to be necessary.
This project is licensed under the MIT License - see the LICENSE.md file for details
- You can see version 1 here, although the code in this project was a from-scratch rewrite.
- I found two guides by Vitor Freitas, How to Use Django's Built-in Login System and How to Add Social Login to Django, very helpful when trying to get started with Django's
authsystem. The account settings and password change pages in particular come nearly directly from his examples.
- I'd be remiss if I didn't point to some other fantastic puzzle sites that inspired me to build this project in the first place:
- Similarly, many thanks to team Duck Soup for their help in testing these puzzles.