picopilot 0.0.0 → 0.1.1

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.
Files changed (334) hide show
  1. package/LICENSE +661 -0
  2. package/dist/bin.d.ts +3 -0
  3. package/dist/bin.d.ts.map +1 -0
  4. package/dist/bin.js +4 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/cli.d.ts +15 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +59 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/commands/audio.d.ts +17 -0
  11. package/dist/commands/audio.d.ts.map +1 -0
  12. package/dist/commands/audio.js +315 -0
  13. package/dist/commands/audio.js.map +1 -0
  14. package/dist/commands/export.d.ts +36 -0
  15. package/dist/commands/export.d.ts.map +1 -0
  16. package/dist/commands/export.js +257 -0
  17. package/dist/commands/export.js.map +1 -0
  18. package/dist/commands/gfx.d.ts +28 -0
  19. package/dist/commands/gfx.d.ts.map +1 -0
  20. package/dist/commands/gfx.js +521 -0
  21. package/dist/commands/gfx.js.map +1 -0
  22. package/dist/commands/init.d.ts +34 -0
  23. package/dist/commands/init.d.ts.map +1 -0
  24. package/dist/commands/init.js +151 -0
  25. package/dist/commands/init.js.map +1 -0
  26. package/dist/commands/lint.d.ts +20 -0
  27. package/dist/commands/lint.d.ts.map +1 -0
  28. package/dist/commands/lint.js +90 -0
  29. package/dist/commands/lint.js.map +1 -0
  30. package/dist/commands/minify.d.ts +20 -0
  31. package/dist/commands/minify.d.ts.map +1 -0
  32. package/dist/commands/minify.js +129 -0
  33. package/dist/commands/minify.js.map +1 -0
  34. package/dist/commands/music.d.ts +13 -0
  35. package/dist/commands/music.d.ts.map +1 -0
  36. package/dist/commands/music.js +241 -0
  37. package/dist/commands/music.js.map +1 -0
  38. package/dist/commands/playtest.d.ts +29 -0
  39. package/dist/commands/playtest.d.ts.map +1 -0
  40. package/dist/commands/playtest.js +631 -0
  41. package/dist/commands/playtest.js.map +1 -0
  42. package/dist/commands/run.d.ts +30 -0
  43. package/dist/commands/run.d.ts.map +1 -0
  44. package/dist/commands/run.js +224 -0
  45. package/dist/commands/run.js.map +1 -0
  46. package/dist/commands/serve.d.ts +50 -0
  47. package/dist/commands/serve.d.ts.map +1 -0
  48. package/dist/commands/serve.js +213 -0
  49. package/dist/commands/serve.js.map +1 -0
  50. package/dist/commands/sfx.d.ts +13 -0
  51. package/dist/commands/sfx.d.ts.map +1 -0
  52. package/dist/commands/sfx.js +254 -0
  53. package/dist/commands/sfx.js.map +1 -0
  54. package/dist/commands/tokens.d.ts +36 -0
  55. package/dist/commands/tokens.d.ts.map +1 -0
  56. package/dist/commands/tokens.js +129 -0
  57. package/dist/commands/tokens.js.map +1 -0
  58. package/dist/commands/verify.d.ts +57 -0
  59. package/dist/commands/verify.d.ts.map +1 -0
  60. package/dist/commands/verify.js +213 -0
  61. package/dist/commands/verify.js.map +1 -0
  62. package/dist/commands/version.d.ts +12 -0
  63. package/dist/commands/version.d.ts.map +1 -0
  64. package/dist/commands/version.js +24 -0
  65. package/dist/commands/version.js.map +1 -0
  66. package/dist/engine/audio/index.d.ts +14 -0
  67. package/dist/engine/audio/index.d.ts.map +1 -0
  68. package/dist/engine/audio/index.js +14 -0
  69. package/dist/engine/audio/index.js.map +1 -0
  70. package/dist/engine/audio/music.d.ts +116 -0
  71. package/dist/engine/audio/music.d.ts.map +1 -0
  72. package/dist/engine/audio/music.js +138 -0
  73. package/dist/engine/audio/music.js.map +1 -0
  74. package/dist/engine/audio/section.d.ts +34 -0
  75. package/dist/engine/audio/section.d.ts.map +1 -0
  76. package/dist/engine/audio/section.js +67 -0
  77. package/dist/engine/audio/section.js.map +1 -0
  78. package/dist/engine/audio/sfx.d.ts +141 -0
  79. package/dist/engine/audio/sfx.d.ts.map +1 -0
  80. package/dist/engine/audio/sfx.js +506 -0
  81. package/dist/engine/audio/sfx.js.map +1 -0
  82. package/dist/engine/cart/cart.d.ts +75 -0
  83. package/dist/engine/cart/cart.d.ts.map +1 -0
  84. package/dist/engine/cart/cart.js +153 -0
  85. package/dist/engine/cart/cart.js.map +1 -0
  86. package/dist/engine/cart/errors.d.ts +29 -0
  87. package/dist/engine/cart/errors.d.ts.map +1 -0
  88. package/dist/engine/cart/errors.js +26 -0
  89. package/dist/engine/cart/errors.js.map +1 -0
  90. package/dist/engine/cart/gfx.d.ts +79 -0
  91. package/dist/engine/cart/gfx.d.ts.map +1 -0
  92. package/dist/engine/cart/gfx.js +171 -0
  93. package/dist/engine/cart/gfx.js.map +1 -0
  94. package/dist/engine/cart/index.d.ts +15 -0
  95. package/dist/engine/cart/index.d.ts.map +1 -0
  96. package/dist/engine/cart/index.js +15 -0
  97. package/dist/engine/cart/index.js.map +1 -0
  98. package/dist/engine/cart/map.d.ts +46 -0
  99. package/dist/engine/cart/map.d.ts.map +1 -0
  100. package/dist/engine/cart/map.js +118 -0
  101. package/dist/engine/cart/map.js.map +1 -0
  102. package/dist/engine/config/config.d.ts +74 -0
  103. package/dist/engine/config/config.d.ts.map +1 -0
  104. package/dist/engine/config/config.js +103 -0
  105. package/dist/engine/config/config.js.map +1 -0
  106. package/dist/engine/config/index.d.ts +7 -0
  107. package/dist/engine/config/index.d.ts.map +1 -0
  108. package/dist/engine/config/index.js +7 -0
  109. package/dist/engine/config/index.js.map +1 -0
  110. package/dist/engine/gfx/grid.d.ts +51 -0
  111. package/dist/engine/gfx/grid.d.ts.map +1 -0
  112. package/dist/engine/gfx/grid.js +116 -0
  113. package/dist/engine/gfx/grid.js.map +1 -0
  114. package/dist/engine/gfx/index.d.ts +17 -0
  115. package/dist/engine/gfx/index.d.ts.map +1 -0
  116. package/dist/engine/gfx/index.js +17 -0
  117. package/dist/engine/gfx/index.js.map +1 -0
  118. package/dist/engine/gfx/label.d.ts +34 -0
  119. package/dist/engine/gfx/label.d.ts.map +1 -0
  120. package/dist/engine/gfx/label.js +190 -0
  121. package/dist/engine/gfx/label.js.map +1 -0
  122. package/dist/engine/gfx/overlap.d.ts +42 -0
  123. package/dist/engine/gfx/overlap.d.ts.map +1 -0
  124. package/dist/engine/gfx/overlap.js +57 -0
  125. package/dist/engine/gfx/overlap.js.map +1 -0
  126. package/dist/engine/gfx/palette.d.ts +29 -0
  127. package/dist/engine/gfx/palette.d.ts.map +1 -0
  128. package/dist/engine/gfx/palette.js +46 -0
  129. package/dist/engine/gfx/palette.js.map +1 -0
  130. package/dist/engine/gfx/png.d.ts +31 -0
  131. package/dist/engine/gfx/png.d.ts.map +1 -0
  132. package/dist/engine/gfx/png.js +165 -0
  133. package/dist/engine/gfx/png.js.map +1 -0
  134. package/dist/engine/gfx/render.d.ts +42 -0
  135. package/dist/engine/gfx/render.d.ts.map +1 -0
  136. package/dist/engine/gfx/render.js +72 -0
  137. package/dist/engine/gfx/render.js.map +1 -0
  138. package/dist/engine/index.d.ts +22 -0
  139. package/dist/engine/index.d.ts.map +1 -0
  140. package/dist/engine/index.js +22 -0
  141. package/dist/engine/index.js.map +1 -0
  142. package/dist/engine/pico8/adapter.d.ts +267 -0
  143. package/dist/engine/pico8/adapter.d.ts.map +1 -0
  144. package/dist/engine/pico8/adapter.js +37 -0
  145. package/dist/engine/pico8/adapter.js.map +1 -0
  146. package/dist/engine/pico8/drive.d.ts +308 -0
  147. package/dist/engine/pico8/drive.d.ts.map +1 -0
  148. package/dist/engine/pico8/drive.js +581 -0
  149. package/dist/engine/pico8/drive.js.map +1 -0
  150. package/dist/engine/pico8/harness.d.ts +146 -0
  151. package/dist/engine/pico8/harness.d.ts.map +1 -0
  152. package/dist/engine/pico8/harness.js +251 -0
  153. package/dist/engine/pico8/harness.js.map +1 -0
  154. package/dist/engine/pico8/index.d.ts +21 -0
  155. package/dist/engine/pico8/index.d.ts.map +1 -0
  156. package/dist/engine/pico8/index.js +21 -0
  157. package/dist/engine/pico8/index.js.map +1 -0
  158. package/dist/engine/pico8/sentinel.d.ts +63 -0
  159. package/dist/engine/pico8/sentinel.d.ts.map +1 -0
  160. package/dist/engine/pico8/sentinel.js +86 -0
  161. package/dist/engine/pico8/sentinel.js.map +1 -0
  162. package/dist/engine/pico8/session-daemon-entry.d.ts +10 -0
  163. package/dist/engine/pico8/session-daemon-entry.d.ts.map +1 -0
  164. package/dist/engine/pico8/session-daemon-entry.js +11 -0
  165. package/dist/engine/pico8/session-daemon-entry.js.map +1 -0
  166. package/dist/engine/pico8/session-daemon-main.d.ts +44 -0
  167. package/dist/engine/pico8/session-daemon-main.d.ts.map +1 -0
  168. package/dist/engine/pico8/session-daemon-main.js +71 -0
  169. package/dist/engine/pico8/session-daemon-main.js.map +1 -0
  170. package/dist/engine/pico8/session-daemon.d.ts +68 -0
  171. package/dist/engine/pico8/session-daemon.d.ts.map +1 -0
  172. package/dist/engine/pico8/session-daemon.js +264 -0
  173. package/dist/engine/pico8/session-daemon.js.map +1 -0
  174. package/dist/engine/pico8/session.d.ts +140 -0
  175. package/dist/engine/pico8/session.d.ts.map +1 -0
  176. package/dist/engine/pico8/session.js +250 -0
  177. package/dist/engine/pico8/session.js.map +1 -0
  178. package/dist/engine/pico8/shell.d.ts +103 -0
  179. package/dist/engine/pico8/shell.d.ts.map +1 -0
  180. package/dist/engine/pico8/shell.js +324 -0
  181. package/dist/engine/pico8/shell.js.map +1 -0
  182. package/dist/engine/pico8/supervisor.d.ts +137 -0
  183. package/dist/engine/pico8/supervisor.d.ts.map +1 -0
  184. package/dist/engine/pico8/supervisor.js +140 -0
  185. package/dist/engine/pico8/supervisor.js.map +1 -0
  186. package/dist/engine/scaffold/index.d.ts +8 -0
  187. package/dist/engine/scaffold/index.d.ts.map +1 -0
  188. package/dist/engine/scaffold/index.js +8 -0
  189. package/dist/engine/scaffold/index.js.map +1 -0
  190. package/dist/engine/scaffold/scaffold.d.ts +49 -0
  191. package/dist/engine/scaffold/scaffold.d.ts.map +1 -0
  192. package/dist/engine/scaffold/scaffold.js +259 -0
  193. package/dist/engine/scaffold/scaffold.js.map +1 -0
  194. package/dist/engine/shrinko/adapter.d.ts +167 -0
  195. package/dist/engine/shrinko/adapter.d.ts.map +1 -0
  196. package/dist/engine/shrinko/adapter.js +107 -0
  197. package/dist/engine/shrinko/adapter.js.map +1 -0
  198. package/dist/engine/shrinko/index.d.ts +13 -0
  199. package/dist/engine/shrinko/index.d.ts.map +1 -0
  200. package/dist/engine/shrinko/index.js +13 -0
  201. package/dist/engine/shrinko/index.js.map +1 -0
  202. package/dist/engine/shrinko/shell.d.ts +69 -0
  203. package/dist/engine/shrinko/shell.d.ts.map +1 -0
  204. package/dist/engine/shrinko/shell.js +156 -0
  205. package/dist/engine/shrinko/shell.js.map +1 -0
  206. package/dist/engine/skills/index.d.ts +8 -0
  207. package/dist/engine/skills/index.d.ts.map +1 -0
  208. package/dist/engine/skills/index.js +8 -0
  209. package/dist/engine/skills/index.js.map +1 -0
  210. package/dist/engine/skills/skills.d.ts +104 -0
  211. package/dist/engine/skills/skills.d.ts.map +1 -0
  212. package/dist/engine/skills/skills.js +149 -0
  213. package/dist/engine/skills/skills.js.map +1 -0
  214. package/dist/index.d.ts +10 -0
  215. package/dist/index.d.ts.map +1 -0
  216. package/dist/index.js +10 -0
  217. package/dist/index.js.map +1 -0
  218. package/dist/version.d.ts +2 -0
  219. package/dist/version.d.ts.map +1 -0
  220. package/dist/version.js +19 -0
  221. package/dist/version.js.map +1 -0
  222. package/package.json +48 -2
  223. package/src/bin.ts +4 -0
  224. package/src/cli.ts +61 -0
  225. package/src/commands/audio.test.ts +280 -0
  226. package/src/commands/audio.ts +410 -0
  227. package/src/commands/export.test.ts +310 -0
  228. package/src/commands/export.ts +316 -0
  229. package/src/commands/gfx-io.test.ts +178 -0
  230. package/src/commands/gfx.test.ts +470 -0
  231. package/src/commands/gfx.ts +645 -0
  232. package/src/commands/init.test.ts +482 -0
  233. package/src/commands/init.ts +193 -0
  234. package/src/commands/lint.test.ts +137 -0
  235. package/src/commands/lint.ts +117 -0
  236. package/src/commands/minify.test.ts +166 -0
  237. package/src/commands/minify.ts +156 -0
  238. package/src/commands/music.test.ts +272 -0
  239. package/src/commands/music.ts +288 -0
  240. package/src/commands/playtest.test.ts +317 -0
  241. package/src/commands/playtest.ts +773 -0
  242. package/src/commands/run.test.ts +264 -0
  243. package/src/commands/run.ts +285 -0
  244. package/src/commands/serve.test.ts +227 -0
  245. package/src/commands/serve.ts +272 -0
  246. package/src/commands/sfx.test.ts +349 -0
  247. package/src/commands/sfx.ts +300 -0
  248. package/src/commands/tokens.test.ts +212 -0
  249. package/src/commands/tokens.ts +168 -0
  250. package/src/commands/verify.test.ts +238 -0
  251. package/src/commands/verify.ts +256 -0
  252. package/src/commands/version.test.ts +70 -0
  253. package/src/commands/version.ts +24 -0
  254. package/src/engine/audio/index.ts +47 -0
  255. package/src/engine/audio/music.test.ts +188 -0
  256. package/src/engine/audio/music.ts +218 -0
  257. package/src/engine/audio/section.test.ts +124 -0
  258. package/src/engine/audio/section.ts +79 -0
  259. package/src/engine/audio/sfx.test.ts +410 -0
  260. package/src/engine/audio/sfx.ts +694 -0
  261. package/src/engine/cart/cart.test.ts +304 -0
  262. package/src/engine/cart/cart.ts +211 -0
  263. package/src/engine/cart/errors.ts +39 -0
  264. package/src/engine/cart/fixtures/crlf.p8 +6 -0
  265. package/src/engine/cart/fixtures/full.p8 +174 -0
  266. package/src/engine/cart/fixtures/header-only.p8 +2 -0
  267. package/src/engine/cart/fixtures/malformed-no-header.p8 +2 -0
  268. package/src/engine/cart/fixtures/minimal.p8 +10 -0
  269. package/src/engine/cart/fixtures/no-trailing-newline.p8 +6 -0
  270. package/src/engine/cart/gfx.ts +195 -0
  271. package/src/engine/cart/index.ts +27 -0
  272. package/src/engine/cart/map.ts +134 -0
  273. package/src/engine/config/config.ts +115 -0
  274. package/src/engine/config/index.ts +12 -0
  275. package/src/engine/gfx/grid.test.ts +128 -0
  276. package/src/engine/gfx/grid.ts +136 -0
  277. package/src/engine/gfx/index.ts +34 -0
  278. package/src/engine/gfx/label.test.ts +105 -0
  279. package/src/engine/gfx/label.ts +229 -0
  280. package/src/engine/gfx/overlap.test.ts +71 -0
  281. package/src/engine/gfx/overlap.ts +66 -0
  282. package/src/engine/gfx/palette.test.ts +35 -0
  283. package/src/engine/gfx/palette.ts +54 -0
  284. package/src/engine/gfx/png.test.ts +111 -0
  285. package/src/engine/gfx/png.ts +196 -0
  286. package/src/engine/gfx/render.test.ts +180 -0
  287. package/src/engine/gfx/render.ts +106 -0
  288. package/src/engine/index.ts +21 -0
  289. package/src/engine/pico8/adapter.ts +303 -0
  290. package/src/engine/pico8/drive.test.ts +415 -0
  291. package/src/engine/pico8/drive.ts +729 -0
  292. package/src/engine/pico8/harness.test.ts +181 -0
  293. package/src/engine/pico8/harness.ts +354 -0
  294. package/src/engine/pico8/index.ts +20 -0
  295. package/src/engine/pico8/sentinel.test.ts +80 -0
  296. package/src/engine/pico8/sentinel.ts +93 -0
  297. package/src/engine/pico8/session-daemon-entry.ts +12 -0
  298. package/src/engine/pico8/session-daemon-main.ts +98 -0
  299. package/src/engine/pico8/session-daemon.test.ts +212 -0
  300. package/src/engine/pico8/session-daemon.ts +306 -0
  301. package/src/engine/pico8/session.test.ts +239 -0
  302. package/src/engine/pico8/session.ts +321 -0
  303. package/src/engine/pico8/shell.test.ts +583 -0
  304. package/src/engine/pico8/shell.ts +412 -0
  305. package/src/engine/pico8/supervisor.test.ts +105 -0
  306. package/src/engine/pico8/supervisor.ts +197 -0
  307. package/src/engine/scaffold/index.ts +15 -0
  308. package/src/engine/scaffold/scaffold.ts +278 -0
  309. package/src/engine/shrinko/adapter.ts +241 -0
  310. package/src/engine/shrinko/fixtures/count-no-compressed.txt +2 -0
  311. package/src/engine/shrinko/fixtures/count-over-budget.txt +3 -0
  312. package/src/engine/shrinko/fixtures/count-under-budget.txt +3 -0
  313. package/src/engine/shrinko/index.ts +33 -0
  314. package/src/engine/shrinko/shell.ts +224 -0
  315. package/src/engine/shrinko/shrinko.test.ts +182 -0
  316. package/src/engine/skills/index.ts +14 -0
  317. package/src/engine/skills/skills.test.ts +376 -0
  318. package/src/engine/skills/skills.ts +205 -0
  319. package/src/index.ts +13 -0
  320. package/src/skills/game-design-reference/SKILL.md +70 -0
  321. package/src/skills/game-jam/SKILL.md +45 -0
  322. package/src/skills/picopilot-art/SKILL.md +62 -0
  323. package/src/skills/picopilot-audio/SKILL.md +194 -0
  324. package/src/skills/picopilot-code/SKILL.md +97 -0
  325. package/src/skills/picopilot-code/reference/README.md +42 -0
  326. package/src/skills/picopilot-code/reference/mode7-racing.md +95 -0
  327. package/src/skills/picopilot-code/reference/platformer.md +119 -0
  328. package/src/skills/picopilot-code/reference/puzzle-grid.md +106 -0
  329. package/src/skills/picopilot-code/reference/rpg-menus-dialog.md +122 -0
  330. package/src/skills/picopilot-code/reference/top-down-adventure.md +117 -0
  331. package/src/skills/picopilot-code/reference/twin-stick-arcade.md +128 -0
  332. package/src/skills/picopilot-debug/SKILL.md +195 -0
  333. package/src/skills/picopilot-overview/SKILL.md +52 -0
  334. package/src/version.ts +22 -0
