@Matticus
Was unable to complete my task.
The little diagrams show it should be possible (?); Collapse each value at X, then compute the masks for each of them. Reduce with & the entirety of the masks with the state.
import "lib/github.com/diku-dk/cpprandom/random"
module d = uniform_int_distribution i64 minstd_rand
def iota_u8 k = map (u8.i64) (iota k)
-- https://futhark-lang.org/examples/searching.html
def find_index 'a [n] (p: a -> bool) (as: [n]a): i64 =
let op (x, i) (y, j) =
if x && y then if i < j
then (x, i)
else (y, j)
else if y then (y, j)
else (x, i)
in (reduce_comm op (false, -1) (zip (map p as) (iota n))).1
-- Indexing this with [box, iteration] fetches the (x, y) value we should utilize.
let LUT = [[(0i64, 0i64), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2)],
[(3, 1), (4, 1), (5, 1), (3, 2), (4, 2), (5, 2), (3, 0), (4, 0), (5, 0)],
[(6, 2), (7, 2), (8, 2), (6, 0), (7, 0), (8, 0), (6, 1), (7, 1), (8, 1)],
[(1, 3), (2, 3), (0, 4), (1, 4), (2, 4), (0, 5), (1, 5), (2, 5), (0, 3)],
[(4, 4), (5, 4), (3, 5), (4, 5), (5, 5), (3, 3), (4, 3), (5, 3), (3, 4)],
[(7, 5), (8, 5), (6, 3), (7, 3), (8, 3), (6, 4), (7, 4), (8, 4), (6, 5)],
[(2, 6), (0, 7), (1, 7), (2, 7), (0, 8), (1, 8), (2, 8), (0, 6), (1, 6)],
[(5, 7), (3, 8), (4, 8), (5, 8), (3, 6), (4, 6), (5, 6), (3, 7), (4, 7)],
[(8, 8), (6, 6), (7, 6), (8, 6), (6, 7), (7, 7), (8, 7), (6, 8), (7, 8)]]
def collapse state rng =
let (rng, random) = d.rand(0, 8) rng
let valid_indices = map2 (*) state
-- Range from 1..9; 0..8 will result in the first index always being '0' which is a null.
(map (+ 1) (iota_u8 9))
|> rotate random
let index = foldl(\a b -> if a > 0 then a else b) 0 valid_indices
in (rng, (i64.u8 (index - 1))) -- Undo the above (+ 1).
def mask index x y =
-- Given a position (u, v), compute which sudoku box we're within.
let box u v = -- 0 1 2
if u < 3 then -- 3 4 5
if v < 3 then 0 -- 6 7 8
else if v < 6 then 3
else 6
else if u < 6 then
if v < 3 then 1
else if v < 6 then 4
else 7
else
if v < 3 then 2
else if v < 6 then 5
else 8
-- Return something akin to [1, 1, 1, 1, 0, 1, 1, 1, 1].
let mask_at_index k =
map2 (^) (replicate 9 1u8) (rotate (9 - k) [1u8, 0, 0, 0, 0, 0, 0, 0, 0])
in let mask_fn u v =
if x == u && y == v -- Collapsed this state.
then (rotate (9 - index) [1u8, 0, 0, 0, 0, 0, 0, 0, 0])
else if x == u || y == v -- Perpendicular lines with the origin at (x, y).
then mask_at_index index
else if box x y == box u v -- Resides in the same sudoku box.
then mask_at_index index
else
replicate 9 1u8
in map (\v -> map (\u -> mask_fn u v) (iota 9)) (iota 9)
def wfc seed =
let rng = minstd_rand.rng_from_seed [seed]
let rngs = minstd_rand.split_rng 9 rng
let initial_state = replicate 9 (replicate 9 (replicate 9 1u8))
let (_, state, _) =
loop (rngs, state, acc) = (rngs, initial_state, 0) for acc < 9 do
let coords = map (\box -> LUT[box, acc]) (iota 9) -- fetch the indices to collapse.
let (rngs, indices) = map2 (\(x, y) rng -> collapse state[x, y] rng) coords rngs |> unzip
let masks = map2 (\index (x, y) -> mask index x y) indices coords
-- TODO: Rather than flatten and coalesce to a size at runtime, map (&) over [9][9][9].
let flattened_masks = map (\m -> flatten_3d m :> [729]u8) masks
let flattened_state = flatten_3d state :> [729]u8
let flattened_state = reduce_comm (map2 (&)) flattened_state flattened_masks
let state = unflatten_3d 9 9 9 flattened_state
in (rngs, state, acc + 1)
-- Find the first non-zero value within each state.
in map (\x -> map (\y -> find_index (\a -> a != 0) state[x, y]) (iota 9)) (iota 9)
in a REPL
[16]> collapse [1, 0, 1, 1, 1, 0, 0, 0, 0] rng
(1946104754u32, 0i64)
[17]> collapse [1, 0, 1, 1, 1, 1, 0, 0, 0] rng
(1946104754u32, 5i64)
[20]> mask 0 0 0
[[[1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8],
[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8]],
[[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8]],
[[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8]],
[[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8]],
[[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8]],
[[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8]],
[[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8]],
[[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8]],
[[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8],
[1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8, 1u8]]]
The compiler is telling me acc is unused, which doesn't make any sense to me...
futhark check wfc.fut
Warning at wfc.fut:76:60-62:
Unused variable "acc" .
I have been bested.