Something went wrong. Try again.
A Takuzu game native to the AT Protocol. atkuzu.tomplanche.com
binairo atproto game takuzu
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106/** * reveal-solution.ts: given ATKUZU_DAILY_SEED, reproduces a daily's solution for a * (date, size) and shows whether it matches the published solutionSha256. * * `solutionSha256` is a SHA-256 hash, not a cipher: it cannot be "decrypted" back into a * grid. What this script does instead is the practical equivalent: the puzzle's seed is * deterministic (see generate-daily.ts's deriveSeed), so re-running the same generator with * the same master seed reproduces the exact same solution byte-for-byte, and hashing that * gives you `solutionSha256` back. Same trick as generate-daily.ts, run for one puzzle * instead of a whole day's archive, without writing anything. * * Requires: * ATKUZU_DAILY_SEED master seed for the daily archive (any string) * * Usage: * pnpm run daily:reveal -- <YYYY-MM-DD> <6|8|12> */import "dotenv/config";import { execFileSync } from "node:child_process";import { createHash } from "node:crypto";import { existsSync, readFileSync } from "node:fs";import { fileURLToPath } from "node:url";import { dirname, resolve } from "node:path";import { BOARD_SIZES, type BoardSize, chunkRows } from "../src/lib/game/board";import type { DailyFile } from "../src/lib/game/daily";
const HERE = dirname(fileURLToPath(import.meta.url));const GENERATOR_DIR = resolve(HERE, "../takuzu-grid-factory");const GENERATOR_BIN = resolve(GENERATOR_DIR, "target/release/takuzu-grid-factory");const ARCHIVE_DIR = resolve(HERE, "../static/daily");
const masterSeed = process.env.ATKUZU_DAILY_SEED;if (!masterSeed) { console.error("Set ATKUZU_DAILY_SEED (master seed for the daily archive) to reveal."); process.exit(1);}
// pnpm doesn't always strip the `--` separator before forwarding args (unlike npm), so guard// against it showing up as a literal argument here.const [dateArg, sizeArg] = process.argv.slice(2).filter((arg) => arg !== "--");const date = dateArg;const size = Number(sizeArg) as BoardSize;
if (!date || !/^\d{4}-\d{2}-\d{2}$/.test(date) || !BOARD_SIZES.includes(size)) { console.error(`Usage: pnpm run daily:reveal -- <YYYY-MM-DD> <${BOARD_SIZES.join("|")}>`); process.exit(1);}
if (!existsSync(GENERATOR_BIN)) { console.log("building takuzu-grid-factory (release)..."); execFileSync("cargo", ["build", "--release"], { cwd: GENERATOR_DIR, stdio: "inherit" });}
/** Identical to generate-daily.ts's deriveSeed: must match to reproduce the same solution. */const deriveSeed = (d: string, s: BoardSize): bigint => { const digest = createHash("sha256").update(`${masterSeed}:${d}:${s}`).digest("hex");
return BigInt(`0x${digest.slice(0, 16)}`);};
const seed = deriveSeed(date, size);
const output = execFileSync(GENERATOR_BIN, ["--size", String(size), "--seed", seed.toString()], { encoding: "utf8"});const lines = output.split("\n");
const readBlock = (heading: string): string => { const start = lines.indexOf(heading) + 1; if (start === 0) { throw new Error(`generator output has no "${heading}" block:\n${output}`); }
return lines.slice(start, start + size).join("");};
const solution = readBlock("solution");const solutionSha256 = createHash("sha256").update(solution).digest("hex");
console.log(`date: ${date}`);console.log(`size: ${size}x${size}`);console.log(`seed: ${seed}`);console.log(`sha256: ${solutionSha256}`);console.log("solution:");for (const row of chunkRows(solution, size)) { console.log(` ${row}`);}
const archivePath = resolve(ARCHIVE_DIR, `${date}.json`);if (!existsSync(archivePath)) { console.log(`\n(no local ${archivePath} to compare against)`); process.exit(0);}
const archive: DailyFile = JSON.parse(readFileSync(archivePath, "utf8"));const published = archive.puzzles.find((p) => p.size === size);
if (!published) { console.log(`\n(no ${size}x${size} entry in ${archivePath})`);} else if (published.solutionSha256 === solutionSha256) { console.log(`\n✓ matches solutionSha256 published in ${archivePath}`);} else { console.log(`\n✗ does NOT match ${archivePath}'s solutionSha256 (${published.solutionSha256})`); console.log(" ATKUZU_DAILY_SEED is probably not the one this archive was generated with.");}