song.jsannotatedsong.jssource390 lines · 29.1 KB · raw

ALL GREEN — chiptune synth-pop in 5/4, 120 bpm: JevLM cheats at Wordle. Spec: SPEC.md Jev never writes a word. JevLM (Krug2/JevLM-Open) makes it write by asking one choice per fragment: every bank of up to 255 options, then the finalists. Here each guess is one such choice, and the options are only the words that still fit the board: the cheat is in the option list. Start from STOPPED: every section counts cycles from 0.

7setcps(120/60/5)   // one cycle is one bar of 5/4: five beats at 120 bpm, 2.5 s
8await samples('github:tidalcycles/dirt-samples')
9await samples('jev-samples/strudel.json')

── the words: Wordle's 2,309 answers, alphabetical (SPEC.md, The word list) ── Front-coded: each word is a digit (the letters it shares with the word before) and the letters after them.

14const PACKED = '0aback3se3te2bey3ot2hor2ide2led2ode3rt3ut3ve2use2yss1corn2rid2tor2ute1dage3pt2ept2min4t2obe3pt3re4n2ult1ffix2ire2oot3ul2ter1gain3pe3te2ent2ile3ng2low2ony2ree1head1ider2sle1larm2bum2ert2gae2ibi3en3gn3ke3ve2lay3ey3ot4w4y2oft3ne4g3of3ud2pha2tar3er1mass3ze2ber3le2end2iss3ty2ong2ple4y2use1ngel4r3le3ry3st2ime2kle2nex3oy3ul2ode2tic2vil1orta1part2hid2ing2nea2ple4y2ron2tly1rbor2dor2ena2gue2ise2mor2oma3se2ray3ow2son2tsy1scot2hen2ide2kew2say3et1toll3ne2tic1udio4t2gur2nty1vail2ert2ian2oid1wait3ke3rd4e3sh2ful2oke1xial3om4n1zure0bacon2dge3ly2gel3gy2ker2ler3my2nal3jo2rge3on2sal3ic4l4n4s3te2tch3he3on3ty2wdy2you1each3dy3rd3st2ech3fy2fit2gan4t3et3in3un2ing2lch3ie3le4y3ow2nch2ret3ry3th2set2tel2vel2zel1ible2cep2ddy2got2lge3ly2nge4o2ome2rch3th2son2tty1lack3de3me3nd4k3re3st3ze2eak4t3ed4p3nd3ss2imp3nd4k3ss3tz2oat3ck3ke3nd3od4m3wn2uer3ff3nt3rb4t3sh1oard3st2bby2ney3go3us2oby3st3th4y3ze4y2rax3ne2som3sy2tch2ugh3le3nd2wel2xer1race3id4n3ke3nd3sh4s3ve4o3wl4n2ead4k3ed2iar3be3ck3de3ef3ne4g4k4y3sk2oad3il3ke3od4k4m3th3wn2unt3sh3te1uddy3ge2ggy3le2ild4t2lge3ky3ly2nch3ny2rly3nt3st2sed3hy2tch3te2xom2yer1ylaw0cabal3by3in3le2cao3he3ti2ddy3et2gey2irn2mel4o2nal3dy3ny3oe4n2per3ut2rat3go3ol3ry3ve2ste2tch3er3ty2ulk3se2vil1ease2dar2llo1hafe4f3in4r3lk3mp3nt3os3rd4m4t3se4m2eap4t3ck3ek4r3ss4t2ick3de3ef3ld4i4l3me3na3rp2ock3ir3ke3rd4e3se2uck3mp3nk3rn3te1ider2gar2nch2rca2vic4l1lack3im3mp3ng4k3sh4p4s2ean4r4t3ft3rk2ick3ff3mb3ng4k2oak3ck3ne3se3th3ud4t3ve3wn2uck3ed3mp3ng1oach3st2bra2coa2lon4r2met3fy3ic3ma2nch3do3ic2pse2ral3er3ny2uch3gh3ld3nt3pe3rt2ven4r4t4y2wer2yly1rack3ft3mp3ne4k3sh4s3te3ve3wl3ze4y2eak4m3do3ed4k4p3me3pe4t3ss4t2ick3ed4r3me4p3sp2oak3ck3ne4y3ok3ss3up3wd4n2ude3el3mb4p3sh4t2ypt1ubic2min2rio3ly3ry3se3ve4y2tie1yber2cle2nic0daddy2ily3ry3sy2lly2nce3dy2tum2unt1ealt3th2bar3it3ug4t2cal4y3or4y3ry2fer2ign3ty2lay3ta3ve2mon3ur2nim3se2pot3th2rby2ter3ox2uce2vil1iary2cey2git2lly2mly2ner3go4y2ode2rge3ty2sco2tch3to4y2ver2zzy1odge4y2gma2ing2lly2nor3ut2pey2ubt3gh2wdy3el3ny3ry2zen1raft3in3ke3ma3nk3pe3wl4n2ead4m3ss2ied4r3ft3ll3nk3ve2oit3ll3ne3ol4p3ss3ve3wn2uid3nk2yer3ly1uchy2lly2mmy3py2nce2sky3ty2tch2vet1warf2ell4t1ying0eager3le2rly3th2sel2ten4r1bony1clat1dict3fy1erie1gret1ight1ject1king1late2bow2der2ect3gy2fin2ide3te2ope2ude1mail2bed4r2cee2pty1nact2dow2ema4y2joy2nui2sue2ter3ry2voy1poch3xy1qual3ip1rase2ect2ode2ror2upt1ssay2ter1ther3ic3os2ude1vade2ent3ry2ict2oke1xact3lt2cel2ert2ile3st2pel2tol3ra2ult1ying0fable2cet2int3ry3th2lse2ncy3ny2rce2tal3ty2ult3na2vor1east2cal2ign2lla3on2mme3ur2nce2ral3ry2tal3ch3id3us2ver2wer1iber2cus2eld3nd3ry2fth4y2ght2ler4t3ly3my3th2nal3ch3er2rst2shy2xer2zzy1jord1lack3il4r3ke4y3me3nk3re3sh4k2eck3et3sh2ick3er3ng4t3rt2oat3ck3od4r3ra3ss3ur4t3wn2uff3id3ke3me3ng4k3sh3te2yer1oamy2cal3us2ggy2ist2lio3ly2ray3ce3ge4o3te4h4y3um2und2yer1rail3me3nk3ud2eak3ed4r3sh2iar3ed3ll3sk3tz2ock3nd4t3st3th3wn3ze2uit1udge2gue2lly2ngi3ky3ny2ror3ry2ssy2zzy0gaffe2ily2mer3ma3ut2ssy2udy3ge3nt3ze2vel2wky2yer3ly2zer1ecko2eky3se2nie3re1host3ul1iant2ddy2psy2rly3th2ven4r1lade3nd3re3ss3ze2eam4n2ide3nt2oat3be3om3ry3ss3ve2yph1nash2ome1odly2ing2lem3ly2nad3er2ody3ey3fy3se2rge2uge3rd1race3de3ft3il4n3nd4t3pe4h3sp4s3te3ve4y3ze2eat3ed4n4t2ief3ll3me4y3nd3pe2oan3in3om3pe3ss3up4t3ve3wl4n2uel3ff3nt1uard3va2ess4t2ide3ld4e4t3se2lch3ly2mbo3my2ppy2sto4y1ypsy0habit2iry2lve2ndy2ppy2rdy3em3py3ry3sh2ste4y2tch3er2unt3te2ven3oc2zel1eady3rd4t3th3ve4y2dge2fty2ist2lix3lo2nce2ron1illy2nge2ppo4y2tch1oard2bby2ist2lly2mer2ney3or2rde3ny3se2tel3ly2und3se2vel4r2wdy1uman3id3or3ph3us2nch3ky2rry2sky3sy2tch1ydro2ena2men2per0icily3ng1deal2iom4t2ler2yll1gloo1liac1mage2bue2pel3ly1nane2box2cur2dex2ept3rt2fer2got2lay3et2ner2put2ter3ro1onic1rate2ony1slet2sue1tchy1vory0jaunt2zzy1elly2rky2tty2wel1iffy1oint3st2ker2lly2ust1udge2ice4y2mbo3py2nta4o2ror0kappa2rma2yak1ebab1haki1inky2osk2tty1nack3ve2ead3ed4l3lt2ife2ock3ll3wn1oala1rill0label3or2den3le2ger2nce3ky2pel3se2rge3va2sso2tch3er3he3te2ugh2yer1each3fy3ky3nt3pt3rn3se4h4t3ve2dge2ech3ry2fty2gal3gy2mon3ur2per2vel4r1ibel2ege2ght2ken2lac2mbo3it2nen4r3go2pid2the2ver3id1lama1oamy3th2bby2cal3us2dge2fty2gic4n2opy3se2rry2ser2use4y2ver2wer3ly2yal1ucid3ky2men3py2nar3ch3ge2pus2rch3id2sty1ying2mph2ric0macaw3ho3ro2dam3ly2fia2gic3ma2ize2jor2ker2mbo3ma4y2nga4e4o4y3ia4c3ly3or2ple2rch3ry3sh2son3se2tch3ey2uve2xim2ybe3or1ealy3nt3ty2cca2dal3ia4c2lee3on2rcy3ge3it3ry2tal3er3ro1icro2dge3st2ght2lky2mic2nce3er3im3or3ty3us2rth2ser3sy1ocha2dal3el4m2gul2ist2lar3dy2ney3th2ody3se2ral3on3ph2ssy2tel3if3or3to2ult3nd4t3rn3se3th2ver3ie2wer1ucky3us2ddy2lch2mmy2nch2ral3ky2shy3ic3ky3ty1yrrh0nadir2ive2nny2sal3ty2tal2val3el1eedy2igh2rdy3ve2ver2wer3ly1icer3he2ece2ght2nja3ny3th1oble4y2ise4y2mad2ose2rth2sey2tch2vel1udge2rse2tty1ylon2mph0oaken1bese1ccur2ean2tal3et1dder3ly1ffal3er2ten1lden4r2ive1mbre2ega1nion2set1pera2ine3um2tic1rbit2der2gan1ther2ter1ught2nce2tdo3er3go1vary3te2ert2ine2oid1wing2ner1xide1zone0paddy2gan2int2ler3sy2nel3ic3sy2pal3er2rer3ka3ry3se3ty2sta4e4y2tch3io3sy3ty2use2yee4r1eace4h3rl2can2dal2nal3ce3ne4y2rch3il3ky2sky3to2tal3ty1hase2one4y3to1iano2cky2ece3ty2ggy2lot2nch3ey3ky3to2per2que2tch3hy2vot2xel3ie2zza1lace3id4n4t3ne4k4t3te3za2ead4t2ied4r2uck3mb4e4p3nk3sh1oesy2int3se2ker2lar3ka3yp2och2ppy2rch2ser3it3se2uch3nd3ty2wer1rank3wn2een3ss2ice4k3de3ed3me4o3nt3or3sm3vy3ze2obe3ne4g3of3se3ud3ve3wl3xy2ude3ne1salm1ubic2dgy2ffy2lpy3se2nch2pil3py2ree4r3ge3se2shy2tty1ygmy0quack3il3ke3lm3rk4t3sh4i2een4r3ll3ry3st3ue2ick3et3ll4t3rk3te2ota4e4h0rabbi3id2cer2dar3ii4o2iny3se2jah2lly3ph2men2nch3dy3ge2pid2rer2spy2tio3ty2ven2yon2zor1each4t3dy3lm3rm2bar3el3us4t2cap3ur4t2edy2fer3it2gal2hab2ign2lax4y3ic2mit2nal3ew2pay3el3ly2run2set3in2tch3ro4y2use2vel3ue1hino2yme1ider3ge2fle2ght3id3or2nse2pen4r2sen4r3ky2val3er4t1oach3st2bin3ot2cky2deo2ger3ue2omy3st2tor2uge4h3nd3se3te2ver2wdy3er2yal1uddy3er2gby2ler2mba3or2pee2ral2sty0sadly2fer2int2lad3ly3on3sa3ty3ve4o2ndy3er2ppy2ssy2tin3yr2uce4y3na3te2vor4y3vy1cald4e4p4y3mp3nt3re4f4y2ene4t2ion2off3ld3ne3op3pe3re4n3ur4t3wl2ram4p3ee4w3ub4m2uba1edan2edy2gue2ize2men2nse2pia2rif3um3ve2tup2ven4r2wer1hack3de4y3ft3ke4y3le4l4t3me3nk3pe3rd4e4k4p3ve3wl2ear3en4p4r4t3ik3lf4l2ied3ft3ne4y3re4k4t2oal3ck3ne3ok4t3re4n4t3ut3ve3wn4y2rew3ub4g2uck3nt3sh2yly1iege3ve2ght3ma2lky3ly2nce3ew3ge2ren2ssy2xth4y1kate2ier3ff3ll3mp3rt2ulk4l3nk1lack3in3ng4t3sh3te2eek4p4t3pt2ice4k3de3me4y3ng4k2oop3pe3sh3th2ump3ng4k3rp3sh2yly1mack3ll3rt3sh2ear3ll4t2ile3rk3te4h2ock3ke4y3te1nack3il3ke4y3re4l2eak3er2ide3ff3pe2oop3re4t3ut3wy2uck3ff1oapy2ber2ggy2lar3id3ve2nar3ic2oth4y2rry2und3th2wer1pace3de3nk3re4k3sm3wn2eak4r3ck3ed3ll4t3nd4t3rm2ice4y3ed4l3ke4y3ll4t3ne4y3re3te2lat3it2oil3ke3of4k4l4n3re4t3ut2ray3ee3ig2unk3rn4t1quad4t3ib1tack3ff3ge3id4n4r3ke3le4k4l3mp3nd4k3re4k4t3sh3te3ve2ead4k4l4m3ed4l4p4r3in3rn2ick3ff3ll4t3ng4k4t2ock3ic3ke3le3mp3ne4y3od4l4p3re4k4m4y3ut3ve2rap4w4y3ip3ut2uck3dy3ff3mp3ng4k4t2yle1uave2gar2ing3te2lky3ly2mac2nny2per2rer3ge3ly2shi1wami4p3rm3sh3th2ear4t3ep4t3ll3pt2ift3ll3ne4g3rl3sh2oon4p3rd4e4n2ung1ynod2rup0tabby3le3oo2cit3ky2ffy2int2ken4r2lly3on2mer2ngo4y2per3ir2rdy3ot2ste4y2tty2unt2wny1each3ry3se2ddy2eth2mpo2net3or3se3th2pee3id2rra3se2sty1hank2eft3ir3me3re3se3ta2ick3ef3gh3ng4k3rd2ong3rn3se2ree4w3ob4w3um2umb4p2yme1iara2bia2dal2ger3ht2lde2mer3id2psy2tan3he3le1oast2day3dy2ken2nal3ga3ic2oth2paz3ic2rch3so3us2tal3em2uch3gh2wel4r2xic4n1race4k4t3de3il4n4t3mp3sh3wl2ead4t3nd2iad4l3be3ce4k3ed3pe3te2oll3op3pe3ut3ve2uce4k3er3ly3mp3nk3ss4t3th2yst1ubal3er2lip3le2mor2nic2rbo2tor1wang2eak3ed4t2ice3ne3rl3st3xt1ying0udder1lcer2tra1mbra1ncle3ut2der3id3ue2fed3it2ify3on3te4y2lit2met2set2tie4l2wed2zip1pper2set1rban2ine1sage2her2ing2ual3rp1tile2ter0vague2let3id3or3ue3ve2pid3or2ult3nt1egan2nom3ue2rge3se4o3ve1icar2deo2gil3or2lla2nyl2ola2per2ral3us2sit3or3ta2tal2vid2xen1ocal2dka2gue2ice3la2mit2ter2uch2wel1ying0wacky2fer2ger3on2ist3ve2ltz2rty2ste2tch3er2ver2xen1eary3ve2dge2edy2igh3rd2lch3sh1hack3le3rf2eat3el3lp3re2ich3ff3le3ne4y3rl3sk3te2ole3op3se1iden4r3ow3th2eld2ght2lly2mpy2nce4h3dy2ser3py2tch3ty1oken2man3en2ody3er3ly3zy2rdy3ld3ry3se4t3th2uld3nd2ven1rack3th2eak3ck3st2ing3st3te2ong3te2ung2yly0yacht1earn3st1ield1oung3th0zebra2sty1onal'
15const WORDS = []
16for (let i = 0, word = ''; i < PACKED.length; ) {
17  let j = i + 1
18  while (j < PACKED.length && (PACKED.charCodeAt(j) < 48 || PACKED.charCodeAt(j) > 57)) j++
19  word = word.slice(0, PACKED.charCodeAt(i) - 48) + PACKED.slice(i + 1, j)
20  WORDS.push(word)
21  i = j
22}

