Spaces:
Running
Running
Download core.js from CyberMax-tools/dewhop: direct link, hf CLI and curl.
- Browser
- Download file 4.4 kB
-
https://huggingface.co/spaces/CyberMax-tools/dewhop/resolve/main/core.js
- Command line
-
hf download hf://spaces/CyberMax-tools/dewhop/core.js
-
curl -L -o core.js https://huggingface.co/spaces/CyberMax-tools/dewhop/resolve/main/core.js
4.4 kB
| // Dewhop core: deterministic simulation (no DOM). A dewdrop sits on a leaf; the next leaf above sways from side to | |
| // side on its vine. One tap makes the drop hop straight up; it lands if the swaying leaf is under it when the hop | |
| // ends (dead centre = a perfect). Miss once and the drop falls: the run is over. Wait too long and the sun dries the | |
| // drop off its leaf. Leaves get narrower and faster; later ones gust (speed up and slow down) or drift. | |
| // Same leaves for everyone each UTC day (seed = date). Fixed 60 Hz ticks. (c) 2026 CyberMax. All rights reserved. | |
| export const HZ = 60; | |
| export const HOP = 16; // ticks a hop takes (the leaf keeps swaying meanwhile: lead your tap) | |
| export const DRY = 5 * HZ; // ticks a drop can wait on one leaf before the sun dries it | |
| export const SETTLE = 14; // ticks after a landing before the next tap counts (the landing splash) | |
| export const X0 = 0.5; // the drop's column (fraction of the width); every leaf sways across it | |
| export function rng(seed) { | |
| let h = 0x811c9dc5; | |
| for (let i = 0; i < seed.length; i++) { h ^= seed.charCodeAt(i); h = Math.imul(h, 0x01000193); } | |
| let a = h >>> 0; | |
| return () => { | |
| a = (a + 0x6d2b79f5) >>> 0; | |
| let t = a; | |
| t = Math.imul(t ^ (t >>> 15), t | 1); | |
| t ^= t + Math.imul(t ^ (t >>> 7), t | 61); | |
| return ((t ^ (t >>> 14)) >>> 0) / 4294967296; | |
| }; | |
| } | |
| /** Leaf i of a seed: half-width w (fraction of the width), sway amplitude a, angular speed s (rad/s), phase ph, | |
| * gust g (the sway speeds up and slows down), side (the vine hangs from the left or the right), hue shift. */ | |
| export function leaf(seed, i) { | |
| const r = rng(`dewhop:${seed}:${i}`); | |
| return { | |
| w: Math.max(0.055, 0.135 - i * 0.0042 - r() * 0.015), | |
| a: Math.min(0.36, 0.2 + i * 0.008 + r() * 0.1), | |
| s: Math.min(3.0, 1.35 + i * 0.06 + r() * 0.35), | |
| ph: r() * Math.PI * 2, | |
| g: i < 5 ? 0 : Math.min(0.55, 0.15 + r() * 0.4), | |
| side: r() < 0.5 ? -1 : 1, | |
| tint: Math.floor(r() * 40) - 20, | |
| }; | |
| } | |
| /** The leaf centre (fraction of the width) after t ticks on it. Never stands still, always crosses the drop's column. */ | |
| export function leafX(l, t) { | |
| const x = t / HZ; | |
| const ang = l.ph + l.s * (x + l.g * Math.sin(x * 2.3) / 2.3); | |
| return X0 + l.a * Math.sin(ang); | |
| } | |
| /** Judge a landing: 'perfect' (middle third of the leaf), 'good' (on the leaf) or 'miss'. */ | |
| export function judge(l, t) { | |
| const d = Math.abs(leafX(l, t) - X0); | |
| return d <= l.w / 3 ? 'perfect' : d <= l.w ? 'good' : 'miss'; | |
| } | |
| /** A run: step(tap) once per tick; returns 'perfect' | 'good' | 'miss' | 'dry' | ''. */ | |
| export function makeSim(seed) { | |
| return { | |
| seed, i: 0, t: 0, wait: 0, hop: 0, cur: leaf(seed, 0), marks: [], score: 0, perfect: 0, streak: 0, best: 0, dead: false, last: '', dried: false, | |
| step(tap) { | |
| if (this.dead) return ''; | |
| this.t++; | |
| if (this.wait > 0) { this.wait--; return ''; } | |
| if (this.hop > 0) { | |
| if (--this.hop > 0) return ''; | |
| const res = judge(this.cur, this.t); | |
| this.marks.push(res); this.last = res; | |
| if (res === 'miss') { this.dead = true; return res; } | |
| this.score++; | |
| if (res === 'perfect') { this.perfect++; this.streak++; this.best = Math.max(this.best, this.streak); } else this.streak = 0; | |
| this.i++; this.cur = leaf(this.seed, this.i); this.t = 0; this.wait = SETTLE; | |
| return res; | |
| } | |
| if (tap) { this.hop = HOP; return ''; } | |
| if (this.t > DRY) { this.marks.push('miss'); this.last = 'dry'; this.dried = true; this.dead = true; return 'dry'; } | |
| return ''; | |
| }, | |
| }; | |
| } | |
| /** Emoji rows for the share text: 🟩 perfect, 🟨 good, 🟥 the miss that ended the run (rows of 10). */ | |
| export function grid(marks) { | |
| const e = marks.map((m) => (m === 'perfect' ? '🟩' : m === 'good' ? '🟨' : '🟥')); | |
| const rows = []; | |
| for (let i = 0; i < e.length; i += 10) rows.push(e.slice(i, i + 10).join('')); | |
| return rows.join('\n'); | |
| } | |
| /** Ticks from now (on leaf l at tick t) until a tap would land dead centre: the bot and the fairness tests use it. */ | |
| export function bestTap(l, t, horizon = 4 * HZ) { | |
| let best = null; | |
| for (let k = 0; k < horizon; k++) { | |
| const d = Math.abs(leafX(l, t + k + 1 + HOP) - X0); // the tap tick, then HOP ticks of hop | |
| if (best === null || d < best.d - 1e-9) best = { k, d }; | |
| if (d < l.w / 6) return { k, d }; | |
| } | |
| return best; | |
| } | |