Tessarune
Harvest Festival · chicken_coop.js

鶏小屋

48×48 · JavaScript 354 行

赤い屋根とスロープを備えた木造の鶏小屋に、藁の巣、散らばった穀物、そして白い雌鶏が配置された 48×48 の小物です。16 コマの idle(待機)では雌鶏が地面を 2 回つつき、16 コマの roost では戸口に座る雌鶏と窓から顔を出す別の鶏が描かれます。8 コマ一回再生の egg では雌鶏が羽を膨らませて立ち上がり、巣の中に卵が現れます。

鶏小屋 — 描画結果6×
このプログラムで描画(変種「egg」) · 48×48 · 8 コマ
Harvest Festival動くスプライト素材: Harvest Festival(itch.io)Vol.6・収穫祭の小物 12 点・37 アニメ

冒頭のコメント。これが指示書です。サイズ、視点、光の向き、すべての変種が書かれています。

chicken_coop.js · 1–11
// Chicken coop — 48x48 floor prop, top-down 3/4, key light top-left.// A little wooden hen house on four short legs: vertical planks with a lit left side, a barn-red shingled roof with// a wooden ridge cap and barge boards, an arched door with straw spilling over the sill, a square window, and a// cleated plank ramp running down to the ground. In front: a straw nest, scattered grain, and a white hen with a// brown barred wing, dark brown tail, red comb and wattle, yellow beak and legs.// Variants://   idle   16f loop  the hen pecks the ground twice, her head bobs between pecks, her tail flicks//   roost  16f loop  the hen sits in the doorway and breathes, blinks once; a second hen's head peeks out of the//                    window and ducks back//   egg     8f once  the hen fluffs up and rises, an egg is revealed in the straw nest under her, she settles back//                    onto it; the last frame equals idle frame 0

最後の数行。submit でキャンバスの大きさと、描画エンジンが書き出す変種の一覧を指定します。