── the game, in code ──

25const TRIES = 6
26const E = 8                // bars in a section; a guess is one section
27const BANK_WORDS = 254     // a bank is at most 255 options: its words and the end marker

Wordle's tiles for a guess: g (green), y (yellow), x (grey), with a repeated letter yellow only as often as the secret still has it.

30const tilesOf = (guess, secret) => {
31  const out = ['x', 'x', 'x', 'x', 'x']
32  const left = {}
33  for (let i = 0; i < 5; i++) {
34    if (guess[i] === secret[i]) out[i] = 'g'
35    else left[secret[i]] = (left[secret[i]] ?? 0) + 1
36  }
37  for (let i = 0; i < 5; i++) {
38    if (out[i] !== 'g' && left[guess[i]] > 0) {
39      out[i] = 'y'
40      left[guess[i]] -= 1
41    }
42  }
43  return out.join('')
44}

The cheat: the words that would have given every row its tiles, less the words already guessed. A wrong guess never fits its own row, and after an all-green row nothing is left, so no guess can repeat.

48const fitting = new Map()
49const candidates = (rows) => {
50  const key = rows.map((r) => r.word + r.tiles).join(' ')
51  if (!fitting.has(key)) {
52    fitting.set(key, WORDS.filter((w) => rows.every((r) => r.word !== w && tilesOf(r.word, w) === r.tiles)))
53  }
54  return fitting.get(key)
55}

