Skip to content

The seeded error's draw, live

A seeded draw's 32 bits for a seed, a step, a key and an index: position_hash(position_hash(step, key, seed), index, seed) (lusine.error.draw). The key is the stream's name hashed, the FNV-1a 32 of its UTF-8, and a game writes it as the name, key = fnv1a("jack"), which the compiler folds to its number at build; two keys draw independently from one seed. Below, the library's draw runs on your GPU as you read: on Boules's jack, each end's draw at four seeds, against the host's seeded error; and each step on two players' streams, "red" and "blue", at sixteen indices from one seed.

This page runs the program on your GPU through WebGPU, so it needs a browser with WebGPU (a recent Chrome, Edge, Safari or Firefox). Without it the program does not run and nothing is drawn below.

What it shows

  • The strip above the controls is green while every check holds, and turns red for good once one fails.
  • Two streams: the chart draws red's and blue's draws at index 0 as uniforms, a point a step: two streams from one seed, going their own ways. Below it, the step, the two uniforms now, and the key for "jack" as the program writes it, 777243802.
  • The checks: Boules's jack, the jack's draw for ends 0 to 5 at seeds 0, 42, 20261005 and 2³² − 1 the host's bits exactly; keys by name, each name's key written as the name the host's FNV-1a 32 of its UTF-8, café and ボール and the empty name among them; all zeros, the one draw that is 0 by construction, every input 0; two keys, two streams, red and blue never drawing the same bits at an index; every index its own, red's sixteen indices sixteen distinct draws; the same draw again, red's draws of the step before made again from the same inputs to the bit. Each is held at every step.

Every number here is computed by the program on the GPU; the page only reads the values back and draws them. The seed is the program's setting, which the host may write; this page offers no control to change it yet.

The program

The jack's cases, the two streams and the checks, the panels the page draws, and the one procedure, main, run each step: the jack's draws, the step's draws of both streams and red's of the step before, and the checks.

// The seeded error's draw's live page: the library's draw held to the host's seeded error's draws of Boules's jack bit
// for bit, its keys written as their names held to the host's FNV-1a, its one zero, and two players' streams drawn
// side by side each step, never sharing a draw, every index its own, and each step's draws made again the next step
// to the bit, with the page's checks computed here on the GPU and shown by the panels the program declares.

namespace draw;

lusine.version version = { { number = 1 } };

// The reader's settings: the seed the players' streams are drawn from, and the one input of the draw that is 0 by
// construction, every input 0.
host struct { integer seed = 20261005; integer zero = 0; } settings;

// Boules's jack: the host's seeded error's draw for each end from 0 to 5 at four seeds, 0 and 2^32 - 1 among them,
// the stream "jack" at index 0, each as its signed 32-bit integer (two's complement: -1 is 2^32 - 1); and the
// library's draw of each.
struct case { host integer seed; host integer end; host integer want; bits got; bool held; };
table<case> jack(capacity = 24) = {
  { seed = 0, end = 0, want = 1771226378 }, { seed = 0, end = 1, want = 1597428039 },
  { seed = 0, end = 2, want = -1600172391 }, { seed = 0, end = 3, want = -1106507608 },
  { seed = 0, end = 4, want = -1043028566 }, { seed = 0, end = 5, want = -1740942070 },
  { seed = 42, end = 0, want = 183509395 }, { seed = 42, end = 1, want = -714712735 },
  { seed = 42, end = 2, want = 1366847977 }, { seed = 42, end = 3, want = 1842301771 },
  { seed = 42, end = 4, want = -1348388786 }, { seed = 42, end = 5, want = 1876875154 },
  { seed = 20261005, end = 0, want = -63416339 }, { seed = 20261005, end = 1, want = 173216405 },
  { seed = 20261005, end = 2, want = -256339728 }, { seed = 20261005, end = 3, want = 467322749 },
  { seed = 20261005, end = 4, want = 368635456 }, { seed = 20261005, end = 5, want = -1167803152 },
  { seed = -1, end = 0, want = -145311654 }, { seed = -1, end = 1, want = -583366062 },
  { seed = -1, end = 2, want = -768620411 }, { seed = -1, end = 3, want = 2112931551 },
  { seed = -1, end = 4, want = -2121031276 }, { seed = -1, end = 5, want = -1267980913 },
};

// Two players' streams, "red" and "blue", drawn at indices 0 to 15 each step from one seed; red's draws of the step
// before, and red's draws of the step before made again this step.
struct lane {
  integer index; bits red; bits blue; float red_uniform; float blue_uniform; bits before; bits again; integer twins;
  bool shared;
};
table<lane> lanes(capacity = 16) = {
  { index = 0 }, { index = 1 }, { index = 2 }, { index = 3 }, { index = 4 }, { index = 5 }, { index = 6 },
  { index = 7 }, { index = 8 }, { index = 9 }, { index = 10 }, { index = 11 }, { index = 12 }, { index = 13 },
  { index = 14 }, { index = 15 },
};

// The step; red's and blue's uniforms at index 0, for the chart; and the key "jack" as the program writes it.
struct { integer step; float red; float blue; bits jack; } streams;