chicken_coop.js · 344–354
submit(Coop, {  size: [W, H], origin: "top-left", kind: "icon", scale: 1,  animate: { fps: 10, duration: N_IDLE / 10, loop: true },  anchor: { x: CX, y: GROUND },  args: ["idle"],  variants: [    { name: "idle", args: ["idle"] },    { name: "roost", args: ["roost"], animate: { fps: 10, duration: N_ROOST / 10, loop: true } },    { name: "egg", args: ["egg"], animate: { fps: 10, duration: N_EGG / 10, loop: false } },  ],});
プログラム全体を表示 (354 行)
chicken_coop.js · 354 行
// Chicken coop — 48x48 floor prop, top-down 3/4, key light top-left.// A little wooden hen house on four short legs: vertical planks with a lit left side, a barn-red shingled roof with// a wooden ridge cap and barge boards, an arched door with straw spilling over the sill, a square window, and a// cleated plank ramp running down to the ground. In front: a straw nest, scattered grain, and a white hen with a// brown barred wing, dark brown tail, red comb and wattle, yellow beak and legs.// Variants://   idle   16f loop  the hen pecks the ground twice, her head bobs between pecks, her tail flicks//   roost  16f loop  the hen sits in the doorway and breathes, blinks once; a second hen's head peeks out of the//                    window and ducks back//   egg     8f once  the hen fluffs up and rises, an egg is revealed in the straw nest under her, she settles back//                    onto it; the last frame equals idle frame 0const W = 48, H = 48, CX = 24, GROUND = 45;const N_IDLE = 16, N_ROOST = 16, N_EGG = 8;const HB = ["n2", "n3", "n4", "n5"];                    // white plumage rampconst BX = 32, BY = 45;                                  // ground hen: feet column / ground row// ---- a tiny layered raster: each layer is emitted as named fields; a layer may carry its own ink ring ----------function Layer(name, clip) {  const px = new Map();  return {    name, px, clip,    set(x, y, k, part) {      if (!k || x < 0 || y < 0 || x >= W || y >= H) return;      if (clip && !clip(x, y)) return;      px.set(y * W + x, [k, part || name]);    },    has(x, y) { return px.has(y * W + x); },  };}function emitParts(g, entries) {  const parts = new Map();  for (const [i, [k, p]] of entries) {    if (!parts.has(p)) parts.set(p, { m: new Map(), x0: W, x1: -1, y0: H, y1: -1 });    const P = parts.get(p), x = i % W, y = (i - x) / W;    P.m.set(i, k); P.x0 = Math.min(P.x0, x); P.x1 = Math.max(P.x1, x); P.y0 = Math.min(P.y0, y); P.y1 = Math.max(P.y1, y);  }  for (const [p, P] of parts) g.part(p, () => g.field({ x: [P.x0, P.x1], y: [P.y0, P.y1] }, (x, y) => P.m.get(y * W + x) ?? null));}// ring: ink on every 4-neighbour of the layer that the layer itself does not cover (separates it from what's behind)function emitLayer(g, L, ring) {  emitParts(g, L.px);  if (!ring) return;  const edge = new Map();  for (const i of L.px.keys()) {    const x = i % W, y = (i - x) / W;    for (const [dx, dy] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) {      const nx = x + dx, ny = y + dy;      if (nx < 0 || ny < 0 || nx >= W || ny >= H || L.has(nx, ny)) continue;      if (L.clip && !L.clip(nx, ny)) continue;      edge.set(ny * W + nx, ["ink", L.name + "Edge"]);    }  }  emitParts(g, edge);}function fillEll(L, cx, cy, rx, ry, fn) {  for (let y = Math.floor(cy - ry - 1); y <= Math.ceil(cy + ry + 1); y++)    for (let x = Math.floor(cx - rx - 1); x <= Math.ceil(cx + rx + 1); x++) {      const nx = (x + 0.5 - cx) / rx, ny = (y + 0.5 - cy) / ry;      if (nx * nx + ny * ny <= 1) L.set(x, y, fn(x, y, nx, ny));    }}function inPoly(pts, x, y) {  let c = false;  for (let i = 0, j = pts.length - 1; i < pts.length; j = i++) {    const [xi, yi] = pts[i], [xj, yj] = pts[j];    if ((yi > y) !== (yj > y) && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi) c = !c;  }  return c;}function fillPoly(L, pts, fn) {  const xs = pts.map((p) => p[0]), ys = pts.map((p) => p[1]);  for (let y = Math.floor(Math.min(...ys)); y <= Math.ceil(Math.max(...ys)); y++)    for (let x = Math.floor(Math.min(...xs)); x <= Math.ceil(Math.max(...xs)); x++)      if (inPoly(pts, x + 0.5, y + 0.5)) L.set(x, y, fn(x, y));}function disc(L, cx, cy, r, fn) { fillEll(L, cx, cy, r, r, fn); }// ---- the coop ------------------------------------------------------------------------------------------------const roofL = (y) => Math.round(11 - ((y - 8) * 3) / 10);const roofR = (y) => 48 - roofL(y);const DOOR = { x0: 16, x1: 21, y0: 23, y1: 32 };        // door interiorconst WIN = { x0: 28, x1: 32, y0: 24, y1: 27 };         // window interiorconst inDoor = (x, y) => x >= DOOR.x0 && x <= DOOR.x1 && y >= DOOR.y0 && y <= DOOR.y1 &&  !(y === DOOR.y0 && (x === DOOR.x0 || x === DOOR.x1));const inWin = (x, y) => x >= WIN.x0 && x <= WIN.x1 && y >= WIN.y0 && y <= WIN.y1;function coop(L) {  // weathervane: a little iron rooster on a pole at the ridge  for (let y = 3; y <= 6; y++) L.set(24, y, "n2", "vane");  for (const [x, y, k] of [[22, 1, "n3"], [23, 1, "n2"], [22, 2, "n2"], [23, 2, "n2"], [24, 2, "n1"], [25, 2, "n2"], [26, 1, "n1"],    [23, 3, "n1"], [24, 3, "n1"], [25, 3, "n1"], [21, 2, "g1"], [22, 0, "r1"]]) L.set(x, y, k, "vane");  // legs: back pair (deeper in shadow, shorter), front pair  for (let y = 35; y <= 39; y++) { L.set(17, y, "w0", "legs"); L.set(31, y, "w0", "legs"); }  for (let y = 35; y <= 42; y++) {    L.set(13, y, "w3", "legs"); L.set(14, y, "w2", "legs");    L.set(33, y, "w2", "legs"); L.set(34, y, "w1", "legs");  }  L.set(13, 35, "w1", "legs"); L.set(14, 35, "w0", "legs"); L.set(33, 35, "w0", "legs"); L.set(34, 35, "w0", "legs");  // floor beam under the wall: lit top edge, shaded face, dark underside  for (let x = 11; x <= 37; x++) {    L.set(x, 33, x < 26 ? "w3" : "w2", "sill");    L.set(x, 34, x < 26 ? "w2" : "w1", "sill");  }  L.set(11, 33, "w4", "sill"); L.set(12, 33, "w4", "sill");  for (let x = 15; x <= 31; x++) L.set(x, 35, "w0", "sill");  // front wall: vertical planks  for (let y = 20; y <= 32; y++) for (let x = 12; x <= 36; x++) {    const c = (x - 12) % 3, pl = Math.floor((x - 12) / 3);    let k = pl < 3 ? "w3" : pl < 6 ? "w3" : "w2";    if (pl >= 6 && c === 0) k = "w3";                     // far planks keep a thin lit edge    if (pl < 2 && c === 0) k = "w4";    if (c === 2) k = "w1";                                // seam    const gy = 25 + ((pl * 5) % 6);                       // one grain streak per plank    if (c === 1 && (y === gy || y === gy + 1)) k = pl < 6 ? "w2" : "w1";    if (y === 20) k = "w0";                               // eave shadow    else if (y === 21) k = c === 2 ? "w0" : "w1";    if ((y === 22 || y === 31) && c === 1) k = "n3";      // nail heads    if (x === 36) k = "w1";    L.set(x, y, k, "wall");  }  // door: trim, dark interior, straw on the floor spilling over the sill  for (let y = DOOR.y0 - 1; y <= DOOR.y1; y++) for (let x = DOOR.x0 - 1; x <= DOOR.x1 + 1; x++) {    if (inDoor(x, y)) continue;    const near = inDoor(x + 1, y) || inDoor(x - 1, y) || inDoor(x, y + 1) || inDoor(x + 1, y + 1) || inDoor(x - 1, y + 1);    if (!near) continue;    const left = x <= DOOR.x0 || (y < DOOR.y0 + 1 && x < 19);    L.set(x, y, left ? "w4" : x > DOOR.x1 ? "w1" : "w3", "door");  }  for (let y = DOOR.y0; y <= DOOR.y1; y++) for (let x = DOOR.x0; x <= DOOR.x1; x++) {    if (!inDoor(x, y)) continue;    let k = y < DOOR.y0 + 2 ? "ink" : "n0";    if (x === DOOR.x1 && y > DOOR.y0 + 1) k = "w0";       // the far jamb's inner face catches a little light    if (y === 30) k = x % 3 === 0 ? "g1" : "g0";         // straw bedding: dark at the back, lit strands in front    if (y === 31) k = x % 2 === 0 ? "g1" : "g0";    if (y === 32) k = x === 17 || x === 20 ? "g2" : "g1";    if (y === 29 && x === 18) k = "g0";    L.set(x, y, k, "door");  }  for (const [x, y, k] of [[16, 33, "g2"], [18, 33, "g3"], [19, 33, "g2"], [21, 33, "g2"], [17, 34, "g1"], [20, 34, "g2"]]) L.set(x, y, k, "straw");  // window: lit top/left frame, dark inside, sill board, straw poking over the sill  for (let y = WIN.y0 - 1; y <= WIN.y1 + 1; y++) for (let x = WIN.x0 - 1; x <= WIN.x1 + 1; x++) {    if (inWin(x, y)) L.set(x, y, y === WIN.y0 ? "ink" : "n0", "window");    else L.set(x, y, x === WIN.x0 - 1 || y === WIN.y0 - 1 ? (x < WIN.x1 + 1 ? "w4" : "w3") : "w1", "window");  }  for (let x = WIN.x0 - 2; x <= WIN.x1 + 2; x++) { L.set(x, WIN.y1 + 2, x < WIN.x1 ? "w3" : "w2", "window"); L.set(x, WIN.y1 + 3, "w0", "window"); }  for (const [x, y, k] of [[29, 27, "g0"], [30, 27, "g1"], [32, 27, "g0"]]) L.set(x, y, k, "straw");  // roof: fascia board, front slope of staggered shingles, ridge cap, barge boards  for (let x = roofL(19) + 1; x <= roofR(19) - 1; x++) L.set(x, 19, x < 22 ? "w4" : x < 33 ? "w3" : "w2", "roof");  for (let y = 8; y <= 18; y++) {    const r = Math.floor((y - 8) / 3), line = (y - 8) % 3;    for (let x = roofL(y); x <= roofR(y); x++) {      const u = (x - roofL(y)) / (roofR(y) - roofL(y));      let k = "r1";      if (line === 0) k = "r0";                            // shadow under the row above      else if ((x + r * 2) % 4 === 0) k = line === 1 ? "r0" : "r1";   // gaps between tabs      else if (line === 1 && u < 0.6 && (x + r * 2) % 4 <= 2) k = "r2";   // lit top-left corner of each tab      else if (line === 2 && u > 0.75) k = "r0";      if (y === 18) k = u < 0.5 ? "r2" : u < 0.85 ? "r1" : "r0";       // the eave edge      if (x === roofL(y)) k = "w3";                         // barge boards      if (x === roofR(y)) k = "w1";      L.set(x, y, k, "roof");    }  }  // a weathered tab and a patch of moss  for (const [x, y, k] of [[30, 12, "r0"], [31, 12, "r0"], [15, 15, "e1"], [16, 15, "e2"], [17, 16, "e1"], [35, 9, "e1"]]) L.set(x, y, k, "roof");  for (let x = 11; x <= 37; x++) {    L.set(x, 7, x < 20 ? "w3" : x < 32 ? "w2" : "w1", "ridge");    if (x > 11 && x < 37) L.set(x, 6, x < 20 ? "w4" : x < 32 ? "w3" : "w2", "ridge");  }}function leaves(L) {  // two fallen leaves resting on the roof (static detail that ties the pack's autumn theme together)  for (const [x, y, k] of [[20, 11, "r3"], [21, 11, "r2"], [28, 15, "g2"], [29, 15, "g1"]]) L.set(x, y, k, "leaves");}function ramp(L) {  const P = [[16, 34], [22, 34], [12, 44], [6, 44]];  fillPoly(L, P, (x, y) => {    const t = (y - 34) / 10, l = 16 - t * 10, r = 22 - t * 10;    const u = (x + 0.5 - l) / (r - l);    let k = u < 0.2 ? "w4" : u < 0.7 ? "w3" : "w2";    if (y === 44) k = u < 0.5 ? "w1" : "w0";    return k;  });  // cleats: a raised slat with a lit top and a shadowed foot  for (const cy of [36, 39, 42]) {    const t = (cy - 34) / 10, l = Math.round(16 - t * 10), r = Math.round(21 - t * 10);    for (let x = l; x <= r; x++) { L.set(x, cy, x < l + 2 ? "w4" : "w3", "cleats"); L.set(x, cy + 1, "w1", "cleats"); }  }}function nest(L) {  // a low heap of straw behind the hen  fillEll(L, 34, 41.5, 5.6, 2.2, (x, y, nx, ny) => {    const a = -0.6 * nx - 0.8 * ny;    let k = a > 0.45 ? "g2" : a > -0.35 ? "g1" : "g0";    if ((x * 3 + y * 2) % 7 === 0 && a > -0.2) k = "g2";   // a few strands    return k;  });  for (const [x, y, k] of [[28, 40, "g2"], [40, 40, "g1"]]) L.set(x, y, k, "nest");}function nestLip(L) {  // straw strands in front of the egg's lower half  for (const [x, y, k] of [[33, 41, "g1"], [34, 41, "g2"], [35, 41, "g1"], [36, 41, "g3"], [37, 41, "g1"]]) L.set(x, y, k, "nestLip");}function egg(L) {  L.set(35, 37, "n5", "egg");  for (let y = 38; y <= 40; y++) for (let x = 34; x <= 36; x++) L.set(x, y, x === 34 ? (y === 40 ? "n3" : "n5") : x === 36 ? "n3" : "n4", "egg");  L.set(35, 38, "n5", "egg"); L.set(36, 40, "n2", "egg");}// ---- the hen ---------------------------------------------------------------------------------------------------// pose: { bx, by, peck 0..1, hd (head x nudge), tail 0/1, fluff 0..1, lift px, blink, sit, br (breath 0..1), face -1 }function hen(L, p) {  const cx = p.bx + 1, cy = p.by - (p.sit ? 3.4 : 6) - p.lift - (p.br || 0) * 0.4;  const rx = 5.6 + p.fluff * 1.2, ry = 3.6 + p.fluff * 0.7 + (p.br || 0) * 0.4;  // legs (behind the body)  if (!p.sit) for (const [lx, lit] of [[p.bx - 2, true], [p.bx, false]]) {    for (let y = Math.floor(cy + 1); y < p.by; y++) L.set(lx, y, lit ? "g2" : "g1", "legs");    L.set(lx - 1, p.by - 1, "g1", "legs"); L.set(lx, p.by - 1, lit ? "g2" : "g1", "legs"); L.set(lx + 1, p.by - 1, "g0", "legs");  }  // tail: a dark brown fan raised behind  const tf = p.tail;  fillPoly(L, [[cx + 2, cy - 1], [cx + 3 + tf, cy - 7], [cx + 5 + tf, cy - 8], [cx + 7 + tf, cy - 5], [cx + 7, cy + 1]], (x, y) => {    const along = (x - cx) + (cy - y);    if (along > 11 + tf) return "w0";    if ((x - cx) <= 3 + tf && y < cy - 2) return "w2";      // lit leading edge of the sickle feathers    return (x + y) % 3 === 0 ? "w0" : "w1";  });  // body  const fl = p.fluff;  fillEll(L, cx, cy, rx, ry, (x, y, nx, ny) => {    const i = litIndex(x, y, cx, cy, rx, ry, 4, 0.22);    return HB[Math.max(0, i)];  });  if (fl > 0.5) for (const [dx, dy] of [[-2, -1], [0, -1], [2, -1], [4, 0], [-4, 0]]) {    // ruffled feather tips break the outline    const x = Math.round(cx + dx * rx / 4), y = Math.round(cy - ry + dy + (Math.abs(dx) === 4 ? ry * 0.6 : 0));    L.set(x, y, dx < 0 ? "n5" : "n4", "body");  }  // wing: brown with darker bars, folded over the flank  fillEll(L, cx + 1.2, cy - 0.1, 3.4 + fl * 0.6, 2.1 + fl * 0.3, (x, y, nx, ny) => {    if (ny < -0.45) return nx < 0.2 ? "w4" : "w3";    if ((x + y * 2) % 3 === 0 && ny < 0.6) return "w1";    return ny > 0.55 ? "w1" : "w2";  });  // neck + head  const up = [cx - 4 + (p.hd || 0), cy - 5 + (p.sit ? 0.5 : 0)], down = [cx - 7, p.by - 4];  const k = p.peck;  const hx = Math.round(up[0] + (down[0] - up[0]) * k), hy = Math.round(up[1] + (down[1] - up[1]) * k);  for (let s = 0; s <= 1.0001; s += 0.2) {    const nx0 = cx - 3 + (hx - (cx - 3)) * s, ny0 = cy - 1.5 + (hy - (cy - 1.5)) * s;    disc(L, nx0, ny0, 1.6, (x) => (x < nx0 ? "n5" : "n4"));  }  disc(L, hx + 0.5, hy + 0.5, 2.3, (x, y, nx, ny) => (nx + ny < -0.2 ? "n5" : nx + ny < 0.7 ? "n4" : "n3"));  // comb and wattle  const dn = k > 0.5;  if (!dn) {    for (const [x, y, c] of [[hx - 1, hy - 3, "r2"], [hx, hy - 3, "r1"], [hx + 1, hy - 3, "r1"], [hx - 1, hy - 4, "r2"], [hx + 1, hy - 4, "r1"],      [hx + 2, hy - 2, "r0"], [hx - 3, hy + 1, "r1"], [hx - 3, hy + 2, "r0"]]) L.set(x, y, c, "comb");    L.set(hx - 3, hy, "g2", "beak"); L.set(hx - 4, hy, "g1", "beak");    L.set(hx - 1, hy - 1, p.blink ? "n3" : "ink", "eye");    if (p.blink) L.set(hx - 2, hy - 1, "n3", "eye");  } else {    // head tipped down to the ground: comb faces forward, beak points down    for (const [x, y, c] of [[hx - 2, hy - 2, "r2"], [hx - 3, hy - 1, "r2"], [hx - 1, hy - 3, "r1"], [hx - 3, hy - 2, "r1"], [hx, hy - 3, "r0"]]) L.set(x, y, c, "comb");    L.set(hx - 2, hy + 2, "g2", "beak"); L.set(hx - 2, hy + 3, "g1", "beak");    L.set(hx, hy, "ink", "eye");  }}function henAt(L, extra) {  hen(L, Object.assign({ bx: BX, by: BY, peck: 0, hd: 0, tail: 0, fluff: 0, lift: 0, blink: 0, sit: false }, extra));}// second hen peeking out of the window (head only, facing right, clipped by the window frame)function windowHen(L, rise) {  const hx = 31, hy = 30 - rise;  disc(L, hx + 0.5, hy + 0.5, 2.3, (x, y, nx, ny) => (nx + ny < -0.2 ? "n5" : nx + ny < 0.7 ? "n4" : "n3"));  for (const [x, y, c] of [[hx, hy - 3, "r2"], [hx + 1, hy - 3, "r1"], [hx - 1, hy - 3, "r1"], [hx - 1, hy - 4, "r2"], [hx + 1, hy - 4, "r1"]]) L.set(x, y, c, "comb");  L.set(hx + 3, hy, "g2", "beak");  L.set(hx + 1, hy - 1, "ink", "eye");  L.set(hx + 2, hy + 2, "r1", "comb");}// ---- poses per variant --------------------------------------------------------------------------------------------const PECK = [0, 0, 0.5, 1, 1, 0.5, 0, 0, 0, 0.5, 1, 1, 0.5, 0, 0, 0];const HD = [0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0];const TAIL = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0];const Coop = sprite(pal, (g, t, mode) => {  t = ((t % 1) + 1) % 1;  const base = Layer("coop");  coop(base);  contactShadow(g, 24, 42, 15, 2);  contactShadow(g, 9, 44, 4, 1);  if (mode !== "roost") contactShadow(g, 33, 45, 7, 1);  g.outlined("ink", () => emitLayer(g, base, false));  const lv = Layer("leaves"); leaves(lv); emitLayer(g, lv, false);  const rp = Layer("ramp"); ramp(rp); emitLayer(g, rp, true);  g.detached("grain", () => {    for (const [x, y, k] of [[20, 44, "g2"], [23, 46, "g3"], [25, 44, "g2"], [22, 42, "g1"], [18, 46, "g2"], [27, 46, "g2"],      [15, 45, "g3"], [26, 42, "g3"], [12, 46, "g2"], [29, 46, "g1"]]) g.pixel(x, y, k);  });  if (mode !== "roost") { const ns = Layer("nest"); nest(ns); emitLayer(g, ns, true); }  if (mode === "roost") {    const f = Math.round(t * N_ROOST) % N_ROOST;    const wl = Layer("henWindow", inWin);    const rise = [0, 0, 0, 1, 3, 4, 4, 4, 4, 4, 4, 3, 1, 0, 0, 0][f];    if (rise > 0) { windowHen(wl, rise); emitLayer(g, wl, false); }    const hl = Layer("hen");    hen(hl, { bx: 18, by: 33, peck: 0, hd: 0, tail: 0, fluff: 0, lift: 0, sit: true,      br: 0.5 - 0.5 * Math.cos(TAU * t * 2), blink: f === 9 || f === 10 ? 1 : 0 });    emitLayer(g, hl, true);    return;  }  const hl = Layer("hen");  if (mode === "egg") {    const f = Math.min(N_EGG - 1, Math.round(t * N_EGG));    const P = [[0, 0, 0], [0.6, 0, 0], [1, 2, -1], [0.8, 5, -1], [0.5, 5, 0], [0.2, 4, 0], [0, 2, 0], [0, 0, 0]][f];    if (f >= 3 && f <= 6) {      const el = Layer("egg"); egg(el); emitLayer(g, el, true);      const lp = Layer("nestLip"); nestLip(lp); emitLayer(g, lp, false);    }    henAt(hl, { fluff: P[0], lift: P[1], hd: P[2], tail: f >= 2 && f <= 4 ? 1 : 0 });  } else {    const f = Math.round(t * N_IDLE) % N_IDLE;    henAt(hl, { peck: PECK[f], hd: HD[f], tail: TAIL[f] });  }  emitLayer(g, hl, true);});submit(Coop, {  size: [W, H], origin: "top-left", kind: "icon", scale: 1,  animate: { fps: 10, duration: N_IDLE / 10, loop: true },  anchor: { x: CX, y: GROUND },  args: ["idle"],  variants: [    { name: "idle", args: ["idle"] },    { name: "roost", args: ["roost"], animate: { fps: 10, duration: N_ROOST / 10, loop: true } },    { name: "egg", args: ["egg"], animate: { fps: 10, duration: N_EGG / 10, loop: false } },  ],});

掲載されたコードでは、ビルドスクリプトが自動付与する共通部分を省略しています。具体的には 1 行のランタイムヘッダー、共有パレット、および汎用ヘルパー関数(contactShadow、litIndex、flame、hash、chk、TAU)です。表示されている行番号は実際のファイルと一致しています。これらのプログラムは AIエージェントが生成し、描画されたコマの仕上がりを確認しながら修正を加えたものです。

エージェントに、こうしたプログラムを書かせる。

Tessarune は描画 API のリファレンス、25 冊の制作マニュアル、結果を確かめるための描画エンジンをエージェントに渡します。