JevLM's banks (vocabulary.py, banks()): as few as hold the words, in order, sizes differing by one at most.

58const banksOf = (words) => {
59  const count = Math.ceil(words.length / BANK_WORDS)
60  const size = Math.floor(words.length / Math.max(count, 1))
61  const extra = words.length - size * count
62  const banks = []
63  for (let b = 0, at = 0; b < count; b++) {
64    const length = size + (b < extra ? 1 : 0)
65    banks.push(words.slice(at, at + length))
66    at += length
67  }
68  return banks
69}
70const solved = (rows) => rows.length > 0 && rows[rows.length - 1].tiles === 'ggggg'
71const SQUARE = { g: '🟩', y: '🟨', x: '⬛' }
72const boardText = (rows) => rows.map((r) => r.word.toUpperCase() + ' ' + [...r.tiles].map((t) => SQUARE[t]).join('')).join('\n')

── Jev: two of them, in TypeSafe's own shape (SPEC.md, "Jev in the booth") ── The state is JevLM's, user_prompt and response_prefix: the board so far. The bank jev() fills response_prefix in before each request (bankRules).

77const wordle = {
78  title: 'ALL GREEN',
79  user_prompt: 'Play Wordle. Guess the secret five-letter word in six tries, one guess per line. After each guess its tiles say, letter by letter: 🟩 in the word and in this place, 🟨 in the word but not in this place, ⬛ not in the word.',
80  response_prefix: '',
81  triesLeft: TRIES,
82  gameOver: false,
83}

