Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d21f483
t27f: idea factory -- any idea in any style preset, every element a p…
gHashTag Oct 19, 2026
2c619fd
t27f skill: idea factory and the particle design system, preset sweep…
gHashTag Oct 19, 2026
5e07615
t27f: Russian ideas in the factory -- UTF-8 and a 5x7 Latin and Cyril…
gHashTag Oct 19, 2026
ba365d1
Merge master into claude/t27f-cyrillic; list five t27b-pass specs in …
gHashTag Oct 19, 2026
9ec9e29
t27f skill: a new spec's t27b ledger row comes from the arm64 run, ke…
gHashTag Oct 19, 2026
8efa4bc
Merge branch 'master' of github.com:gHashTag/t27 into claude/t27f-cyr…
gHashTag Oct 19, 2026
dae06a9
Ledger: glyph57 and petal_menu pass the t27b front end (Refs #10788)
gHashTag Oct 19, 2026
29f8511
t27f: idea factory picks its style preset from a hash of the idea, PR…
gHashTag Oct 19, 2026
7f33764
Ledger rows for mos_psp103 and re-sealed frontend, both inherited fro…
gHashTag Oct 19, 2026
549fe2c
Merge branch 'claude/t27f-cyrillic' into claude/t27f-preset-hash
gHashTag Oct 19, 2026
d5dc2c9
Merge remote-tracking branch 'origin/master' into claude/t27f-cyrillic
gHashTag Oct 19, 2026
4f739c1
Ledger row for sar_switch, another master spec landed without one (Re…
gHashTag Oct 19, 2026
4d5b412
Merge branch 'claude/t27f-cyrillic' into claude/t27f-preset-hash
gHashTag Oct 19, 2026
f8c3822
Merge remote-tracking branch 'origin/master' into claude/t27f-cyrillic
gHashTag Oct 19, 2026
fe4fd88
Ledger: sar_switch sorts after sar_comparator (Refs #10788)
gHashTag Oct 19, 2026
2b94b35
Merge remote-tracking branch 'origin/claude/t27f-cyrillic' into claud…
gHashTag Oct 19, 2026
e1d344e
Merge remote-tracking branch 'origin/master' into claude/t27f-cyrillic
gHashTag Oct 19, 2026
b1312de
Merge branch 'claude/t27f-cyrillic' into claude/t27f-preset-hash
gHashTag Oct 19, 2026
20b5464
t27b ledger: master's rows plus this lane's glyph57 row, so the branc…
gHashTag Oct 19, 2026
18c6bd9
t27f: take master's idea_factory seal so master merges in cleanly; re…
gHashTag Oct 19, 2026
7828cff
Merge branch 'master' into claude/t27f-preset-hash
gHashTag Oct 19, 2026
3a1dd4e
t27f: re-save the idea_factory seal after merging master (Refs #10803)
gHashTag Oct 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .claude/skills/t27f/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,12 @@ of particles, and runnable in the OS itself and on FPGA. Two specs do this:
- **`specs/t27f/idea_factory.t27` is the factory**, `(IDEA, PRESET) -> RGB24 frames`. It wraps
(at most 9 characters per line, 4 lines per scene, `|` cuts a scene), lays out in glyph57's 5x7 font,
assigns particles to glyph sub-slots and the card border, and closes on `CTA`.
- **A batch of ideas: set `PRESET = PRESET_AUTO`** (255). `main` runs `pick_preset(IDEA, PRESET)`,
and AUTO means `preset_of(IDEA)` = FNV-1a 32 of the idea's bytes mod `PRESETS`. The same idea
always gets the same style, and different ideas spread across all five; A..Z hit every preset.
This is Remotion's `random(seed)` applied to style. Any other `PRESET` value is used as given
(gHashTag/t27#10803). Test hashes against the published FNV vectors (`""`, `a`, `b`, `foobar`),
never against a value the spec computes itself.

**Keep it FPGA-shaped.** Use Q8 fixed point. The per-step and per-pixel paths use only add,
multiply, compare, table lookup (`kt` kernel ROM, `tt` tone ROM) and power-of-two division.
Expand Down
18 changes: 9 additions & 9 deletions .trinity/seals/t27f_idea_factory.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"built_by": "t27c-bootstrap@0.5.2+355d44a39",
"built_by": "t27c-bootstrap@0.5.2+ae19b93cd",
"closure": [
{
"spec": "specs/t27f/glyph57.t27",
Expand All @@ -15,23 +15,23 @@
}
],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:6a6ffcf30791e08b93221c1067abfc7e053f13c23dff90de4d9f7c9e23461479",
"gen_hash_rust": "sha256:d64150e24319f74e2b9413d45cfbd96a7d8bbc0df9d51c9fac0b68c8837b2efb",
"gen_hash_verilog": "sha256:387961ee44ce4ce7f32d4ccb23a4ac58ac6ef688db3b4c8aef38d1c60e41cc18",
"gen_hash_zig": "sha256:115d3f8ff262b6fd65b59d760aeb7eff66500b9f77f56577fb22233fefaa65e7",
"gen_hash_c": "sha256:ddc41e93c87c1268b2d075c6b34fba3208ac77bc2db7992ee9430b219f3aa2f1",
"gen_hash_rust": "sha256:dfb079933e8ffe3343e2360a23e71f9a92bc7c27b20a2e11c6659f95e1562c63",
"gen_hash_verilog": "sha256:029af4d7206071497f23fd5b12d15eb47df379d6f508d2f9a192edfb12337578",
"gen_hash_zig": "sha256:27984b39231077b1f334e7d6fe2f2249667ffb6838f0bf21f4b8e38861e98fd7",
"module": "idea_factory",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-19T02:41:09Z",
"sealed_at": "2026-10-19T06:20:13Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:249909fbd1679b488be14be323c0079735a55895560423934c21daf28c824db9",
"spec_hash": "sha256:26ab3c6cae74c0c62124efb321098e184703f4b05f1e53fc902fa209dd036768",
"spec_path": "specs/t27f/idea_factory.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 10,
"total": 10,
"passed": 13,
"total": 13,
"vacuous": 0
},
"toolchain": {
Expand Down
73 changes: 72 additions & 1 deletion specs/t27f/idea_factory.t27
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ use t27f::scene_raster::red;
use t27f::scene_raster::green;
use t27f::scene_raster::blue;
use t27f::particle_ds::ONE;
use t27f::particle_ds::PRESETS;
use t27f::particle_ds::BG;
use t27f::particle_ds::KEY_COL;
use t27f::particle_ds::GRAIN;
Expand Down Expand Up @@ -272,6 +273,32 @@ pub fn frame_bytes() -> i32 {
return FRAME_W * FRAME_H * 3;
}

// ---- style: the preset can come from the idea itself (gHashTag/t27#10803) ----

// PRESET = PRESET_AUTO asks the factory to pick the style from the idea's bytes, so a batch of
// ideas spreads over the presets and a re-render of one idea keeps its style.
pub const PRESET_AUTO : usize = 255;
pub const FNV32_BASIS : u32 = 2166136261;
pub const FNV32_PRIME : u32 = 16777619;

// FNV-1a 32 over the bytes of t (UTF-8 for a Russian idea): xor the byte in, then multiply.
pub fn fnv1a32(t: []const u8) -> u32 {
var h : u32 = FNV32_BASIS;
var i : usize = 0;
while (i < t.len) {
h = (h ^ @as(u32, t[i])) *% FNV32_PRIME;
i = i + 1;
}
return h;
}
pub fn preset_of(t: []const u8) -> usize {
return @as(usize, fnv1a32(t) % @as(u32, PRESETS));
}
pub fn pick_preset(t: []const u8, p: usize) -> usize {
if (p == PRESET_AUTO) { return preset_of(t); }
return p;
}

// ---- assign ----

// Where card particle m of mm sits on the border of the rectangle (x0, y0)-(x1, y1): what 0 = x,
Expand Down Expand Up @@ -329,7 +356,7 @@ pub fn main() -> void {
var accb : [1080]i32 = undefined;
var row : [3240]u8 = undefined;

const p = PRESET;
const p = pick_preset(IDEA, PRESET);
const rr = reach(p);
var d2 : i32 = 0;
while (d2 <= rr * rr) {
Expand Down Expand Up @@ -633,3 +660,47 @@ test "a frame is 1080x1920 RGB24, 6220800 bytes" {
assert(frame_bytes() == 6220800);
assert(FPS == 30);
}

// Expected values are the published FNV-1a 32 test vectors, not values this file computes.
test "fnv1a32 gives the published FNV-1a 32 vectors for the empty string, a, b and foobar" {
assert(fnv1a32("") == 2166136261);
assert(fnv1a32("a") == 3826002220);
assert(fnv1a32("b") == 3876335077);
assert(fnv1a32("foobar") == 3214735720);
}

test "PRESET_AUTO picks the preset from the idea's hash, any other preset is kept" {
assert(PRESET == 0);
assert(PRESET_AUTO >= PRESETS);
assert(preset_of("a") == 0);
assert(preset_of("b") == 2);
assert(pick_preset("b", PRESET_AUTO) == 2);
assert(pick_preset("b", 3) == 3);
assert(pick_preset("a", 4) == 4);
assert(pick_preset(IDEA, PRESET_AUTO) == preset_of(IDEA));
assert(preset_of(RU_IDEA[0..80]) < PRESETS);
}

test "the 26 one-letter ideas A to Z between them pick every preset" {
var hit : [5]i32 = undefined;
var k : usize = 0;
while (k < 5) {
hit[k] = 0;
k = k + 1;
}
var b : [1]u8 = undefined;
var c : u8 = 65;
while (c <= 90) {
b[0] = c;
const p = preset_of(b[0..1]);
assert(p < PRESETS);
hit[p] = hit[p] + 1;
c = c + 1;
}
assert(hit[0] > 0);
assert(hit[1] > 0);
assert(hit[2] > 0);
assert(hit[3] > 0);
assert(hit[4] > 0);
assert(hit[0] + hit[1] + hit[2] + hit[3] + hit[4] == 26);
}
Loading