Just some twiddling

This commit is contained in:
Neale Pickett 2023-09-12 19:30:53 -06:00
parent 175b7aaa1b
commit 0831c4e3d5
5 changed files with 57 additions and 42 deletions

View File

@ -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") {

View File

@ -74,6 +74,7 @@ p {
}
form, pre {
margin: 1em;
overflow-x: auto;
}
input, select {
padding: 0.6em;

View File

@ -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;
}

View File

@ -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,7 +40,7 @@ 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)
@ -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) {

View File

@ -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]