@@ -0,0 +1,308 @@
1
+ /**
2
+ * The `playtest` DRIVE-transform (ADR-0011): the PURE, CI-testable core that
3
+ * turns an ARBITRARY cart's Lua + a driven-run SPEC into a THROWAWAY driven cart
4
+ * whose input and frame loop the harness OWNS. This is the load-bearing seam of
5
+ * `picopilot playtest`, kept pure (cart text + spec in -> cart text + encoded
6
+ * command blocks out) so its correctness (the `btn`/`btnp` shim, the
7
+ * `_update`/`_draw` wrap, btnp-edge reconstruction, the opcode/block codec, the
8
+ * opt-in `srand`) is unit-tested WITHOUT the paid PICO-8 binary. Only the live
9
+ * drive-and-capture needs a real session (the manual/opt-in tier, ADR-0011).
10
+ *
11
+ * The recipe is DECIDED + spike-verified (PICO-8 v0.2.7). This module IMPLEMENTS
12
+ * it; it is not open design. The three load-bearing verified facts:
13
+ * 1. Redefine the global `btn`/`btnp` to read a per-frame held-buttons byte
14
+ * from `serial(0x804)`, RECONSTRUCTING `btnp` EDGES from the held level (a
15
+ * single scripted press = exactly one `btnp` true frame, not held-repeat).
16
+ * 2. The host<->cart transport is FIXED-SIZE command blocks (small unpadded
17
+ * writes to a live `-x` stdin coalesce/drop in the OS pipe buffer), drained
18
+ * ONE block per frame gated on the step-budget, with a `printh` ACK per
19
+ * completed command (the resumable session waits on the ACK; the one-shot
20
+ * ignores it).
21
+ * 3. The harness OWNS the frame loop: it wraps `_update`/`_update60`/`_draw` so
22
+ * a STEP advances exactly N frames and a PAUSE (budget 0) FREEZES the game
23
+ * (its callbacks skipped -> all state held, incl. logic in `_draw`).
24
+ *
25
+ * The one-shot command sends the whole script up front and exits; the cart-side
26
+ * machine here is identical to what the resumable session reuses (ADR-0011).
27
+ */
28
+ /**
29
+ * A structured reason a drive harness could not be built (a target refusal, not
30
+ * a crash). Surfaced by the command as incur's `error({code,...})` envelope.
31
+ */
32
+ export type DriveErrorCode =
33
+ /** The cart could not be parsed (not a valid .p8). */
34
+ 'playtest-cart-parse-error'
35
+ /** The input script named an out-of-range button bit or frame. */
36
+ | 'playtest-input-invalid';
37
+ /** A structured drive-harness-build failure. */
38
+ export declare class DriveError extends Error {
39
+ readonly code: DriveErrorCode;
40
+ constructor(code: DriveErrorCode, message: string);
41
+ }
42
+ /**
43
+ * The PICO-8 button bit order (the `btn(i)`/`btnp(i)` index), used by the
44
+ * held-buttons byte the harness pipes over `serial(0x804)`: bit `i` set = button
45
+ * `i` held this frame. Matches PICO-8's own `btn` indices (verified).
46
+ */
47
+ export declare const BUTTON_BITS: {
48
+ readonly left: 0;
49
+ readonly right: 1;
50
+ readonly up: 2;
51
+ readonly down: 3;
52
+ readonly o: 4;
53
+ readonly x: 5;
54
+ };
55
+ /** The valid button-bit range [0,5] a held-buttons byte can set. */
56
+ export declare const BUTTON_BIT_MIN = 0;
57
+ export declare const BUTTON_BIT_MAX = 5;
58
+ /** The accepted button NAMES (and their bits) the live `input` verb parses. */
59
+ export declare const BUTTON_NAMES: Readonly<Record<string, number>>;
60
+ /**
61
+ * Parses a live-session `input` button spec into a single HELD-buttons byte: a
62
+ * comma/space-separated list of button NAMES (`left right up down o x`, plus the
63
+ * single-letter aliases `l r u d`), e.g. `"right o"` = hold Right + O this turn.
64
+ * Case-insensitive; an empty spec is the byte 0 (release all). An unknown name is
65
+ * a structured {@link DriveError} (`playtest-input-invalid`), never a silent skip.
66
+ * This is the SESSION-facing input surface (per-turn held level); the one-shot's
67
+ * {@link parseInputScript} is the frame-scripted surface. Both land on the same
68
+ * held byte -> {@link btnpEdges} machine.
69
+ */
70
+ export declare function parseButtons(spec: string): number;
71
+ /**
72
+ * The FIXED command-block size in bytes (ADR-0011). Load-bearing: small unpadded
73
+ * writes to a live `pico8 -x` stdin coalesce/drop in the OS pipe buffer, so every
74
+ * command is sent as a block of exactly this many bytes (opcode + arg + pad). The
75
+ * cart reads exactly this many bytes per frame and buffers a partial block across
76
+ * frames. 4 bytes comfortably clears the 1-2 byte coalesce window the spike hit.
77
+ */
78
+ export declare const BLOCK_SIZE = 4;
79
+ /**
80
+ * The tagged opcodes carried in a command block's first byte (ADR-0011). One
81
+ * command per block; the cart drains one block per frame against the step budget.
82
+ * - STEP<n>: advance exactly `n` frames (the arg byte), then pause.
83
+ * - INPUT<b>: set the held-buttons byte to `b` (the arg byte) for the next STEP.
84
+ * - SHOT: screenshot the current (frozen) frame.
85
+ * - PAUSE: set the step budget to 0 (freeze; callbacks skipped).
86
+ * - QUIT: print the done-sentinel so the launcher kills PICO-8.
87
+ */
88
+ export declare const OPCODE: {
89
+ readonly step: 1;
90
+ readonly input: 2;
91
+ readonly shot: 3;
92
+ readonly pause: 4;
93
+ readonly quit: 5;
94
+ };
95
+ export type OpcodeName = keyof typeof OPCODE;
96
+ /**
97
+ * The cart-side ACK lines the drive shim `printh`s when a command COMPLETES
98
+ * (ADR-0011: the resumable session WAITS for the ACK before sending the next
99
+ * command; the one-shot ignores them and waits only for the QUIT sentinel). One
100
+ * source of truth shared by the Lua shim (which prints them) and the host-side
101
+ * {@link import('./session.js').DriveSession} (which waits on them), so the
102
+ * handshake can never drift between the two ends.
103
+ *
104
+ * The load-bearing one is {@link ACK.stepDone}: a STEP's ACK fires when the frame
105
+ * budget REACHES ZERO (the N frames have actually advanced), NOT when the STEP
106
+ * block is merely read. That is what makes stepping deterministic, the host acts
107
+ * on a SETTLED, known frame, never mid-step. INPUT/SHOT/PAUSE complete the frame
108
+ * they are read (instantaneous), so they ACK at read time.
109
+ */
110
+ export declare const ACK: {
111
+ /** A STEP's frame budget has fully drained (the N frames advanced). */
112
+ readonly stepDone: "__PP_ACK_STEP_DONE__";
113
+ /** The held-buttons byte was set (INPUT). */
114
+ readonly input: "__PP_ACK_INPUT__";
115
+ /** A screenshot was queued for the current frame (SHOT). */
116
+ readonly shot: "__PP_ACK_SHOT__";
117
+ /** The frame budget was forced to 0 (PAUSE). */
118
+ readonly pause: "__PP_ACK_PAUSE__";
119
+ };
120
+ /** One command in a driven-run script (the pre-codec, human-facing form). */
121
+ export type DriveCommand = {
122
+ readonly op: 'step';
123
+ readonly frames: number;
124
+ } | {
125
+ readonly op: 'input';
126
+ readonly held: number;
127
+ } | {
128
+ readonly op: 'shot';
129
+ } | {
130
+ readonly op: 'pause';
131
+ } | {
132
+ readonly op: 'quit';
133
+ };
134
+ /**
135
+ * A single scripted button press/hold: press button `bit` on every frame in
136
+ * `[from, to]` inclusive (a single-frame press is `from === to`). The parsed
137
+ * form of the `--input "frame:bit,..."` spec, before it is expanded into the
138
+ * per-frame held-buttons bytes and then INPUT/STEP command blocks.
139
+ */
140
+ export interface ScriptedPress {
141
+ readonly from: number;
142
+ readonly to: number;
143
+ readonly bit: number;
144
+ }
145
+ /**
146
+ * Encodes one {@link DriveCommand} into a FIXED-SIZE ({@link BLOCK_SIZE}) block:
147
+ * `[opcode, arg, 0, 0]`. `arg` carries the STEP frame count or the INPUT held
148
+ * byte; SHOT/PAUSE/QUIT ignore it (0). Fixed size is the point (ADR-0011): the
149
+ * cart always consumes exactly {@link BLOCK_SIZE} bytes per read.
150
+ */
151
+ export declare function encodeCommand(cmd: DriveCommand): Uint8Array;
152
+ /** Encodes a whole command script into a single concatenated block stream. */
153
+ export declare function encodeScript(script: readonly DriveCommand[]): Uint8Array;
154
+ /**
155
+ * A stateful block DECODER mirroring the cart-side shim: feed it raw byte chunks
156
+ * (a live `-x` stdin delivers arbitrary-sized reads), it buffers partial blocks
157
+ * across chunks and yields exactly one {@link DriveCommand} per completed
158
+ * {@link BLOCK_SIZE}-byte block. This is the TS twin of the Lua decoder, so the
159
+ * codec (including "a block split across two reads still decodes once") is
160
+ * asserted in CI without the binary.
161
+ */
162
+ export declare class BlockDecoder {
163
+ private buffer;
164
+ /** Feeds a chunk of bytes; returns the commands newly completed by it. */
165
+ push(chunk: Uint8Array | readonly number[]): DriveCommand[];
166
+ }
167
+ /** Decodes one {@link BLOCK_SIZE}-byte block into a {@link DriveCommand}. */
168
+ export declare function decodeBlock(block: readonly number[]): DriveCommand;
169
+ /**
170
+ * Reconstructs `btnp` EDGES from a per-frame HELD-buttons sequence (ADR-0011,
171
+ * the load-bearing verified fact). Given the held byte for each frame, returns
172
+ * the `btnp` byte for each frame: bit `i` is set only on the frame the button
173
+ * TRANSITIONS 0 -> 1 (a rising edge), so a single scripted press triggers exactly
174
+ * one `btnp` (menus/start/single-actions behave), a hold triggers one edge then
175
+ * stays held (no repeat), and a release+press triggers two edges. This is a pure
176
+ * mirror of the cart-side shim (`btnp = held & ~prev`), tested independently.
177
+ */
178
+ export declare function btnpEdges(held: readonly number[]): number[];
179
+ /**
180
+ * Expands scripted presses into a per-frame HELD-buttons byte array of length
181
+ * `frames`: frame `f`'s byte has bit `p.bit` set for every press `p` whose
182
+ * `[from,to]` window covers `f`. This is the level signal the harness pipes; the
183
+ * cart reconstructs `btnp` edges from it (see {@link btnpEdges}).
184
+ */
185
+ export declare function pressesToHeld(presses: readonly ScriptedPress[], frames: number): number[];
186
+ /**
187
+ * Resolves a single BUTTON token to its bit. Accepts a NAME (`left right up down
188
+ * o x`, plus aliases `l r u d`, case-insensitive) OR a bare bit digit `0..5`, so
189
+ * BOTH `playtest` input surfaces share one button vocabulary (the `--input`
190
+ * timeline and the session `input` verb). An unknown token is a structured
191
+ * {@link DriveError}.
192
+ */
193
+ export declare function parseButtonToken(token: string): number;
194
+ /**
195
+ * Parses an `--input` spec into scripted presses. Each comma-separated token is
196
+ * either a TIMELINE entry `frame:button` / `from-to:button` (a press at that
197
+ * frame, or a hold across the inclusive window) OR a BARE button (a name/key with
198
+ * no `frame:`), which is a press at frame 0, so a name-only spec like `"z"` or
199
+ * `"o right"` (the natural "just press this" form, matching the session `input`
200
+ * verb) Just Works. `button` is a NAME (`o x left right up down`, aliases
201
+ * `l r u d`, PICO-8 keys `z`/`x`) or a bit digit `0..5`, so both `playtest` input
202
+ * surfaces share ONE vocabulary. E.g. `"3:o, 18-22:o, 20:right"` = press O at
203
+ * frame 3, hold O frames 18..22, press right at frame 20; `"z"` = press O at
204
+ * frame 0. Whitespace around tokens is ignored (a bare list may be space- or
205
+ * comma-separated). An unknown button is a structured {@link DriveError}.
206
+ */
207
+ export declare function parseInputScript(spec: string): ScriptedPress[];
208
+ /**
209
+ * The GENERIC input default (ADR-0011, US #9): applied when `--input` is omitted.
210
+ * Tuned to REACH play and stay there briefly for common one-button / runner /
211
+ * flappy shapes: press O once to START, a few well-spaced single-frame O presses
212
+ * (clean `btnp` edges) so a one-button game acts without dying instantly, a short
213
+ * O HOLD for hold-to-thrust games, and a right-nudge for directional runners. It
214
+ * is a best-effort default, NOT a universal driver; an unusual control scheme
215
+ * should pass an explicit per-cart `--input` (documented in the command help).
216
+ */
217
+ export declare const GENERIC_INPUT: readonly ScriptedPress[];
218
+ /**
219
+ * The default number of frames the generic/one-shot driver advances the cart, and
220
+ * the shot points within it. Chosen (per the spike) to catch LIVE play soon after
221
+ * the start press (before any death/retry churn) then a couple more spread out.
222
+ */
223
+ export declare const DEFAULT_DRIVE_FRAMES = 40;
224
+ export declare const DEFAULT_SHOT_FRAMES: readonly number[];
225
+ /**
226
+ * The driven-run SPEC the transform expands: either an explicit per-frame press
227
+ * script (`presses`) or the {@link GENERIC_INPUT} default, plus the frames to
228
+ * advance, the frames to screenshot at, and the opt-in `srand` seed.
229
+ */
230
+ export interface DriveSpec {
231
+ /** The scripted presses; defaults to {@link GENERIC_INPUT} when omitted. */
232
+ readonly presses?: readonly ScriptedPress[];
233
+ /** Total frames to advance the driven cart (defaults to {@link DEFAULT_DRIVE_FRAMES}). */
234
+ readonly frames?: number;
235
+ /** The frames to screenshot at (defaults to {@link DEFAULT_SHOT_FRAMES}). */
236
+ readonly shotFrames?: readonly number[];
237
+ /**
238
+ * The opt-in determinism seed (US #4, ADR-0011): iff given, the transform
239
+ * injects `srand(seed)` at cart start so an otherwise-random cart replays
240
+ * identically. NEVER injected silently (it changes game behaviour).
241
+ */
242
+ readonly seed?: number;
243
+ /** The done-sentinel the cart prints on QUIT (defaults to {@link DONE_SENTINEL}). */
244
+ readonly sentinel?: string;
245
+ /** The base filename the SHOT command names each screenshot (defaults to `play`). */
246
+ readonly shotBasename?: string;
247
+ /**
248
+ * Resolver for `#include <file>` directives in the cart's Lua (picopilot's
249
+ * standard scaffold is `__lua__` = a single `#include main.lua`). The driven
250
+ * cart runs from a THROWAWAY temp dir where the included files do not exist, so
251
+ * the transform must INLINE them to be self-contained. Given an include path
252
+ * (relative to the source cart), return the file's text, or `undefined` if it
253
+ * cannot be read (the directive is then left verbatim). Injected (not `fs`
254
+ * imported) so the transform stays pure + unit-testable. Omitted => includes
255
+ * are left as-is (fine for an already-inlined cart).
256
+ */
257
+ readonly readInclude?: (includePath: string) => string | undefined;
258
+ }
259
+ /**
260
+ * Inlines `#include <file>` directives in a cart's Lua so the driven cart is
261
+ * self-contained (it runs from a throwaway temp dir where the included files are
262
+ * absent, and `definedCallbacks` must SEE the real `_update`/`_draw` that live in
263
+ * the included file). Matches PICO-8's own `#include`: a line whose first
264
+ * non-space token is `#include`, the rest of the line is the path. Resolves
265
+ * recursively (an included file may itself `#include`), guarding against cycles.
266
+ * A path the resolver cannot read is left verbatim (best-effort, like the rest of
267
+ * the transform). With no resolver, the Lua is returned unchanged.
268
+ */
269
+ export declare function resolveIncludes(lua: string, readInclude: ((includePath: string) => string | undefined) | undefined, seen?: ReadonlySet<string>): string;
270
+ /** The default base filename SHOT names each screenshot (`play0.png`, ...). */
271
+ export declare const SHOT_BASENAME: "play";
272
+ /**
273
+ * Builds the ONE-SHOT command SCRIPT from a spec: the full sequence of INPUT /
274
+ * STEP / SHOT / QUIT blocks that drives the cart frame-by-frame. Each frame is an
275
+ * INPUT (the held byte for that frame) followed by a single-frame STEP; SHOT
276
+ * blocks are interleaved at the shot frames; a trailing QUIT ends the run. The
277
+ * cart drains one block per frame, so the whole script is sent up front and the
278
+ * cart replays it deterministically (ADR-0011: the one-shot sends everything and
279
+ * exits; the block+ACK transport lets it queue without loss).
280
+ */
281
+ export declare function buildDriveScript(spec: DriveSpec): DriveCommand[];
282
+ /** The result of building a drive harness: the throwaway cart + its command blocks. */
283
+ export interface DriveHarness {
284
+ /** The throwaway `.p8` cart text (the entry's cart is UNTOUCHED). */
285
+ readonly cartText: string;
286
+ /** The command script (pre-codec) the harness drives the cart with. */
287
+ readonly script: readonly DriveCommand[];
288
+ /** The encoded FIXED-SIZE command blocks to pipe to the cart's stdin. */
289
+ readonly blocks: Uint8Array;
290
+ /** The base filename SHOT names each screenshot (for the result envelope). */
291
+ readonly shotBasename: string;
292
+ /** The number of SHOT commands in the script (= expected screenshots). */
293
+ readonly shotCount: number;
294
+ }
295
+ /**
296
+ * Builds the throwaway DRIVE harness (ADR-0011). Takes the user's cart text + a
297
+ * {@link DriveSpec} and produces a new cart with the SAME `__gfx__`/`__sfx__`/...
298
+ * and a `__lua__` that is: the drive shim, then the cart's own Lua (optionally
299
+ * prefixed with `srand(seed)` iff `--seed` given), then the wrapper tab. The
300
+ * entry's own cart bytes are NEVER mutated (we transform a parsed COPY).
301
+ *
302
+ * Returns the driven cart text plus the encoded command blocks the command pipes
303
+ * to stdin. Kept pure so every path is unit-tested without the paid binary.
304
+ *
305
+ * @throws {DriveError} an unparseable cart.
306
+ */
307
+ export declare function buildDriveHarness(cartText: string, spec?: DriveSpec): DriveHarness;
308
+ //# sourceMappingURL=drive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drive.d.ts","sourceRoot":"","sources":["../../../src/engine/pico8/drive.ts"],"names":[],"mappings":"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"}