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 MILLISECOND = 1
|
||||||
const SECOND = MILLISECOND * 1000
|
const SECOND = MILLISECOND * 1000
|
||||||
|
const FRAMERATE = 24 / SECOND // Fast enough for this tomfoolery
|
||||||
|
|
||||||
class Point {
|
class Point {
|
||||||
constructor(x, y) {
|
constructor(x, y) {
|
||||||
|
@ -87,7 +88,7 @@ class QixLine {
|
||||||
* like the video game "qix"
|
* like the video game "qix"
|
||||||
*/
|
*/
|
||||||
class QixBackground {
|
class QixBackground {
|
||||||
constructor(ctx, frameInterval = SECOND/6) {
|
constructor(ctx, frameRate = 6/SECOND) {
|
||||||
this.ctx = ctx
|
this.ctx = ctx
|
||||||
this.min = new Point(0, 0)
|
this.min = new Point(0, 0)
|
||||||
this.max = new Point(this.ctx.canvas.width, this.ctx.canvas.height)
|
this.max = new Point(this.ctx.canvas.width, this.ctx.canvas.height)
|
||||||
|
@ -95,7 +96,7 @@ class QixBackground {
|
||||||
|
|
||||||
this.lines = [
|
this.lines = [
|
||||||
new QixLine(
|
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)),
|
||||||
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)),
|
new Point(1 + randint(this.box.x / 100), 1 + randint(this.box.y / 100)),
|
||||||
)
|
)
|
||||||
|
|
||||||
this.frameInterval = frameInterval
|
this.frameInterval = MILLISECOND / frameRate
|
||||||
this.nextFrame = 0
|
this.nextFrame = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,8 +158,8 @@ function init() {
|
||||||
let ctx = canvas.getContext("2d")
|
let ctx = canvas.getContext("2d")
|
||||||
|
|
||||||
let qix = new QixBackground(ctx)
|
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") {
|
if (document.readyState === "loading") {
|
||||||
|
|
|
@ -74,6 +74,7 @@ p {
|
||||||
}
|
}
|
||||||
form, pre {
|
form, pre {
|
||||||
margin: 1em;
|
margin: 1em;
|
||||||
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
input, select {
|
input, select {
|
||||||
padding: 0.6em;
|
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
|
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.
|
// 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.
|
// 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
|
return h
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ class Hash {
|
||||||
static djb2xor(buf) {
|
static djb2xor(buf) {
|
||||||
let h = 5381
|
let h = 5381
|
||||||
for (let c of (new TextEncoder()).encode(buf)) {
|
for (let c of (new TextEncoder()).encode(buf)) {
|
||||||
h = h * 33 ^ c
|
h = ((h * 33) ^ c) >>> 0
|
||||||
}
|
}
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ class Hash {
|
||||||
* Used until MOTH v4.5
|
* Used until MOTH v4.5
|
||||||
*
|
*
|
||||||
* @param {String} buf Input
|
* @param {String} buf Input
|
||||||
* @returns {String} hex-encoded digest
|
* @returns {Promise.<String>} hex-encoded digest
|
||||||
*/
|
*/
|
||||||
static async sha256(buf) {
|
static async sha256(buf) {
|
||||||
const msgUint8 = new TextEncoder().encode(buf)
|
const msgUint8 = new TextEncoder().encode(buf)
|
||||||
|
@ -55,9 +55,23 @@ class Hash {
|
||||||
* @param {Number[]} buf Byte array
|
* @param {Number[]} buf Byte array
|
||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
static async hexlify(buf) {
|
static hexlify(buf) {
|
||||||
return buf.map(b => b.toString(16).padStart(2, "0")).join("")
|
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)
|
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) {
|
async IsPossiblyCorrect(str) {
|
||||||
let userAnswerHashes = [
|
let userAnswerHashes = await Hash.All(str)
|
||||||
Hash.djb2(str),
|
|
||||||
Hash.djb2xor(str),
|
|
||||||
await Hash.sha256(str),
|
|
||||||
]
|
|
||||||
|
|
||||||
for (let pah of this.AnswerHashes) {
|
for (let pah of this.AnswerHashes) {
|
||||||
for (let uah of userAnswerHashes) {
|
for (let uah of userAnswerHashes) {
|
||||||
|
|
|
@ -95,9 +95,9 @@ async function loadPuzzle(category, points) {
|
||||||
|
|
||||||
let server = new moth.Server()
|
let server = new moth.Server()
|
||||||
let puzzle = server.GetPuzzle(category, points)
|
let puzzle = server.GetPuzzle(category, points)
|
||||||
console.time("Puzzle load")
|
console.time("Populate")
|
||||||
await puzzle.Populate()
|
await puzzle.Populate()
|
||||||
console.timeEnd("Puzzle load")
|
console.timeEnd("Populate")
|
||||||
|
|
||||||
let title = `${category} ${points}`
|
let title = `${category} ${points}`
|
||||||
document.querySelector("title").textContent = title
|
document.querySelector("title").textContent = title
|
||||||
|
@ -122,6 +122,8 @@ async function loadPuzzle(category, points) {
|
||||||
document.getElementById("files").appendChild(li)
|
document.getElementById("files").appendChild(li)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let baseElement = document.head.appendChild(document.createElement("base"))
|
||||||
|
baseElement.href = contentBase
|
||||||
|
|
||||||
window.app.puzzle = puzzle
|
window.app.puzzle = puzzle
|
||||||
console.info("window.app.puzzle =", window.app.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
|
// There isn't a more graceful way to "unload" scripts attached to the current puzzle
|
||||||
window.addEventListener("hashchange", () => location.reload())
|
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 hashpart = location.hash.split("#")[1] || ""
|
||||||
let catpoints = hashpart.split(":")
|
let catpoints = hashpart.split(":")
|
||||||
let category = catpoints[0]
|
let category = catpoints[0]
|
||||||
|
|
Loading…
Reference in New Issue