clone

a takuzu puzzle field for agents to explore tile by tile, with a referee and a replayable spectator view

main
mosaic / site / scripts / push-day.ts
push-day.ts 2.9K
1// Generate a day's field on this machine and push it to the hosted daily:2//3// deno task push-day 2026-08-28 [--host https://mosaic-daily.fly.dev]4// [--chunks 3] [--chunk-g 60] [--G 1000] [--rings 2] [--namespace mosaic-daily]5// deno task push-day --tomorrow ... (the nightly machine: tomorrow, UTC)6//7// The token comes from MOSAIC_PUSH_TOKEN.8// The field parameters must match the hosted daily's configuration. The9// day seed is drawn at random here and travels inside the pushed field10// (day_seed); the daily keeps it private until the day is archived.11import { gen } from "../lib/engine.ts";12import { randomSeed } from "../lib/daily.ts";1314const args = Object.fromEntries(15 Deno.args.map((a, i, all) =>16 a.startsWith("--") ? [a.slice(2), all[i + 1] ?? ""] : []17 ).filter((p) => p.length),18);19const date = Deno.args.includes("--tomorrow")20 ? new Date(Date.now() + 24 * 3600 * 1000).toISOString().slice(0, 10)21 : Deno.args.find((a) => /^\d{4}-\d{2}-\d{2}$/.test(a));22if (!date) {23 console.error(24 "usage: deno task push-day YYYY-MM-DD|--tomorrow [--host URL] [--rings N|all] [--jobs N] [--band N]",25 );26 Deno.exit(2);27}28const host = (args.host ?? "https://mosaic-daily.fly.dev").replace(/\/$/, "");29const cfg = {30 namespace: args.namespace ?? "mosaic-daily",31 chunks: Number(args.chunks ?? 3),32 chunk_g: Number(args["chunk-g"] ?? 60),33 G: Number(args.G ?? 1000),34 rings: args.rings === "all" ? "all" as const : Number(args.rings ?? 2),35 grow_minutes: 60,36 rollover_hour_utc: 0,37 fog: true,38 generate: true,39 prepare: true,40 prepare_hour_utc: 20,41 jobs: Number(args.jobs ?? 2),42 band: Number(args.band ?? 24),43 emergency: { chunks: 3, chunk_g: 60, rings: 1 },44};45function token(): string {46 const env = Deno.env.get("MOSAIC_PUSH_TOKEN");47 if (env && env.trim()) return env.trim();48 throw new Error("no MOSAIC_PUSH_TOKEN in the environment");49}50// the day seed is random and private (the field is regenerable from it);51// its commitment is what the manifest publishes52const seed = randomSeed();53console.log(54 `generating ${date} (${cfg.chunks}x${cfg.chunks} of ${cfg.chunk_g}, ${cfg.rings} rings ahead on G=${cfg.G})`,55);56const t0 = Date.now();57const out = await gen({58 seed,59 G: cfg.G,60 kind: "field",61 chunks: cfg.chunks,62 chunk_g: cfg.chunk_g,63 field_g: cfg.G,64 rings: cfg.rings,65 jobs: cfg.jobs,66 band: cfg.band,67});68const { rings = [], ...base } = out;69const body = JSON.stringify({ base, rings });70console.log(71 `generated in ${Math.round((Date.now() - t0) / 1000)} s: ${72 (base.manifest as { tiles: unknown[] }).tiles.length73 } tiles + ${rings.length} rings, ${74 (body.length / 1e6).toFixed(1)75 } MB; pushing to ${host}`,76);77const res = await fetch(`${host}/push/${date}`, {78 method: "POST",79 headers: {80 "Authorization": `Bearer ${token()}`,81 "Content-Type": "application/json",82 },83 body,84});85console.log(res.status, await res.text());86if (!res.ok) Deno.exit(1);