// The checks: Boules's jack the host's draws exactly; each name's key, written as the name, the host's FNV-1a 32 of
// its UTF-8 (names in other scripts among them); the one draw that is 0 by construction, every input
// 0; and at every step: red and blue never drawing the same bits at an index, red's sixteen indices sixteen distinct
// draws, and red's draws of the step before made again to the bit.
struct { bool jack = true; bool keys = true; bool zero = true; bool streams = true; bool indices = true;
         bool again = true; } checks;

// A strip of lamps the page draws, lit green while every check holds and red once one fails.
struct lamp { vector<float, 4> colour; };
lattice<lamp, 2> lamps(spacing = 1 m, boundary = wall);

vector<float, 4> lamp_colour(bool holds)
{
  return holds ? vector<float, 4>(0.15, 0.6, 0.3, 1.0) : vector<float, 4>(0.75, 0.15, 0.15, 1.0);
}

bool matches(bits got, integer want) { return got == bits(want); }
integer missed(table<bool> held) { return count(held) where !held; }

bits jack_draw(integer seed, integer end)
{
  return lusine.error.draw(seed = bits(seed), step = bits(end), key = fnv1a("jack"), index = bits(0));
}

bits red(integer seed, integer step, integer index)
{
  return lusine.error.draw(seed = bits(seed), step = bits(step), key = fnv1a("red"), index = bits(index));
}

bits blue(integer seed, integer step, integer index)
{
  return lusine.error.draw(seed = bits(seed), step = bits(step), key = fnv1a("blue"), index = bits(index));
}

// The host's FNV-1a 32 of each name, as the host computes it, each as its signed 32-bit integer.
bool keys()
{
  return fnv1a("jack") == bits && fnv1a("red") == bits
      && fnv1a("blue") == bits && fnv1a("jack/normal") == bits(68526392)
      && fnv1a("café") == bits && fnv1a("ボール") == bits
      && fnv1a("") == bits;
}

bool zero(integer seed) { return lusine.error.draw(seed = bits(seed), step = bits(seed), key = bits(seed),
                                                   index = bits(seed)) == bits(0); }

// How many of red's draws this step are this lane's: one, its own, where every index draws its own bits.
integer twins(table<bits> every, bits mine)
{
  let drawn = mine;
  return count(every) where every == drawn;
}

integer shared(table<bool> shared) { return count(shared) where shared; }
integer lone(table<integer> twins) { return count(twins) where twins == 1; }
integer kept(table<bits> again, table<bits> before) { return count(again) where again == before; }

float first_of(table<float> uniform, table<integer> index) { return sum(uniform) where index == 0; }

output table<lusine.widget> panels = {
  lusine.series { { panel = "two streams", chart = "index 0", quantity = streams.red, steps = 120, label = "red" } },
  lusine.series { { panel = "two streams", chart = "index 0", quantity = streams.blue, steps = 120,
                    label = "blue" } },
  lusine.value { { panel = "two streams", quantity = streams.step, label = "step" } },
  lusine.value { { panel = "two streams", quantity = streams.red, label = "red's uniform" } },
  lusine.value { { panel = "two streams", quantity = streams.blue, label = "blue's uniform" } },
  lusine.value { { panel = "two streams", quantity = streams.jack, label = "the key for jack" } },
  lusine.value { { panel = "the checks", quantity = checks.jack, label = "Boules's jack" } },
  lusine.value { { panel = "the checks", quantity = checks.keys, label = "keys by name" } },
  lusine.value { { panel = "the checks", quantity = checks.zero, label = "all zeros" } },
  lusine.value { { panel = "the checks", quantity = checks.streams, label = "two keys, two streams" } },
  lusine.value { { panel = "the checks", quantity = checks.indices, label = "every index its own" } },
  lusine.value { { panel = "the checks", quantity = checks.again, label = "the same draw again" } },
};

void main()
{
  jack.got = jack_draw(seed = jack.seed, jack.end);
  jack.held = matches(jack.got, jack.want);
  streams.step = streams.step + 1;
  lanes.before = lanes.red;
  lanes.again = red(settings.seed, step = streams.step - 1, lanes.index);
  lanes.red = red(settings.seed, streams.step, lanes.index);
  lanes.blue = blue(settings.seed, streams.step, lanes.index);
  lanes.shared = lanes.red == lanes.blue;
  lanes.twins = twins(every = lanes.red, mine = lanes.red);
  lanes.red_uniform = lusine.error.uniform(drawn = lanes.red);
  lanes.blue_uniform = lusine.error.uniform(drawn = lanes.blue);
  streams.red = first_of(uniform = lanes.red_uniform, lanes.index);
  streams.blue = first_of(uniform = lanes.blue_uniform, lanes.index);
  streams.jack = fnv1a("jack");
  checks.jack = checks.jack && missed(jack.held) == 0;
  checks.keys = checks.keys && keys();
  checks.zero = checks.zero && zero(seed = settings.zero);
  checks.streams = checks.streams && shared(lanes.shared) == 0;
  checks.indices = checks.indices && lone(lanes.twins) == 16;
  checks.again = checks.again && (streams.step == 1 || kept(lanes.again, lanes.before) == 16);
  lamps.colour = lamp_colour(holds = checks.jack && checks.keys && checks.zero && checks.streams && checks.indices
                                     && checks.again);
}

The draw's other proof (twenty thousand draws against the host's, the edges of the 32-bit range, a planted fault) is on its proof page.