From 15503cb45b604350296318d964a93e0e22ce490d Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Sun, 24 Feb 2019 11:53:22 -0700 Subject: [PATCH] Check user-supplied answers for possible correctness --- devel/devel-server.py | 2 -- devel/moth.py | 6 +++--- theme/puzzle.html | 2 +- theme/puzzle.js | 44 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/devel/devel-server.py b/devel/devel-server.py index 3d2de63..2be469a 100755 --- a/devel/devel-server.py +++ b/devel/devel-server.py @@ -40,8 +40,6 @@ def get_puzzle(request, data=None): puzzle = cat.puzzle(points) return puzzle -# OMG what is this I hate Python now -@asyncio.coroutine async def handle_answer(request): data = await request.post() puzzle = get_puzzle(request, data) diff --git a/devel/moth.py b/devel/moth.py index 9820783..ee13f46 100644 --- a/devel/moth.py +++ b/devel/moth.py @@ -15,9 +15,9 @@ import tempfile messageChars = b'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' -def djb2hash(buf): +def djb2hash(str): h = 5381 - for c in buf: + for c in str.encode("utf-8"): h = ((h * 33) + c) & 0xffffffff return h @@ -277,7 +277,7 @@ class Puzzle: def hashes(self): "Return a list of answer hashes" - return [djb2hash(a.encode('utf-8')) for a in self.answers] + return [djb2hash(a) for a in self.answers] class Category: diff --git a/theme/puzzle.html b/theme/puzzle.html index fc03cfa..2a8dcba 100644 --- a/theme/puzzle.html +++ b/theme/puzzle.html @@ -22,7 +22,7 @@ Team ID:
- Answer:
+ Answer:
diff --git a/theme/puzzle.js b/theme/puzzle.js index f129a51..6ab5f07 100644 --- a/theme/puzzle.js +++ b/theme/puzzle.js @@ -44,7 +44,17 @@ function devel_addin(obj, e) { } } +// The routine used to hash answers in compiled puzzle packages +function djb2hash(buf) { + let h = 5381 + for (let c of (new TextEncoder).encode(buf)) { // JavaScript is weird. + h = ((h * 33) + c) & 0xffffffff + } + return h +} + +// Pop up a message function toast(message, timeout=5000) { let p = document.createElement("p") @@ -56,7 +66,7 @@ function toast(message, timeout=5000) { ) } - +// When the user submits an answer function submit(e) { e.preventDefault() fetch("answer", { @@ -92,6 +102,9 @@ function loadPuzzle(categoryName, points, puzzleId) { // Populate authors document.getElementById("authors").textContent = obj.authors.join(", ") + // Make the whole puzzle available + window.puzzle = obj + // If answers are provided, this is the devel server if (obj.answers) { devel_addin(obj, document.getElementById("devel")) @@ -139,6 +152,32 @@ function loadPuzzle(categoryName, points, puzzleId) { document.querySelector("input[name=points]").value = points } +function answerCheck(e) { + let answer = e.target.value + let ok = document.querySelector("#answer_ok") + + // You have to provide someplace to put the check + if (! ok) { + return + } + + let possiblyCorrect = false + let answerHash = djb2hash(answer) + for (let correctHash of window.puzzle.hashes) { + if (correctHash == answerHash) { + possiblyCorrect = true + } + } + + if (possiblyCorrect) { + ok.textContent = "🙆" + ok.title = "Possibly correct" + } else { + ok.textContent = "🙅" + ok.title = "Definitely not correct" + } +} + function init() { let params = new URLSearchParams(window.location.search) let categoryName = params.get("cat") @@ -154,6 +193,9 @@ function init() { document.querySelector("input[name=id]").value = teamId } + if (document.querySelector("#answer")) { + document.querySelector("#answer").addEventListener("input", answerCheck) + } document.querySelector("form").addEventListener("submit", submit) }