mirror of https://github.com/dirtbags/moth.git
Just some twiddling
This commit is contained in:
parent
175b7aaa1b
commit
0831c4e3d5
|
@ -4,6 +4,7 @@ function randint(max) {
|
|||
|
||||
const MILLISECOND = 1
|
||||
const SECOND = MILLISECOND * 1000
|
||||
const FRAMERATE = 24 / SECOND // Fast enough for this tomfoolery
|
||||
|
||||
class Point {
|
||||
constructor(x, y) {
|
||||
|
@ -87,7 +88,7 @@ class QixLine {
|
|||
* like the video game "qix"
|
||||
*/
|
||||
class QixBackground {
|
||||
constructor(ctx, frameInterval = SECOND/6) {
|
||||
constructor(ctx, frameRate = 6/SECOND) {
|
||||
this.ctx = ctx
|
||||
this.min = new Point(0, 0)
|
||||
this.max = new Point(this.ctx.canvas.width, this.ctx.canvas.height)
|
||||
|
@ -95,7 +96,7 @@ class QixBackground {
|
|||
|
||||
this.lines = [
|
||||
new QixLine(
|
||||
0,
|
||||
Math.random(),
|
||||
new Point(randint(this.box.x), randint(this.box.y)),
|
||||
new Point(randint(this.box.x), randint(this.box.y)),
|
||||
)
|
||||
|
@ -109,7 +110,7 @@ class QixBackground {
|
|||
new Point(1 + randint(this.box.x / 100), 1 + randint(this.box.y / 100)),
|
||||
)
|
||||
|
||||
this.frameInterval = frameInterval
|
||||
this.frameInterval = MILLISECOND / frameRate
|
||||
this.nextFrame = 0
|
||||
}
|
||||
|
||||
|
@ -157,8 +158,8 @@ function init() {
|
|||
let ctx = canvas.getContext("2d")
|
||||
|
||||
let qix = new QixBackground(ctx)
|
||||
setInterval(() => qix.Animate(), SECOND/6)
|
||||
|
||||
// window.requestAnimationFrame is overkill for something this silly
|
||||
setInterval(() => qix.Animate(), MILLISECOND/FRAMERATE)
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
|
|
|
@ -74,6 +74,7 @@ p {
|
|||
}
|
||||
form, pre {
|
||||
margin: 1em;
|
||||
overflow-x: auto;
|
||||
}
|
||||
input, select {
|
||||
padding: 0.6em;
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
// Dan Bernstein hash v1
|
||||
// Used until MOTH v3.5
|
||||
function djb2(buf) {
|
||||
let h = 5381
|
||||
for (let c of (new TextEncoder()).encode(buf)) { // Encode as UTF-8 and read in each byte
|
||||
// JavaScript converts everything to a signed 32-bit integer when you do bitwise operations.
|
||||
// So we have to do "unsigned right shift" by zero to get it back to unsigned.
|
||||
h = (((h * 33) + c) & 0xffffffff) >>> 0
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// Used until MOTH v4.5
|
||||
async function sha256(message) {
|
||||
const msgUint8 = new TextEncoder().encode(message); // encode as (utf-8) Uint8Array
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8); // hash the message
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
|
||||
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
|
||||
return hashHex;
|
||||
}
|
|
@ -15,7 +15,7 @@ class Hash {
|
|||
for (let c of (new TextEncoder()).encode(buf)) { // Encode as UTF-8 and read in each byte
|
||||
// JavaScript converts everything to a signed 32-bit integer when you do bitwise operations.
|
||||
// So we have to do "unsigned right shift" by zero to get it back to unsigned.
|
||||
h = (((h * 33) + c) & 0xffffffff) >>> 0
|
||||
h = ((h * 33) + c) >>> 0
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ class Hash {
|
|||
static djb2xor(buf) {
|
||||
let h = 5381
|
||||
for (let c of (new TextEncoder()).encode(buf)) {
|
||||
h = h * 33 ^ c
|
||||
h = ((h * 33) ^ c) >>> 0
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
@ -40,14 +40,14 @@ class Hash {
|
|||
* Used until MOTH v4.5
|
||||
*
|
||||
* @param {String} buf Input
|
||||
* @returns {String} hex-encoded digest
|
||||
* @returns {Promise.<String>} hex-encoded digest
|
||||
*/
|
||||
static async sha256(buf) {
|
||||
const msgUint8 = new TextEncoder().encode(buf)
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8)
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
||||
return this.hexlify(hashArray);
|
||||
}
|
||||
static async sha256(buf) {
|
||||
const msgUint8 = new TextEncoder().encode(buf)
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8)
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
||||
return this.hexlify(hashArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hex-encode a byte array
|
||||
|
@ -55,9 +55,23 @@ class Hash {
|
|||
* @param {Number[]} buf Byte array
|
||||
* @returns {String}
|
||||
*/
|
||||
static async hexlify(buf) {
|
||||
static hexlify(buf) {
|
||||
return buf.map(b => b.toString(16).padStart(2, "0")).join("")
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply every hash to the input buffer.
|
||||
*
|
||||
* @param {String} buf Input
|
||||
* @returns {Promise.<String[]>}
|
||||
*/
|
||||
static async All(buf) {
|
||||
return [
|
||||
String(this.djb2(buf)),
|
||||
String(this.djb2xor(buf)),
|
||||
await this.sha256(buf),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -166,12 +180,24 @@ class Puzzle {
|
|||
return this.server.GetContent(this.Category, this.Points, filename)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string is possibly correct.
|
||||
*
|
||||
* The server sends a list of answer hashes with each puzzle: this method
|
||||
* checks to see if any of those hashes match a hash of the string.
|
||||
*
|
||||
* The MOTH development team likes obscure hash functions with a lot of
|
||||
* collisions, which means that a given input may match another possible
|
||||
* string's hash. We do this so that if you run a brute force attack against
|
||||
* the list of hashes, you have to write your own brute force program, and
|
||||
* you still have to pick through a lot of potentially correct answers when
|
||||
* it's done.
|
||||
*
|
||||
* @param {String} str User-submitted possible answer
|
||||
* @returns {Promise.<Boolean>}
|
||||
*/
|
||||
async IsPossiblyCorrect(str) {
|
||||
let userAnswerHashes = [
|
||||
Hash.djb2(str),
|
||||
Hash.djb2xor(str),
|
||||
await Hash.sha256(str),
|
||||
]
|
||||
let userAnswerHashes = await Hash.All(str)
|
||||
|
||||
for (let pah of this.AnswerHashes) {
|
||||
for (let uah of userAnswerHashes) {
|
||||
|
|
|
@ -95,9 +95,9 @@ async function loadPuzzle(category, points) {
|
|||
|
||||
let server = new moth.Server()
|
||||
let puzzle = server.GetPuzzle(category, points)
|
||||
console.time("Puzzle load")
|
||||
console.time("Populate")
|
||||
await puzzle.Populate()
|
||||
console.timeEnd("Puzzle load")
|
||||
console.timeEnd("Populate")
|
||||
|
||||
let title = `${category} ${points}`
|
||||
document.querySelector("title").textContent = title
|
||||
|
@ -122,6 +122,8 @@ async function loadPuzzle(category, points) {
|
|||
document.getElementById("files").appendChild(li)
|
||||
}
|
||||
|
||||
let baseElement = document.head.appendChild(document.createElement("base"))
|
||||
baseElement.href = contentBase
|
||||
|
||||
window.app.puzzle = puzzle
|
||||
console.info("window.app.puzzle =", window.app.puzzle)
|
||||
|
@ -142,6 +144,11 @@ function init() {
|
|||
// There isn't a more graceful way to "unload" scripts attached to the current puzzle
|
||||
window.addEventListener("hashchange", () => location.reload())
|
||||
|
||||
// Make all links absolute, because we're going to be changing the base URL
|
||||
for (let e of document.querySelectorAll("[href]")) {
|
||||
e.href = new URL(e.href, location)
|
||||
}
|
||||
|
||||
let hashpart = location.hash.split("#")[1] || ""
|
||||
let catpoints = hashpart.split(":")
|
||||
let category = catpoints[0]
|
||||
|
|
Loading…
Reference in New Issue