code wiki / _hdl_build / nx_sudoku_core.nx
nx_sudoku_core.nx
buildroot/runtime/_hdl_build/nx_sudoku_core.nx
about
nx_sudoku_core.nx -- LIB (no main): the ONE sudoku algorithm in the estate, written BASE-RELATIVE
so the identical code runs natively (base = an mmap'd buffer) and inside our WebAssembly (base = 0).
WHY BASE-RELATIVE. The estate's wasm game contract (nx_wasm_2048, nx_wasm_craft) is that a game is
pure integer logic over a flat memory image addressed as base+offset. A module written that way needs
no allocator, no syscalls and no host cooperation, so the SAME source is the native binary AND the
browser module. The alternative -- one copy in NishiLang for the tool and another in JavaScript for
the page -- is two implementations of one ruleset, which is the duplicate-ruler defect the estate
keeps paying for. There is exactly ONE solver, ONE counter and ONE carver here, and both consumers
(nx_sudoku for banks, nx_sudoku_wasm for play) compose them.
THE LOAD-BEARING PROPERTY: uniqueness is COUNTED, never assumed. sc_carve empties a cell only when
sc_count still returns exactly 1, and sc_count saturates at 2 because "is it still unique" only ever
needs to separate 1 from more-than-1. The clue floor is 17 -- McGuire, Tugemann & Civario (2012)
proved exhaustively that no 16-clue sudoku has a unique solution, so a request for fewer is CLAMPED
rather than honoured: asking for 16 asks for something that provably does not exist.
Deterministic: the PRNG is a seeded LCG held in the memory image, so a given seed reproduces a given
puzzle exactly. A generator you cannot re-run is not one you can test.
license_tier: ORIGINAL No hw writes (Rule 26).
dependencies 0 imports · 2 importers
imports: none
imported by: nx_sudoku.nxnx_sudoku_wasm.nx
structs
| none |
consts
| 23 | const SC_G: i64 = 0 // 81 i64 the working / puzzle grid |
| 24 | const SC_SOL: i64 = 648 // 81 i64 its full solution |
| 25 | const SC_WORK: i64 = 1296 // 81 i64 scratch grid for counting |
| 26 | const SC_ORD: i64 = 1944 // 81 i64 removal order |
| 27 | const SC_SHUF: i64 = 2592 // 81*9 per-depth candidate shuffle (never allocated in the hot loop) |
| 28 | const SC_RNG: i64 = 8424 // 1 i64 LCG state |
| 29 | const SC_SIZE: i64 = 8432 // total bytes a caller must provide |
| 31 | const SC_CLUE_FLOOR: i64 = 17 |
| 32 | const SC_LCG_A: i64 = 1103515245 |
| 33 | const SC_LCG_C: i64 = 12345 |
| 34 | const SC_LCG_M: i64 = 2147483648 |
functions
| 37 | func sc_get(base: i64, off: i64, i: i64) -> i64 { let p: *i64 = (base+off) as *i64; return p[i] } |
| 38 | func sc_put(base: i64, off: i64, i: i64, v: i64) -> i64 { let p: *i64 = (base+off) as *i64; p[i]=v; return 0 } |
| 39 | func sc_copy(base: i64, src: i64, dst: i64) -> i64 |
| 44 | func sc_clear(base: i64, off: i64) -> i64 |
| 49 | func sc_clues(base: i64, off: i64) -> i64 |
| 57 | func sc_seed(base: i64, s: i64) -> i64 |
| 63 | func sc_next(base: i64) -> i64 |
| 70 | func sc_below(base: i64, n: i64) -> i64 { if n<=0 { return 0 } return sc_next(base)%n } |
| 74 | func sc_ok(base: i64, off: i64, i: i64, v: i64) -> i64 |
| 96 | func sc_fill(base: i64, off: i64, i: i64) -> i64 |
| 124 | func sc_count(base: i64, off: i64, i: i64, n: i64) -> i64 |
| 144 | func sc_carve(base: i64, clues: i64) -> i64 |
| 179 | func sc_gen(base: i64, clues: i64, seed: i64) -> i64 |
| 196 | func sc_unavoidable(base: i64, off: i64, out: *i64) -> i64 |