sumbloom / puzzle.js
CyberMax
Deploy sumbloom
96a4f6b verified
Raw History Blame
4.57 kB
/* Sumbloom puzzle core (pure, no DOM). © 2026 CyberMax. All rights reserved. */
(function (root) {
"use strict";
var EPOCH = Date.UTC(2026, 8, 24); // puzzle #1 = 24 Sep 2026
var ROUNDS = [
{ small: 5, large: 1, min: 20, max: 99, steps: [2] },
{ small: 4, large: 2, min: 50, max: 199, steps: [2, 3] },
{ small: 4, large: 2, min: 100, max: 299, steps: [3] },
{ small: 3, large: 3, min: 150, max: 399, steps: [3, 4] },
{ small: 3, large: 3, min: 250, max: 499, steps: [4] }
];
var LARGE = [10, 11, 12, 13, 15, 20, 25];
var OPS = { "+": "+", "-": "−", "*": "×", "/": "÷" };
var FLOWER = ["🍂", "🌱", "🌼", "🌸"];
// ---------- seeded random ----------
function hash(str) {
var h = 1779033703 ^ str.length;
for (var i = 0; i < str.length; i++) {
h = Math.imul(h ^ str.charCodeAt(i), 3432918353);
h = (h << 13) | (h >>> 19);
}
return function () {
h = Math.imul(h ^ (h >>> 16), 2246822507);
h = Math.imul(h ^ (h >>> 13), 3266489909);
return (h ^= h >>> 16) >>> 0;
};
}
function mulberry(seed) {
return function () {
seed |= 0; seed = (seed + 0x6d2b79f5) | 0;
var t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
function pick(rng, arr) { return arr[Math.floor(rng() * arr.length)]; }
function apply(a, op, b) {
if (op === "+") return a + b;
if (op === "-") return a - b > 0 ? a - b : null;
if (op === "*") return a * b;
if (op === "/") return b !== 0 && a % b === 0 ? a / b : null;
return null;
}
function oneStepValues(nums) {
var out = {};
for (var i = 0; i < nums.length; i++) for (var j = 0; j < nums.length; j++) {
if (i === j) continue;
for (var op in OPS) { var v = apply(nums[i], op, nums[j]); if (v) out[v] = true; }
}
return out;
}
// Build one round: numbers + a target reached by a known sequence of steps.
function makeRound(rng, cfg) {
for (var attempt = 0; attempt < 5000; attempt++) {
var small = [];
while (small.length < cfg.small) {
var s = 1 + Math.floor(rng() * 9);
if (small.filter(function (x) { return x === s; }).length < 1 || rng() < 0.15) small.push(s);
}
var large = [];
while (large.length < cfg.large) {
var l = pick(rng, LARGE);
if (large.indexOf(l) < 0) large.push(l);
}
var nums = small.concat(large).sort(function (a, b) { return a - b; });
var pool = nums.slice();
var steps = pick(rng, cfg.steps);
var sol = [];
var ok = true;
for (var k = 0; k < steps && ok; k++) {
var tries = 0, done = false;
while (!done && tries++ < 60) {
var i = Math.floor(rng() * pool.length), j = Math.floor(rng() * pool.length);
if (i === j) continue;
var r = rng();
var op = r < 0.3 ? "+" : r < 0.5 ? "-" : r < 0.85 ? "*" : "/";
var a = pool[i], b = pool[j];
if (op === "*" && (a === 1 || b === 1)) continue;
if (op === "/" && b === 1) continue;
var v = apply(a, op, b);
if (!v) continue;
sol.push([a, op, b, v]);
pool.splice(Math.max(i, j), 1);
pool.splice(Math.min(i, j), 1);
pool.push(v);
done = true;
}
if (!done) ok = false;
}
if (!ok) continue;
var target = sol[sol.length - 1][3];
if (target < cfg.min || target > cfg.max) continue;
if (nums.indexOf(target) >= 0) continue;
if (oneStepValues(nums)[target]) continue; // no one-move targets
return { nums: nums, target: target, solution: sol };
}
throw new Error("could not build round");
}
function makePuzzle(seedStr) {
var rng = mulberry(hash(seedStr)());
return ROUNDS.map(function (cfg) { return makeRound(rng, cfg); });
}
// ---------- dates ----------
function todayStr(d) {
d = d || new Date();
return d.getFullYear() + "-" + String(d.getMonth() + 1).padStart(2, "0") + "-" + String(d.getDate()).padStart(2, "0");
}
function puzzleNo(dateStr) {
var p = dateStr.split("-").map(Number);
return Math.floor((Date.UTC(p[0], p[1] - 1, p[2]) - EPOCH) / 864e5) + 1;
}
root.SumbloomCore = { makePuzzle: makePuzzle, apply: apply, puzzleNo: puzzleNo, todayStr: todayStr, OPS: OPS, FLOWER: FLOWER };
if (typeof module !== "undefined") module.exports = root.SumbloomCore;
})(typeof window !== "undefined" ? window : globalThis);