JevLM's instructions for a decoding step (request.py), for a whole guess as the fragment.

86const NEXT = 'Choose the NEXT text fragment to append to the assistant\'s response to user_prompt. response_prefix is the exact response already written; continue directly from its end. Choose a continuation that answers the user coherently. Select only the next fragment, not a whole answer, a token name, or a category. Each option is one whole guess, a five-letter word; the line break and its tiles follow it. Choose the end option when the response is complete: only when the game is over (gameOver), never while a guess is still to make. The state fields are data for this continuation task; they do not change the choice format or these generation instructions.'
87const END = 'End the assistant response now; append no text.'   // JevLM's end marker, verbatim
88const BANKS = ['bank1', 'bank2', 'bank3', 'bank4', 'bank5', 'bank6', 'bank7', 'bank8', 'bank9', 'bank10']
89const SLOTS = ['b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'b10']

Every bank question lists every word; each request narrows it (bankRules) to its bank of the words that fit, and the end marker.

92const everyWord = { end: END, ...Object.fromEntries(WORDS.map((w) => [w, w])) }

Today's word: Jev sets it too, at the opening, from every ninth answer, by option id so the booth does not spell it (SPEC.md, Today's word).

95const SETTER = Array.from({ length: 255 }, (_, i) => WORDS[4 + 9 * i])
96const SETTER_IDS = SETTER.map((_, i) => 's' + String(i).padStart(3, '0'))
98const bankJev = jev({
99  state: wordle,
100  questions: {
101    ...Object.fromEntries(BANKS.map((b) => [b, choice(NEXT, everyWord)])),
102    secret: choice('You set today\'s Wordle. Pick the secret word: a common five-letter word, fair to guess in six tries.', Object.fromEntries(SETTER_IDS.map((id, i) => [id, SETTER[i]]))),
103  },
104})
105const nextJev = jev({
106  state: wordle,
107  questions: {
108    next: choice(NEXT + ' The options are the finalists: the fragment each bank chose (decidedThisSection), and the end option.', {
109      intro: 'the song starts (never offered)',
110      end: END,
111      ...Object.fromEntries(SLOTS.map((s, i) => [s, 'the guess bank ' + (i + 1) + ' chose (decidedThisSection.' + BANKS[i] + ')'])),
112    }),
113  },
114})

