picopilot 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/engine/scaffold/scaffold.ts"],"names":[],"mappings":"AAiBA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,sBAAsB,CAAC;AAEpD;;;;GAIG;AACH,wBAAgB,MAAM,IAAI,MAAM,CAM/B;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,IAAI,MAAM,CAehC;AAED,2FAA2F;AAC3F,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,IAAI,MAAM,CAiLjC;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,YAAY,EAAE,CAO9C"}
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/engine/scaffold/scaffold.ts"],"names":[],"mappings":"AAiBA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,sBAAsB,CAAC;AAEpD;;;;GAIG;AACH,wBAAgB,MAAM,IAAI,MAAM,CAM/B;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,IAAI,MAAM,CAehC;AAED,2FAA2F;AAC3F,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,IAAI,MAAM,CAmNjC;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,YAAY,EAAE,CAO9C"}
@@ -103,10 +103,12 @@ One change = one pass through this loop. Do NOT skip the last step:
103
103
  2. \`picopilot tokens\` to watch the 8192-token budget.
104
104
  3. \`picopilot verify\` (the static gate: tokens + integrity). GREEN means
105
105
  well-formed, it does NOT mean the cart runs or plays.
106
- 4. **SEE it run.** Add a temporary probe to \`main.lua\` and \`picopilot run\`, then
107
- LOOK at the screenshot PNG. Remove the probe after. Or use
108
- \`picopilot playtest run\` to drive input and screenshot live gameplay with no
109
- cart edit. Probe recipe:
106
+ 4. **SEE it PLAY, not just render.** A single screenshot proves a frame draws; it
107
+ does NOT prove the game behaves. So DRIVE it and watch it change over time:
108
+ \`picopilot playtest run\` feeds input and captures live gameplay with no cart
109
+ edit, or add the temporary \`run\` probe below to grab specific frames. Capture
110
+ a sequence and compare frames so you can see MOTION and STATE CHANGE, not one
111
+ still. Probe recipe:
110
112
 
111
113
  \`\`\`lua
112
114
  -- TEMPORARY: screenshot a couple of frames, then signal done so run exits.
@@ -115,11 +117,32 @@ One change = one pass through this loop. Do NOT skip the last step:
115
117
  if t==45 then printh("__PICOPILOT_DONE__") end
116
118
  \`\`\`
117
119
 
118
- **Definition of done: you have looked at a screenshot of the running cart and it
119
- renders what you intended.** A green \`verify\` is NOT done. A \`picopilot run\`
120
- that ends in \`exitReason: timeout\` with NO screenshot is NOT proof it works, it
121
- just means the cart never self-quit; you still have not seen it. Never declare a
122
- game finished on static checks or a bare timeout alone.
120
+ **Definition of done: you have driven YOUR game's core loop and confirmed its
121
+ state actually behaves, not just that it renders.** Screenshots hide logic bugs
122
+ (state that should change on an event but does not, or does not reset when it
123
+ should). So do NOT check a fixed checklist: enumerate the state transitions YOUR
124
+ specific game defines (whatever its own actions, events, and restart do), then
125
+ drive each one and confirm the before/after state is what you intended, before
126
+ AND after a full round or restart. If reading state off the screen is hard, add
127
+ a temporary \`printh\` to dump the values and confirm them directly.
128
+
129
+ Two checks that catch the bugs models ship most:
130
+
131
+ - **Every DISTINCT outcome, in the right direction.** When the game has more than
132
+ one kind of interaction (different objects, hazards, or actions), drive EACH
133
+ kind separately and confirm its effect has the intended SIGN, a reward raises
134
+ what it should, a penalty lowers what it should. Do not verify one happy path
135
+ and assume the rest; the classic bug is two different interactions wired to the
136
+ same or opposite-signed effect (a hazard that adds score instead of costing a
137
+ life).
138
+ - **Is it actually a GAME (can the player lose)?** After driving it, run the
139
+ design self-checks against what you SAW, not just the design on paper: can the
140
+ player genuinely fail (is there real pressure, or did you never come close to
141
+ losing, meaning it is too easy)? does it escalate? is it fair (no unavoidable
142
+ loss)? If you drove a full round and nothing threatened you, tune it before
143
+ calling it done. (The \`game-design-reference\` skill carries these self-checks.)
144
+
145
+ A green \`verify\`, a bare \`timeout\`, or a nice-looking title is NOT done.
123
146
 
124
147
  ## Handing the finished cart to the user
125
148
 
@@ -201,6 +224,17 @@ in \`picopilot.json\`), so you never silently destroy map data you cannot see.
201
224
  - **Cart data:** \`cartdata(id)\`, \`dget(i)\`, \`dset(i,v)\`.
202
225
  - **Debug:** \`printh(str)\` (to host stdout, \`picopilot run\` captures it), \`stat(n)\`.
203
226
 
227
+ **Do NOT guess an API name.** If a function is not in this list and you are not
228
+ SURE it exists, look it up before writing the call, do not invent one. The
229
+ common crashes: random is \`rnd(x)\` (a float in \`[0,x)\`; integer \`flr(rnd(n))\`),
230
+ NOT \`rand\`/\`ranf\`/\`random\`; there is NO built-in collision (write an AABB
231
+ overlap yourself); \`spr\` is \`spr(n,x,y,[w,h],[fx],[fy])\` with NO color/rotate
232
+ arg; persistence is \`cartdata\`+\`dset\`/\`dget\`, not \`poke4\`; bare \`flr\`/\`abs\`/\`min\`
233
+ (no \`math.\`); \`add\`/\`del\`/\`deli\` (no \`table.insert\`). The \`picopilot-code\` skill
234
+ ships a full \`reference/pico8-api.md\` (exact signatures + a wrong-name table);
235
+ read it when unsure. A nil-call crash costs a whole playtest round; the lookup
236
+ does not.
237
+
204
238
  ## Gotchas that silently break carts (these differ from normal Lua/math)
205
239
 
206
240
  These compile and run but behave WRONG. They are the traps models hit most:
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../../src/engine/scaffold/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAC;AACtC,OAAO,EAAC,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AAErD;;;;;;;;;GASG;AAEH,0FAA0F;AAC1F,MAAM,YAAY,GAAG,EAAE,CAAC;AAExB;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,mBAAmB,CAAC;AAEpD;;;;GAIG;AACH,MAAM,UAAU,MAAM;IACrB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CACtB,sDAAsD,YAAY,aAAa,CAC/E,CAAC;IACF,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,gBAAgB,IAAI,CAAC,CAAC;IAChD,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC;AACzB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,OAAO;IACtB,OAAO;;;;;;;;;;;;;CAaP,CAAC;AACF,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,aAAa;IAC5B,OAAO,iBAAiB,EAAE,CAAC;AAC5B,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ;IACvB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+KP,CAAC;AACF,CAAC;AAYD;;;;;;GAMG;AACH,MAAM,UAAU,aAAa;IAC5B,OAAO;QACN,EAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,EAAC;QACpC,EAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,EAAC;QACtC,EAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAC;QACxC,EAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,aAAa,EAAE,EAAC;KAClD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../../src/engine/scaffold/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAC;AACtC,OAAO,EAAC,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AAErD;;;;;;;;;GASG;AAEH,0FAA0F;AAC1F,MAAM,YAAY,GAAG,EAAE,CAAC;AAExB;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,mBAAmB,CAAC;AAEpD;;;;GAIG;AACH,MAAM,UAAU,MAAM;IACrB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CACtB,sDAAsD,YAAY,aAAa,CAC/E,CAAC;IACF,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,gBAAgB,IAAI,CAAC,CAAC;IAChD,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC;AACzB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,OAAO;IACtB,OAAO;;;;;;;;;;;;;CAaP,CAAC;AACF,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,aAAa;IAC5B,OAAO,iBAAiB,EAAE,CAAC;AAC5B,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ;IACvB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiNP,CAAC;AACF,CAAC;AAYD;;;;;;GAMG;AACH,MAAM,UAAU,aAAa;IAC5B,OAAO;QACN,EAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,EAAC;QACpC,EAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,EAAC;QACtC,EAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAC;QACxC,EAAC,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,aAAa,EAAE,EAAC;KAClD,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "picopilot",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "An agent-first toolchain for PICO-8 game development, built on the incur CLI framework.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {
@@ -126,6 +126,33 @@ describe('picopilot init: AGENTS.md carries the curated PICO-8 reference', () =>
126
126
  expect(agents).toContain('__PICOPILOT_DONE__');
127
127
  // A bare timeout is explicitly NOT proof it works.
128
128
  expect(agents.toLowerCase()).toContain('timeout');
129
+ // Done means BEHAVIOUR verified, not just a rendered frame: drive the core
130
+ // loop and confirm state transitions (the bugs a screenshot hides).
131
+ const lower = agents.toLowerCase();
132
+ expect(lower).toContain('state');
133
+ expect(lower).toMatch(/behave|behaviour|behavior/);
134
+ // GENERIC, not a fixed mechanic checklist: it must tell the agent to
135
+ // enumerate ITS OWN game's transitions, so it neither assumes mechanics the
136
+ // game lacks nor checks only a canned list and misses the rest.
137
+ expect(lower).toContain('enumerate');
138
+ expect(lower).toMatch(/your (specific )?game/);
139
+ expect(lower).toContain('do not check a fixed checklist');
140
+ // Two named checks: distinct-outcome correctness (sign of the effect) and
141
+ // is-it-a-game / can-the-player-lose (the too-easy check), both generic.
142
+ expect(lower).toContain('distinct');
143
+ expect(lower).toMatch(/sign|direction/);
144
+ expect(lower).toMatch(/can the player lose|player.*(lose|fail)/);
145
+ expect(lower).toContain('too easy');
146
+ });
147
+
148
+ it('warns against guessing the API and points at the shipped pico8-api reference', () => {
149
+ const lower = agents.toLowerCase();
150
+ expect(lower).toContain('do not guess');
151
+ expect(agents).toContain('reference/pico8-api.md');
152
+ // Names the exact traps from the observed run: rnd-not-rand, no collision.
153
+ expect(agents).toContain('rnd(x)');
154
+ expect(agents).toContain('rand');
155
+ expect(agents.toLowerCase()).toContain('no built-in collision');
129
156
  });
130
157
 
131
158
  it('hands the finished cart off via `pico8 -run`, NOT `serve`', () => {
@@ -112,10 +112,12 @@ One change = one pass through this loop. Do NOT skip the last step:
112
112
  2. \`picopilot tokens\` to watch the 8192-token budget.
113
113
  3. \`picopilot verify\` (the static gate: tokens + integrity). GREEN means
114
114
  well-formed, it does NOT mean the cart runs or plays.
115
- 4. **SEE it run.** Add a temporary probe to \`main.lua\` and \`picopilot run\`, then
116
- LOOK at the screenshot PNG. Remove the probe after. Or use
117
- \`picopilot playtest run\` to drive input and screenshot live gameplay with no
118
- cart edit. Probe recipe:
115
+ 4. **SEE it PLAY, not just render.** A single screenshot proves a frame draws; it
116
+ does NOT prove the game behaves. So DRIVE it and watch it change over time:
117
+ \`picopilot playtest run\` feeds input and captures live gameplay with no cart
118
+ edit, or add the temporary \`run\` probe below to grab specific frames. Capture
119
+ a sequence and compare frames so you can see MOTION and STATE CHANGE, not one
120
+ still. Probe recipe:
119
121
 
120
122
  \`\`\`lua
121
123
  -- TEMPORARY: screenshot a couple of frames, then signal done so run exits.
@@ -124,11 +126,32 @@ One change = one pass through this loop. Do NOT skip the last step:
124
126
  if t==45 then printh("__PICOPILOT_DONE__") end
125
127
  \`\`\`
126
128
 
127
- **Definition of done: you have looked at a screenshot of the running cart and it
128
- renders what you intended.** A green \`verify\` is NOT done. A \`picopilot run\`
129
- that ends in \`exitReason: timeout\` with NO screenshot is NOT proof it works, it
130
- just means the cart never self-quit; you still have not seen it. Never declare a
131
- game finished on static checks or a bare timeout alone.
129
+ **Definition of done: you have driven YOUR game's core loop and confirmed its
130
+ state actually behaves, not just that it renders.** Screenshots hide logic bugs
131
+ (state that should change on an event but does not, or does not reset when it
132
+ should). So do NOT check a fixed checklist: enumerate the state transitions YOUR
133
+ specific game defines (whatever its own actions, events, and restart do), then
134
+ drive each one and confirm the before/after state is what you intended, before
135
+ AND after a full round or restart. If reading state off the screen is hard, add
136
+ a temporary \`printh\` to dump the values and confirm them directly.
137
+
138
+ Two checks that catch the bugs models ship most:
139
+
140
+ - **Every DISTINCT outcome, in the right direction.** When the game has more than
141
+ one kind of interaction (different objects, hazards, or actions), drive EACH
142
+ kind separately and confirm its effect has the intended SIGN, a reward raises
143
+ what it should, a penalty lowers what it should. Do not verify one happy path
144
+ and assume the rest; the classic bug is two different interactions wired to the
145
+ same or opposite-signed effect (a hazard that adds score instead of costing a
146
+ life).
147
+ - **Is it actually a GAME (can the player lose)?** After driving it, run the
148
+ design self-checks against what you SAW, not just the design on paper: can the
149
+ player genuinely fail (is there real pressure, or did you never come close to
150
+ losing, meaning it is too easy)? does it escalate? is it fair (no unavoidable
151
+ loss)? If you drove a full round and nothing threatened you, tune it before
152
+ calling it done. (The \`game-design-reference\` skill carries these self-checks.)
153
+
154
+ A green \`verify\`, a bare \`timeout\`, or a nice-looking title is NOT done.
132
155
 
133
156
  ## Handing the finished cart to the user
134
157
 
@@ -210,6 +233,17 @@ in \`picopilot.json\`), so you never silently destroy map data you cannot see.
210
233
  - **Cart data:** \`cartdata(id)\`, \`dget(i)\`, \`dset(i,v)\`.
211
234
  - **Debug:** \`printh(str)\` (to host stdout, \`picopilot run\` captures it), \`stat(n)\`.
212
235
 
236
+ **Do NOT guess an API name.** If a function is not in this list and you are not
237
+ SURE it exists, look it up before writing the call, do not invent one. The
238
+ common crashes: random is \`rnd(x)\` (a float in \`[0,x)\`; integer \`flr(rnd(n))\`),
239
+ NOT \`rand\`/\`ranf\`/\`random\`; there is NO built-in collision (write an AABB
240
+ overlap yourself); \`spr\` is \`spr(n,x,y,[w,h],[fx],[fy])\` with NO color/rotate
241
+ arg; persistence is \`cartdata\`+\`dset\`/\`dget\`, not \`poke4\`; bare \`flr\`/\`abs\`/\`min\`
242
+ (no \`math.\`); \`add\`/\`del\`/\`deli\` (no \`table.insert\`). The \`picopilot-code\` skill
243
+ ships a full \`reference/pico8-api.md\` (exact signatures + a wrong-name table);
244
+ read it when unsure. A nil-call crash costs a whole playtest round; the lookup
245
+ does not.
246
+
213
247
  ## Gotchas that silently break carts (these differ from normal Lua/math)
214
248
 
215
249
  These compile and run but behave WRONG. They are the traps models hit most:
@@ -21,6 +21,11 @@ function readSkill(name: string): string {
21
21
  return readFileSync(join(skillsSourceDir(), name, 'SKILL.md'), 'utf8');
22
22
  }
23
23
 
24
+ /** Reads a skill RESOURCE file (a non-SKILL.md sibling, e.g. a reference doc). */
25
+ function readRef(skill: string, rel: string): string {
26
+ return readFileSync(join(skillsSourceDir(), skill, rel), 'utf8');
27
+ }
28
+
24
29
  describe('picopilot skills: generation output (the authored discipline skills)', () => {
25
30
  it('ships exactly the named authored skills, each with valid frontmatter', () => {
26
31
  expect([...SKILL_NAMES].sort()).toEqual(
@@ -130,6 +135,22 @@ describe('picopilot skills: generation output (the authored discipline skills)',
130
135
  expect(md).toContain('+=');
131
136
  });
132
137
 
138
+ it('picopilot-code POINTS AT the pico8-api reference (do-not-guess), and the reference names the traps', () => {
139
+ const md = readSkill('picopilot-code');
140
+ // The SKILL.md pointer: an on-demand "do not guess, read the API" pointer.
141
+ expect(md).toContain('reference/pico8-api.md');
142
+ expect(md.toLowerCase()).toMatch(/do not guess|don't guess/);
143
+ // The reference resource itself: exact signatures + the wrong-name table that
144
+ // covers the exact mistakes a weak model made (rnd-not-rand, no collision,
145
+ // spr arg count).
146
+ const api = readRef('picopilot-code', 'reference/pico8-api.md');
147
+ expect(api).toContain('rnd(x)');
148
+ expect(api).toContain('rand');
149
+ expect(api).toContain('rectcol');
150
+ expect(api).toContain('spr(n,x,y,[w,h]');
151
+ expect(api.toLowerCase()).toContain('do not exist');
152
+ });
153
+
133
154
  it('picopilot-art carries the render→look→set→render loop AND the non-multimodal fallback', () => {
134
155
  const md = readSkill('picopilot-art');
135
156
  // The art loop.
@@ -314,6 +335,7 @@ describe('picopilot skills: isolated --install-skills (the load-bearing shared-w
314
335
  expect(existsSync(join(codeSkill, 'SKILL.md'))).toBe(true);
315
336
  for (const res of [
316
337
  'reference/README.md',
338
+ 'reference/pico8-api.md',
317
339
  'reference/platformer.md',
318
340
  'reference/puzzle-grid.md',
319
341
  'reference/twin-stick-arcade.md',
@@ -49,6 +49,19 @@ run. A green verify is well-formed, not proven-to-run, so it points you at
49
49
  `picopilot run`. With shrinko absent, verify is `gate-incapable` (never green),
50
50
  because it cannot check token bloat, the failure this loop exists to catch.
51
51
 
52
+ ## Do NOT guess the API: read `reference/pico8-api.md`
53
+
54
+ The most expensive weak-model mistake is calling a function that does not exist
55
+ (`rand`, `ranf`, `rectcol`, a `spr` with too many args) and only finding out when
56
+ a playtest round crashes with `attempt to call nil value`. When you are unsure a
57
+ function exists or what its arguments are, READ `reference/pico8-api.md` before
58
+ writing the call: it lists the exact names + signatures of the common API and a
59
+ table of the wrong names LLMs reach for (from mainstream Lua / other engines) and
60
+ their real PICO-8 equivalents. Random is `rnd(x)` (a float in `[0,x)`), not
61
+ `rand`/`ranf`; there is NO built-in collision (write an AABB); `spr` is
62
+ `spr(n,x,y,[w,h],[fx],[fy])` with no color/rotate arg. Checking the reference is
63
+ far cheaper than burning a run on a nil-call crash.
64
+
52
65
  ## PICO-8 gotchas that break carts silently
53
66
 
54
67
  These are NOT token issues; they are behaviours that differ from mainstream Lua
@@ -94,4 +107,5 @@ from this skill's `reference/` folder (see `reference/README.md` for the index):
94
107
  - `reference/rpg-menus-dialog.md`: UI state stack, cursor menus, timed text reveal.
95
108
 
96
109
  Each carries a minimal idiomatic implementation and a genre-pitfalls checklist.
97
- Adapt them to your game.
110
+ Adapt them to your game. `reference/pico8-api.md` (the exact API surface) sits in
111
+ the same folder for when you need to look a function up.
@@ -7,7 +7,13 @@ implementations written in idiomatic, token-cheap PICO-8 Lua (see the
7
7
  `picopilot-code` SKILL for the token discipline they follow). They teach the
8
8
  STRUCTURE and the genre-specific pitfalls; adapt them to your game.
9
9
 
10
- Load the ONE that matches your game:
10
+ First, whenever you are unsure a PICO-8 function exists or what its arguments
11
+ are, read the API surface (do NOT guess a name like `rand`/`rectcol`):
12
+
13
+ - `pico8-api.md`: EXACT names + signatures for the whole common API, plus a
14
+ table of wrong names that do NOT exist in PICO-8 and their real equivalents.
15
+
16
+ Then load the ONE genre file that matches your game:
11
17
 
12
18
  - `platformer.md`: side-view gravity/jump, per-axis tile collision, camera
13
19
  follow.
@@ -0,0 +1,118 @@
1
+ # PICO-8 API reference (exact names and signatures)
2
+
3
+ The PICO-8 function surface with EXACT names and argument order. Read this instead of guessing: the #1 way an agent wastes a run is calling a function that does not exist (`rand`, `ranf`, `rectcol`) or passing the wrong arguments, then burning a playtest round on an `attempt to call nil value` crash. If you are unsure a function exists or what its arguments are, look it up HERE first. `[x]` marks an optional argument.
4
+
5
+ ## Do NOT guess: names that do NOT exist in PICO-8
6
+
7
+ These are the wrong names an LLM reaches for out of habit (mainstream Lua, JS, other engines). Each one crashes or silently misbehaves. Use the RIGHT column.
8
+
9
+ | You may type (WRONG) | PICO-8 reality |
10
+ |----------------------|----------------|
11
+ | `rand()`, `ranf()`, `random()`, `math.random` | `rnd(x)` returns a float in `[0,x)`; `rnd()` is `[0,1)`. Integer: `flr(rnd(n))`. `rnd(tbl)` picks a random element. |
12
+ | `rand(lo,hi)` (a range) | there is NO range form. Do `lo+rnd(hi-lo)` (float) or `lo+flr(rnd(hi-lo))` (int). |
13
+ | `rectcol`, `collide`, `intersect`, `overlap` | NO built-in collision. Write your own AABB (see below). |
14
+ | `math.floor`, `math.abs`, `math.min`, `math.max`, `math.sqrt` | bare `flr`, `abs`, `min`, `max`, `sqrt` (no `math.` table). |
15
+ | `table.insert`, `table.remove` | `add(t,v)`, `del(t,v)` (by value), `deli(t,i)` (by index). |
16
+ | `string.sub`, `s:sub(..)`, `#s` for chars | `sub(s,i,j)` (1-based, inclusive both ends). |
17
+ | `print(x,y,...)` with x,y swapped, or `printf` | `print(str,[x,y,[col]])` (string FIRST). |
18
+ | `pset(x,y,col)` ok, but `pget(x,y,col)` | `pget(x,y)` takes NO color (it READS one). |
19
+ | `2*pi`, `sin(radians)` | `sin`/`cos` take TURNS `0..1`, and `sin` is INVERTED. A full circle is `t:0->1`. Do NOT multiply by pi. |
20
+ | `poke4` for "save a number" | persistence is `cartdata(id)` then `dset(i,v)` / `dget(i)`. (`poke2/poke4` exist but are raw 16/32-bit RAM writes, not a save API.) |
21
+ | `//` (Lua 5.3 int-divide) | `\` is integer divide (`5\2`==2). `/` does NOT truncate. |
22
+ | shadowing builtins as locals: `local type`, `local add`, `local t=...` near `t=time()` | do not name locals after builtins/globals you use (`type`, `add`, `del`, `t`, `x`, `y`, `pal`, `time`, `sub`). It silently breaks calls to them. |
23
+
24
+ ## AABB overlap (there is no built-in collision, write this)
25
+
26
+ ```lua
27
+ function hit(ax,ay,aw,ah, bx,by,bw,bh)
28
+ return ax<bx+bw and bx<ax+aw and ay<by+bh and by<ay+ah
29
+ end
30
+ ```
31
+
32
+ ## Graphics
33
+
34
+ - `cls([col])` clear screen (to `col`, default 0).
35
+ - `spr(n,x,y,[w,h],[flip_x],[flip_y])` draw sprite `n` at `x,y`. `w,h` are in SPRITES (default 1,1), NOT pixels. There is NO color/rotate/scale arg; that is `sspr`/`pal` territory. Do NOT pass extra args.
36
+ - `sspr(sx,sy,sw,sh,dx,dy,[dw,dh],[flip_x],[flip_y])` stretch a spritesheet rect.
37
+ - `map(cx,cy,sx,sy,cw,ch,[layer])` draw map cels `cx,cy` (size `cw,ch`) at screen `sx,sy`.
38
+ - `pset(x,y,[col])`, `pget(x,y)` (pget reads, no col arg).
39
+ - `sget(x,y)`, `sset(x,y,[col])` read/write a spritesheet pixel.
40
+ - `print(str,[x,y,[col]])` string is the FIRST arg. Returns the x cursor after.
41
+ - `rect(x0,y0,x1,y1,[col])`, `rectfill(x0,y0,x1,y1,[col])` (two CORNERS, not x,y,w,h).
42
+ - `circ(x,y,r,[col])`, `circfill(x,y,r,[col])`.
43
+ - `oval(x0,y0,x1,y1,[col])`, `ovalfill(...)` (bounding-box corners).
44
+ - `line(x0,y0,x1,y1,[col])`.
45
+ - `pal([c0,c1,[p]])` remap color `c0`->`c1` (p=0 draw, 1 screen). No-arg `pal()` RESETS.
46
+ - `palt([col,t])` set color `col` transparent (`t` true/false). No-arg resets (0 transparent).
47
+ - `camera([x,y])` offset all later draws. No-arg resets.
48
+ - `clip([x,y,w,h])` clip rect. No-arg resets.
49
+ - `color([col])` set the default pen color.
50
+ - `fillp([pat])` set the fill pattern for shape fns.
51
+ - `flip()` present the frame (rarely needed; `_draw` flips for you).
52
+ - `cursor([x,y,[col]])`, `fget`/`fset` (flags, under Map).
53
+
54
+ ## Map and sprite flags
55
+
56
+ - `mget(x,y)` read the map cel (sprite index) at tile `x,y`.
57
+ - `mset(x,y,v)` write a map cel.
58
+ - `fget(n,[f])` get sprite `n` flags (all as a bitfield, or bit `f`).
59
+ - `fset(n,[f],v)` set sprite flags.
60
+
61
+ ## Input
62
+
63
+ - `btn([i,[p]])` is button `i` HELD? (i=0..5 = left,right,up,down,O,X; p=player 0..7). No-arg returns a bitfield.
64
+ - `btnp([i,[p]])` was button `i` just PRESSED this frame (with auto-repeat)?
65
+ - Button ids: 0 left, 1 right, 2 up, 3 down, 4 O (z/c/n), 5 X (x/v/m).
66
+
67
+ ## Audio
68
+
69
+ - `sfx(n,[channel,[offset,[length]]])` play sfx `n`. `n=-1` stops. `channel=-1` any, `-2` stop on the channel.
70
+ - `music([n,[fade_ms,[channel_mask]]])` play music pattern `n`. `n=-1` stops.
71
+
72
+ ## Math (all bare, no `math.` table)
73
+
74
+ - `flr(x)` floor (round toward -inf). `ceil(x)` = `-flr(-x)`.
75
+ - `abs`, `min(a,b)`, `max(a,b)`, `mid(a,b,c)` (the middle value = idiomatic CLAMP), `sgn(x)`.
76
+ - `sqrt(x)`, `rnd([x])` (float `[0,x)`; `rnd(tbl)` picks an element), `srand(x)` (seed).
77
+ - `sin(t)`, `cos(t)` take TURNS `0..1`; `sin` is INVERTED (screen +y down). `atan2(dx,dy)` returns a turn `0..1`.
78
+ - `%` modulo, `\` integer divide, `&` `|` `^^` `~` `<<` `>>` bitops (Lua-5.3-style ops are NOT all present; use these).
79
+
80
+ ## Tables and iteration (1-BASED)
81
+
82
+ - `add(t,v,[i])` append (or insert at `i`), returns `v`.
83
+ - `del(t,v)` remove the first element EQUAL to `v` (by value).
84
+ - `deli(t,[i])` remove the element at INDEX `i` (default last), returns it.
85
+ - `count(t,[v])` length (or count of `v`). `#t` also works for a contiguous array.
86
+ - `all(t)` iterator for `for x in all(t) do`. `foreach(t,fn)` call `fn(v)` per element.
87
+ - `pairs(t)`, `ipairs(t)`, `next(t)`. `unpack(t)`, `pack(...)`.
88
+ - Deleting the CURRENT item is safe in `foreach` and in a DESCENDING `for i=#t,1,-1`; an ASCENDING `for i=1,#t` ERRORS once the list shrinks.
89
+
90
+ ## Strings
91
+
92
+ - `sub(s,i,[j])` substring, 1-based, INCLUSIVE both ends (`sub("abc",2,3)`=="bc").
93
+ - `#s` length, `..` concat, `tostr(x,[hex])`, `tonum(s)`, `chr(n)`, `ord(s,[i])`.
94
+ - `split(s,[sep,[convert]])` -> a 1-based array (great for cheap data tables). NO Lua `string` stdlib (`string.format`, `gsub`, etc. do NOT exist).
95
+
96
+ ## Memory (raw RAM; wrong addresses corrupt gfx/sound/state)
97
+
98
+ - `peek(addr)`, `poke(addr,v)` one byte. `peek2/poke2` 16-bit, `peek4/poke4` 32-bit (fixed-point).
99
+ - `memcpy(dst,src,len)`, `memset(dst,val,len)`, `reload(...)`, `cstore(...)`.
100
+ - Only poke DOCUMENTED addresses (see the memory map in `AGENTS.md`). Prefer the draw functions above over poking the screen.
101
+
102
+ ## Cart data (persistent save)
103
+
104
+ - `cartdata(id)` register a 64-slot save block (call ONCE in `_init`; `id` is a unique string).
105
+ - `dget(i)` read slot `i` (0..63), `dset(i,v)` write it. This is how you save a high score, NOT `poke4`.
106
+
107
+ ## System / debug
108
+
109
+ - `time()` / `t()` seconds since start (a fixed-point float). Do NOT name a local `time`.
110
+ - `stat(n)` system info (`stat(6)` is the `-p`/playtest input string; others: memory, fps, etc.).
111
+ - `printh(str,[filename])` print to the HOST stdout (picopilot `run`/`playtest` capture this) -> your main debug channel.
112
+ - `menuitem(...)`, `extcmd(cmd)` (`extcmd("screen")` screenshots, used by the run probe), `stop()`, `assert(cond,[msg])`, `trace()`.
113
+
114
+ ## The execution loop
115
+
116
+ - `_init()` once at start. `_update()` at 30fps (or `_update60()` at 60fps, not both). `_draw()` once per visible frame. Mutate state ONLY in `_update`; `_draw` can be skipped under load.
117
+
118
+ For the behaviours that differ from mainstream Lua/math (turns-not-radians, integer divide, 1-based tables, persistent draw state, the `and/or` ternary trap), see the gotchas in `AGENTS.md`. This file is WHAT EXISTS; that is HOW IT BEHAVES.