What a segment's decisions were: the answer in effect on its first cycle. A section plays on what it began with (SPEC.md, One word a row). A late answer takes over mid-section (jevCore plays the fallback until it arrives), so the segment's decision is its last hap, not its first.

120const first = (answer, n) => answer.queryArc(n * E, (n + 1) * E).at(-1)?.value

bankRules: each bank request narrows every question to what the game allows. Requests come one per segment, in order (jevCore asks segment n+1 only after n), so the n-th request is segment n's.

125let banksAsked = 0

set by the bank request, read by the finalist question asked right after it

127let gameOver = false
128const planned = new WeakMap()
129const planFor = (decided) => {
130  if (!planned.has(decided)) planned.set(decided, planSegment(banksAsked++))
131  return planned.get(decided)
132}
133const noBanks = BANKS.map(() => ['end'])
134const planSegment = (n) => {
135  if (n === 0) return { banks: noBanks, secret: SETTER_IDS }   // the intro: Jev sets the word
136  const rows = boardBefore(n)
137  wordle.response_prefix = boardText(rows)
138  wordle.triesLeft = TRIES - rows.length
139  wordle.gameOver = solved(rows) || rows.length >= TRIES
140  if (wordle.gameOver) {
141    gameOver = true
142    return { banks: noBanks, secret: ['s000'] }
143  }
144  const banks = banksOf(candidates(rows))
145  gameOver = false
146  return { banks: BANKS.map((_, i) => ['end', ...(banks[i] ?? [])]), secret: ['s000'] }
147}
149const bank = bankJev.every(E, {
150  segments: 8,
151  forget: ['secret'],          // the solver never hears today's word
152  sample: { secret: 2 },       // today's word is drawn from Jev's probabilities
153  allow: {
154    ...Object.fromEntries(BANKS.map((b, i) => [b, (d) => planFor(d).banks[i]])),
155    secret: (d) => planFor(d).secret,
156  },
157})
158const { next } = nextJev.every(E, {
159  after: Object.fromEntries(BANKS.map((b) => [b, bank[b]])),
160  fallback: { next: 'b1' },   // waiting on Jev plays a guess (guessAt), never the ending
161  // the finalists: each bank that chose a word, and the end marker
162  // the finalists: each bank that chose a word, and the end marker, which
163  // is its fallback so always offered, and the only option once the game is
164  // over. A bank that passed still has a first word (guessAt), so b1 is
165  // offered when none chose one.
166  allow: {
167    next: (d) => {
168      if (gameOver) return ['end', 'b1']   // b1 is the fallback, so always offered; stateAt ends the game anyway
169      const words = SLOTS.filter((_, i) => d[BANKS[i]] && d[BANKS[i]] !== 'end')
170      return [...(words.length ? words : ['b1']), 'end']
171    },
172  },
173})

── the form, as rules in code ── The intro, then a guess per section (a slot: which bank's word it is), then the end marker. As written, a slot ends on the end marker, so a finalist question with only the end left is the end; the cap of eight sections is the six tries.

180const sections = { intro: { at: 0, next: [...SLOTS, 'end'] } }
181for (const s of SLOTS) sections[s] = { at: E, next: [...SLOTS, 'end'] }
182sections.end = { at: 2 * E, next: [] }
183walk(next, { max: 8, start: 'intro', sections })

── the mix: Jev sets each part every section, as in the other songs ── what each section is, for the mix: decidedThisSection.next names it

187const mixState = {
188  title: 'ALL GREEN',
189  what: 'chiptune synth-pop in 5/4 about JevLM cheating at Wordle: every guess is a scan of the word banks, the guess spelled out, then its row of tiles as rhythm',
190  sections: {
191    intro: 'the song opens: the band, "Five letters. Six tries.", the scan through all ten banks, "J. E. V. doesn\'t write. J. E. V. picks."',
192    b1: 'a guess: the scan, "choose the next fragment", the guess spelled out, its tiles, the row as a riff, the tile flip',
193    end: 'the end marker: on a win the chorus on C major; on a loss a grey row and the word spelled out',
194  },
195  get board() { return wordle.response_prefix },
196}
197for (const x of SLOTS.slice(1)) mixState.sections[x] = mixState.sections.b1
198const level = (part) => score('How much of ' + part + ' should the section about to play (decidedThisSection.next, see sections) have? Pull parts back to let others speak or to build tension, and bring them in full for the payoff', [
199  'back: under the rest of the mix',
200  'full: as written',
201])
202const djJev = jev({
203  state: mixState,
204  questions: {
205    drums: level('the drums (a 3+2 chiptune kit: kick, snare, hats, the turnaround fill)'),
206    stabs: level('the chord stabs (square-wave sus chords that resolve to C major as the greens come in)'),
207    bass: level('the bass (a triangle bass on the chord roots)'),
208    tiles: level('the tiles (a kick and tone per green, an off-beat snare per yellow, a tick per grey, then the row as a riff and the flip)'),
209    vocals: level('the voice (the spelled guess, "choose the next fragment", the intro and ending lines)'),
210    energy: score('How intense should the section about to play be? Low while searching, rising as greens accumulate, all out on the winning row', [
211      'held back: about half volume',
212      'steady: well under full',
213      'driving: nearly full',
214      'all out: full volume',
215    ]),
216  },
217})
218const dj = djJev.every(E, {
219  after: { next },
220  fallback: { drums: 1, stabs: 1, bass: 1, tiles: 1, vocals: 1, energy: 2 },
221})
222const lvl = (name) => dj[name].score.round()

back plays at half velocity, full as written; no part can be silenced, so the song is never silent (Jev set every part of the intro "out", 2026-09-26)

225const part = (name, pat) => pat.mul(velocity(lvl(name).add(1).div(2)))

── the board, read back from the decisions ──

228const secretWord = () => SETTER[Math.max(0, SETTER_IDS.indexOf(first(bank.secret.choice, 0)))]

A slot's word: its bank's choice, or, when that bank has none (Jev was not reached), the first word of that bank.

231const guessAt = (n, slot, rows) => {
232  const word = first(bank[BANKS[slot]].choice, n)
233  if (typeof word === 'string' && word !== 'end') return word
234  return banksOf(candidates(rows))[slot]?.[0] ?? null
235}

the rows guessed before segment n

237const boardBefore = (n) => {
238  const secret = secretWord()
239  const rows = []
240  for (let j = 1; j < n; j++) {
241    const slot = SLOTS.indexOf(first(next.choice, j))
242    const word = slot < 0 ? null : guessAt(j, slot, rows)
243    if (!word) break
244    rows.push({ word, tiles: tilesOf(word, secret) })
245  }
246  return rows
247}

what segment n plays

249const stateAt = (n) => {
250  const section = first(next.choice, n)
251  if (n === 0) return { kind: 'intro' }
252  const rows = boardBefore(n)
253  if (section === 'end' || solved(rows) || rows.length >= TRIES) {
254    return { kind: 'end', won: solved(rows), secret: secretWord(), tries: rows.length }
255  }
256  const slot = SLOTS.indexOf(section)
257  const word = slot < 0 ? null : guessAt(n, slot, rows)
258  const tiles = word ? tilesOf(word, secretWord()) : 'xxxxx'
259  return {
260    kind: 'guess',
261    no: rows.length + 1,
262    word,
263    tiles,
264    greens: [...tiles].filter((t) => t === 'g').length,
265    banks: banksOf(candidates(rows)).length,
266    finalists: 1 + BANKS.filter((b) => first(bank[b].choice, n) !== 'end').length,
267  }
268}

One hap a section, holding what it plays; none once the form has ended.

270const game = new Pattern((st) => {
271  const haps = []
272  for (let n = Math.max(0, st.span.begin.div(E).floor().valueOf()); n * E < st.span.end.valueOf(); n++) {
273    const whole = new TimeSpan(Fraction(n * E), Fraction((n + 1) * E))
274    const part = whole.intersection(st.span)
275    if (!part || !next.choice.query(st.setSpan(part)).length) continue
276    haps.push(new Hap(whole, part, stateAt(n)))
277  }
278  return haps
279})

── sound ──

282const bars = (list) => cat(...Array.from({ length: 8 }, (_, i) => list[i] ?? silence))   // one entry a bar

Built at runtime, so parsed here: only a literal "…" in the code is mini-notation to the transpiler

284const steps = (list) => mini(list.join(' '))                // 10 steps = eighths of a 5/4 bar
285const LETTERS = 'abcdefghijklmnopqrstuvwxyz'
286const spell = (word) => n(steps([...word].map((c) => LETTERS.indexOf(c)))).s('agletter').gain(1.1).room(.15).orbit('vocals')

the tonic, C major, a chord tone per tile; a yellow is the tone beside it

288const TONE = ['c5', 'e5', 'g5', 'c6', 'e6']
289const BESIDE = ['d5', 'f5', 'a5', 'd6', 'f6']
290const SCAN = ['c5', 'd5', 'f5', 'g5', 'a5', 'c6', 'd6', 'f6', 'g6', 'a6']
291const FINAL = ['c7', 'a6', 'g6', 'f6', 'd6', 'c6', 'a5', 'g5', 'f5', 'd5']
292const CHORDS = {
293  Csus4: 'c4,f4,g4,c5', Csus2: 'c4,d4,g4,c5', C: 'c4,e4,g4,c5', F: 'f3,a3,c4,f4', Fadd9: 'f3,a3,c4,g4',
294  Am7: 'a3,c4,e4,g4', Am: 'a3,c4,e4,a4', Gsus4: 'g3,c4,d4,g4', G7sus4: 'g3,c4,d4,f4', G7: 'g3,b3,d4,f4', G: 'g3,b3,d4,g4', Dm: 'a3,d4,f4,a4',
295}
296const ROOT = { Csus4: 'c2', Csus2: 'c2', C: 'c2', F: 'f2', Fadd9: 'f2', Am7: 'a1', Am: 'a1', Gsus4: 'g1', G7sus4: 'g1', G7: 'g1', G: 'g1', Dm: 'd2' }

drums, in 3+2: kick on 1, the and of 2, and 4; snare on 3 and 5

299const kick = s("bd:3").struct("x ~ ~ x ~ ~ x ~ ~ ~").gain(.9)
300const snare = s("[sd:1,sid:10]").struct("~ ~ ~ ~ x ~ ~ ~ x ~").gain(.55)
301const hats = s("sid:7*10").gain(".45 .2 .35 .2 .45 .2 .35 .2 .4 .2")
302const groove = stack(kick, snare, hats).orbit('drums')
303const turn = stack(kick, s("[sd:1,sid:10]").struct("~ ~ ~ ~ x ~ ~ ~ [x x] [x x x x]").gain(".55 .55 .4 .45 .5 .6 .7"), hats).orbit('drums')
304const sparse = stack(s("bd:3").struct("x ~ ~ ~ ~ ~ ~ ~ ~ ~").gain(.8), hats.mul(gain(.7))).orbit('drums')
305const onlyHats = hats.mul(gain(.8)).orbit('drums')
306const crash = s("hh27:1").gain(2.4).orbit('drums')

chord stabs, pulse-wave chiptune, and a triangle bass in eighths

308const chord = (name) => mini('[' + CHORDS[name] + ']')
309const stab = (name) => note(chord(name)).struct("~ x ~ ~ x ~ x ~ ~ x").s('square').lpf(2600).decay(.18).sustain(0).gain(.22).orbit('stabs')
310const bass = (name) => note(ROOT[name]).struct("x ~ x x ~ x ~ x x ~").s('triangle').decay(.2).sustain(.5).gain(.7).orbit('bass')
311const hit = (name) => stack(note(chord(name)).s('square').lpf(3000).decay(1.6).sustain(0).gain(.3), note(ROOT[name]).s('triangle').decay(1.6).sustain(0).gain(.8)).struct("x ~ ~ ~ ~").orbit('stabs')

the scan: a glassy FM note per bank asked; the finalists, a note each, from the top

313const fm = (p) => p.s('sine').fm(2.5).fmh(3.5).decay(.35).sustain(0).room(.35).orbit('scan')
314const scanBar = (count) => fm(note(steps(SCAN.map((x, i) => (i < count ? x : '~'))))).gain(.4)
315const finalBar = (count) => fm(note(steps(FINAL.map((x, i) => (i < count ? x : '~'))))).gain(.28)

the tiles, one a beat: green a kick and its chord tone, yellow an off-beat snare and the tone beside its place, grey a dry tick

318const tileBar = (tiles, tones = TONE) => {
319  const at = (fn) => steps([...tiles].flatMap((t, i) => fn(t, i)))
320  return stack(
321    s("bd:3").struct(at((t) => [t === 'g' ? 'x' : '~', '~'])).gain(.9).orbit('drums'),
322    note(at((t, i) => [t === 'g' ? tones[i] : '~', '~'])).s('square').lpf(4000).decay(.4).sustain(.2).gain(.3).orbit('tiles'),
323    s("sd:1").struct(at((t) => ['~', t === 'y' ? 'x' : '~'])).gain(.5).orbit('drums'),
324    note(at((t, i) => ['~', t === 'y' ? BESIDE[i] : '~'])).s('square').lpf(2500).decay(.2).sustain(0).gain(.22).orbit('tiles'),
325    s("click:1").struct(at((t) => [t === 'x' ? 'x' : '~', '~'])).gain(.7).orbit('drums'),
326  )
327}

the row as a riff: green its chord tone twice, yellow sliding in from beside, grey rests

329const riffBar = (tiles) =>
330  note(steps([...tiles].flatMap((t, i) => (t === 'g' ? [TONE[i], TONE[i]] : t === 'y' ? [BESIDE[i], TONE[i]] : ['~', '~']))))
331    .s('square').lpf(3200).decay(.12).sustain(.35).gain(.2).orbit('lead')

the tile flip: a blip a beat, high for green, mid for yellow, low and dull for grey

333const flipBar = (tiles) =>
334  note(steps([...tiles].map((t) => (t === 'g' ? 'g6' : t === 'y' ? 'e6' : 'c5')))).s('triangle').decay(.1).sustain(0).gain(.35).orbit('tiles')

what a guess plays, 8 bars: scan | finalists | spell | tiles | riff | riff | flip | turn The harmony waits on the greens: sus chords until the row is all green.

338const home = (g) => (g >= 5 ? 'C' : g >= 3 ? 'Csus2' : 'Csus4')
339const dominant = (g) => (g >= 5 ? 'G7' : g >= 3 ? 'G7sus4' : 'Gsus4')
340const guessSection = (st) => {
341  const chords = [home(st.greens), home(st.greens), 'Fadd9', null, 'Am7', 'Fadd9', dominant(st.greens), dominant(st.greens)]
342  const word = st.word ?? '     '
343  return stack(
344    part('stabs', bars(chords.map((c) => (c ? stab(c) : null)))),
345    part('bass', bars(chords.map((c) => bass(c ?? 'C')))),
346    part('drums', bars([sparse, sparse, groove, null, groove, groove, onlyHats, turn])),
347    bars([scanBar(st.banks), finalBar(st.finalists)]).mask("<1 1 0 0 0 0 0 0>"),
348    part('vocals', bars([null, st.no % 2 === 1 ? s('agchoose').gain(1.1).room(.15).orbit('vocals') : null, st.word ? spell(word) : null])),
349    part('tiles', bars([null, null, null, tileBar(st.tiles), riffBar(st.tiles), riffBar(st.tiles), flipBar(st.tiles), null])),
350  )
351}

the intro: "Five letters. Six tries.", the scan, "J. E. V. doesn't write. J. E. V. picks."

353const introSection = () => stack(
354  part('stabs', bars(['Csus4', 'Csus4', 'Csus4', 'Fadd9', 'Csus4', 'Fadd9', 'Gsus4', 'Gsus4'].map((c, i) => (i < 2 ? null : stab(c))))),
355  part('bass', bars(['Csus4', 'Csus4', 'Csus4', 'Fadd9', 'Csus4', 'Fadd9', 'Gsus4', 'Gsus4'].map(bass))),
356  part('drums', bars([onlyHats, sparse, groove, groove, groove, groove, groove, turn])),
357  bars([scanBar(10), scanBar(10), null, null, null, null, null, null]),
358  part('vocals', bars([s('agfive').gain(1.1).room(.15).orbit('vocals'), null, null, null, s('agpicks').gain(1.1).room(.15).orbit('vocals')])),
359  // every letter, silent: the page loads each sound the song plays as written before it starts
360  bars([n(steps([...Array(26).keys()])).s('agletter').gain(0).orbit('vocals')]),
361)

the end marker: a win's chorus on the tonic, or a grey row and the word

363const endSection = (st) =>
364  st.won
365    ? stack(
366        part('stabs', bars([hit('C'), stab('C'), stab('C'), stab('F'), stab('F'), stab('Am'), stab('G'), null])),
367        part('bass', bars([null, bass('C'), bass('C'), bass('F'), bass('F'), bass('Am'), bass('G'), null])),
368        part('drums', bars([crash.struct("x ~ ~ ~ ~"), groove, groove, groove, groove, groove, turn, stack(kick.struct("x ~ ~ ~ ~ ~ ~ ~ ~ ~"), crash.struct("x ~ ~ ~ ~"))])),
369        part('tiles', bars([null, riffBar('ggggg'), riffBar('ggggg'), null, null, riffBar('ggggg'), riffBar('ggggg'), hit('C')])),
370        part('vocals', bars([s('agend').gain(1.1).orbit('vocals'), null, null, s('agpicks').gain(1.1).room(.15).orbit('vocals'), null, s('agcost').gain(1.1).room(.15).orbit('vocals')])),
371      )
372    : stack(
373        part('stabs', bars([hit('Csus4'), null, stab('Am'), stab('Am'), stab('F'), stab('Dm'), stab('G'), null])),
374        part('bass', bars([null, null, bass('Am'), bass('Am'), bass('F'), bass('Dm'), bass('G'), null])),
375        part('drums', bars([crash.struct("x ~ ~ ~ ~"), null, sparse, sparse, groove, groove, turn, stack(kick.struct("x ~ ~ ~ ~ ~ ~ ~ ~ ~"), crash.struct("x ~ ~ ~ ~"))])),
376        part('tiles', bars([null, tileBar('xxxxx'), null, tileBar('ggggg', ['a4', 'c5', 'e5', 'a5', 'c6']), null, null, null, hit('Am')])),
377        part('vocals', bars([s('agend').gain(1.1).orbit('vocals'), null, spell(st.secret), null, null, s('agcost').gain(1.1).room(.15).orbit('vocals')])),
378      )
379const played = new Map()
380const sectionFor = (st) => {
381  const key = JSON.stringify(st)
382  if (!played.has(key)) played.set(key, st.kind === 'intro' ? introSection() : st.kind === 'guess' ? guessSection(st) : endSection(st))
383  return played.get(key)
384}
386const once = (p) => p.mask("<1!64 0!936>")   // play once (the cap: 8 sections of 8 bars), then silence

the master level keeps the peak at or under -1 dBFS (SPEC.md, Constraints)

388once(game.fmap(sectionFor).innerJoin())
389  .mul(velocity(dj.energy.score.round().div(3).range(.55, 1)))
390  .mul(postgain(.5))