c64-kb 0.17.0

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 (970) hide show
  1. package/CHANGELOG.md +2082 -0
  2. package/LICENSE +29 -0
  3. package/README.md +559 -0
  4. package/VERSION +25 -0
  5. package/dist/cli/setup.js +66 -0
  6. package/dist/cli/setup.js.map +1 -0
  7. package/dist/cli-memorize-audit.js +97 -0
  8. package/dist/cli-memorize-audit.js.map +1 -0
  9. package/dist/cli.js +335 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/config.js +83 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/context.js +53 -0
  14. package/dist/context.js.map +1 -0
  15. package/dist/domain/budget-notes.js +104 -0
  16. package/dist/domain/budget-notes.js.map +1 -0
  17. package/dist/domain/budget.js +417 -0
  18. package/dist/domain/budget.js.map +1 -0
  19. package/dist/domain/game-design.js +77 -0
  20. package/dist/domain/game-design.js.map +1 -0
  21. package/dist/domain/timing.js +39 -0
  22. package/dist/domain/timing.js.map +1 -0
  23. package/dist/graph/apply.js +132 -0
  24. package/dist/graph/apply.js.map +1 -0
  25. package/dist/graph/claims.js +231 -0
  26. package/dist/graph/claims.js.map +1 -0
  27. package/dist/graph/extract/archetype.js +118 -0
  28. package/dist/graph/extract/archetype.js.map +1 -0
  29. package/dist/graph/extract/common.js +69 -0
  30. package/dist/graph/extract/common.js.map +1 -0
  31. package/dist/graph/extract/failure.js +37 -0
  32. package/dist/graph/extract/failure.js.map +1 -0
  33. package/dist/graph/extract/game-design.js +189 -0
  34. package/dist/graph/extract/game-design.js.map +1 -0
  35. package/dist/graph/extract/hardware.js +124 -0
  36. package/dist/graph/extract/hardware.js.map +1 -0
  37. package/dist/graph/extract/pitfall.js +62 -0
  38. package/dist/graph/extract/pitfall.js.map +1 -0
  39. package/dist/graph/extract/raster-band.js +56 -0
  40. package/dist/graph/extract/raster-band.js.map +1 -0
  41. package/dist/graph/extract/recipe.js +71 -0
  42. package/dist/graph/extract/recipe.js.map +1 -0
  43. package/dist/graph/extract/technique-entities.js +180 -0
  44. package/dist/graph/extract/technique-entities.js.map +1 -0
  45. package/dist/graph/extract/technique.js +179 -0
  46. package/dist/graph/extract/technique.js.map +1 -0
  47. package/dist/graph/extract/toolchain.js +67 -0
  48. package/dist/graph/extract/toolchain.js.map +1 -0
  49. package/dist/graph/extract/types.js +3 -0
  50. package/dist/graph/extract/types.js.map +1 -0
  51. package/dist/graph/extract/vocabulary.js +65 -0
  52. package/dist/graph/extract/vocabulary.js.map +1 -0
  53. package/dist/graph/extract.js +43 -0
  54. package/dist/graph/extract.js.map +1 -0
  55. package/dist/graph/kernal-clobbers.js +64 -0
  56. package/dist/graph/kernal-clobbers.js.map +1 -0
  57. package/dist/graph/machine-variants.js +208 -0
  58. package/dist/graph/machine-variants.js.map +1 -0
  59. package/dist/ingest/files.js +99 -0
  60. package/dist/ingest/files.js.map +1 -0
  61. package/dist/ingest/passes.js +104 -0
  62. package/dist/ingest/passes.js.map +1 -0
  63. package/dist/ingest/points.js +50 -0
  64. package/dist/ingest/points.js.map +1 -0
  65. package/dist/ingest/report.js +140 -0
  66. package/dist/ingest/report.js.map +1 -0
  67. package/dist/ingest/tally.js +81 -0
  68. package/dist/ingest/tally.js.map +1 -0
  69. package/dist/ingest/verified-on.js +43 -0
  70. package/dist/ingest/verified-on.js.map +1 -0
  71. package/dist/ingest.js +135 -0
  72. package/dist/ingest.js.map +1 -0
  73. package/dist/schemas/cost-basis.js +5 -0
  74. package/dist/schemas/cost-basis.js.map +1 -0
  75. package/dist/schemas/machine-variant.js +25 -0
  76. package/dist/schemas/machine-variant.js.map +1 -0
  77. package/dist/schemas/plan-budget.js +121 -0
  78. package/dist/schemas/plan-budget.js.map +1 -0
  79. package/dist/schemas/tool-outputs.js +526 -0
  80. package/dist/schemas/tool-outputs.js.map +1 -0
  81. package/dist/server/define-tool.js +52 -0
  82. package/dist/server/define-tool.js.map +1 -0
  83. package/dist/server/defined-only.js +9 -0
  84. package/dist/server/defined-only.js.map +1 -0
  85. package/dist/server/tools-briefings.js +70 -0
  86. package/dist/server/tools-briefings.js.map +1 -0
  87. package/dist/server/tools-lookup.js +180 -0
  88. package/dist/server/tools-lookup.js.map +1 -0
  89. package/dist/server/tools-maintenance.js +116 -0
  90. package/dist/server/tools-maintenance.js.map +1 -0
  91. package/dist/server/tools-pitfalls.js +89 -0
  92. package/dist/server/tools-pitfalls.js.map +1 -0
  93. package/dist/server/tools-recipes.js +296 -0
  94. package/dist/server/tools-recipes.js.map +1 -0
  95. package/dist/server/tools-runtime.js +48 -0
  96. package/dist/server/tools-runtime.js.map +1 -0
  97. package/dist/server/tools.js +43 -0
  98. package/dist/server/tools.js.map +1 -0
  99. package/dist/server.js +95 -0
  100. package/dist/server.js.map +1 -0
  101. package/dist/services/analytics/schema.js +78 -0
  102. package/dist/services/analytics/schema.js.map +1 -0
  103. package/dist/services/analytics/statements.js +99 -0
  104. package/dist/services/analytics/statements.js.map +1 -0
  105. package/dist/services/analytics.js +195 -0
  106. package/dist/services/analytics.js.map +1 -0
  107. package/dist/services/bm25.js +158 -0
  108. package/dist/services/bm25.js.map +1 -0
  109. package/dist/services/chunker.js +81 -0
  110. package/dist/services/chunker.js.map +1 -0
  111. package/dist/services/embeddings.js +58 -0
  112. package/dist/services/embeddings.js.map +1 -0
  113. package/dist/services/falkor/base.js +85 -0
  114. package/dist/services/falkor/base.js.map +1 -0
  115. package/dist/services/falkor/links.js +361 -0
  116. package/dist/services/falkor/links.js.map +1 -0
  117. package/dist/services/falkor/nodes.js +183 -0
  118. package/dist/services/falkor/nodes.js.map +1 -0
  119. package/dist/services/falkor/params.js +45 -0
  120. package/dist/services/falkor/params.js.map +1 -0
  121. package/dist/services/falkor/schema.js +77 -0
  122. package/dist/services/falkor/schema.js.map +1 -0
  123. package/dist/services/falkor.js +100 -0
  124. package/dist/services/falkor.js.map +1 -0
  125. package/dist/services/qdrant.js +238 -0
  126. package/dist/services/qdrant.js.map +1 -0
  127. package/dist/services/versions.js +73 -0
  128. package/dist/services/versions.js.map +1 -0
  129. package/dist/services/vice-bin.js +51 -0
  130. package/dist/services/vice-bin.js.map +1 -0
  131. package/dist/tools/briefings/archetype.js +174 -0
  132. package/dist/tools/briefings/archetype.js.map +1 -0
  133. package/dist/tools/briefings/budget.js +177 -0
  134. package/dist/tools/briefings/budget.js.map +1 -0
  135. package/dist/tools/briefings/build-order.js +135 -0
  136. package/dist/tools/briefings/build-order.js.map +1 -0
  137. package/dist/tools/briefings/build.js +185 -0
  138. package/dist/tools/briefings/build.js.map +1 -0
  139. package/dist/tools/briefings/discovery.js +290 -0
  140. package/dist/tools/briefings/discovery.js.map +1 -0
  141. package/dist/tools/briefings/plan-pitfalls.js +69 -0
  142. package/dist/tools/briefings/plan-pitfalls.js.map +1 -0
  143. package/dist/tools/briefings/render.js +151 -0
  144. package/dist/tools/briefings/render.js.map +1 -0
  145. package/dist/tools/briefings/rows.js +41 -0
  146. package/dist/tools/briefings/rows.js.map +1 -0
  147. package/dist/tools/briefings/toolchain.js +63 -0
  148. package/dist/tools/briefings/toolchain.js.map +1 -0
  149. package/dist/tools/briefings/why-proposed.js +69 -0
  150. package/dist/tools/briefings/why-proposed.js.map +1 -0
  151. package/dist/tools/briefings.js +44 -0
  152. package/dist/tools/briefings.js.map +1 -0
  153. package/dist/tools/feedback.js +97 -0
  154. package/dist/tools/feedback.js.map +1 -0
  155. package/dist/tools/hydrate.js +75 -0
  156. package/dist/tools/hydrate.js.map +1 -0
  157. package/dist/tools/intelligence.js +67 -0
  158. package/dist/tools/intelligence.js.map +1 -0
  159. package/dist/tools/lint/asm-decimal.js +86 -0
  160. package/dist/tools/lint/asm-decimal.js.map +1 -0
  161. package/dist/tools/lint/asm-rules.js +221 -0
  162. package/dist/tools/lint/asm-rules.js.map +1 -0
  163. package/dist/tools/lint/asm-shared.js +3 -0
  164. package/dist/tools/lint/asm-shared.js.map +1 -0
  165. package/dist/tools/lint/c-rules.js +215 -0
  166. package/dist/tools/lint/c-rules.js.map +1 -0
  167. package/dist/tools/lint/text.js +122 -0
  168. package/dist/tools/lint/text.js.map +1 -0
  169. package/dist/tools/lint/types.js +40 -0
  170. package/dist/tools/lint/types.js.map +1 -0
  171. package/dist/tools/lint.js +76 -0
  172. package/dist/tools/lint.js.map +1 -0
  173. package/dist/tools/memorization-mcp.js +111 -0
  174. package/dist/tools/memorization-mcp.js.map +1 -0
  175. package/dist/tools/pitfalls/bm25.js +53 -0
  176. package/dist/tools/pitfalls/bm25.js.map +1 -0
  177. package/dist/tools/pitfalls/format.js +58 -0
  178. package/dist/tools/pitfalls/format.js.map +1 -0
  179. package/dist/tools/pitfalls/graph.js +124 -0
  180. package/dist/tools/pitfalls/graph.js.map +1 -0
  181. package/dist/tools/pitfalls.js +134 -0
  182. package/dist/tools/pitfalls.js.map +1 -0
  183. package/dist/tools/prompts.js +81 -0
  184. package/dist/tools/prompts.js.map +1 -0
  185. package/dist/tools/query/compatibility/closure.js +62 -0
  186. package/dist/tools/query/compatibility/closure.js.map +1 -0
  187. package/dist/tools/query/compatibility/facts.js +28 -0
  188. package/dist/tools/query/compatibility/facts.js.map +1 -0
  189. package/dist/tools/query/compatibility/fetch.js +164 -0
  190. package/dist/tools/query/compatibility/fetch.js.map +1 -0
  191. package/dist/tools/query/compatibility/hard-rules.js +159 -0
  192. package/dist/tools/query/compatibility/hard-rules.js.map +1 -0
  193. package/dist/tools/query/compatibility/index.js +27 -0
  194. package/dist/tools/query/compatibility/index.js.map +1 -0
  195. package/dist/tools/query/compatibility/kernal-zp-rule.js +90 -0
  196. package/dist/tools/query/compatibility/kernal-zp-rule.js.map +1 -0
  197. package/dist/tools/query/compatibility/render.js +106 -0
  198. package/dist/tools/query/compatibility/render.js.map +1 -0
  199. package/dist/tools/query/compatibility/rules.js +337 -0
  200. package/dist/tools/query/compatibility/rules.js.map +1 -0
  201. package/dist/tools/query/compatibility/unit-rules.js +253 -0
  202. package/dist/tools/query/compatibility/unit-rules.js.map +1 -0
  203. package/dist/tools/query/game-design.js +83 -0
  204. package/dist/tools/query/game-design.js.map +1 -0
  205. package/dist/tools/query/lookups.js +220 -0
  206. package/dist/tools/query/lookups.js.map +1 -0
  207. package/dist/tools/query/plan-budget.js +266 -0
  208. package/dist/tools/query/plan-budget.js.map +1 -0
  209. package/dist/tools/query/recipes.js +214 -0
  210. package/dist/tools/query/recipes.js.map +1 -0
  211. package/dist/tools/query/retrieval.js +128 -0
  212. package/dist/tools/query/retrieval.js.map +1 -0
  213. package/dist/tools/query/shared.js +121 -0
  214. package/dist/tools/query/shared.js.map +1 -0
  215. package/dist/tools/query/techniques.js +331 -0
  216. package/dist/tools/query/techniques.js.map +1 -0
  217. package/dist/tools/query/timing.js +106 -0
  218. package/dist/tools/query/timing.js.map +1 -0
  219. package/dist/tools/query/types.js +11 -0
  220. package/dist/tools/query/types.js.map +1 -0
  221. package/dist/tools/query.js +21 -0
  222. package/dist/tools/query.js.map +1 -0
  223. package/dist/tools/resources.js +186 -0
  224. package/dist/tools/resources.js.map +1 -0
  225. package/dist/tools/run-game.js +249 -0
  226. package/dist/tools/run-game.js.map +1 -0
  227. package/dist/tools/selfimprovement.js +367 -0
  228. package/dist/tools/selfimprovement.js.map +1 -0
  229. package/docker-compose.yml +35 -0
  230. package/docs/ARCHITECTURE.md +164 -0
  231. package/docs/CONVENTIONS-archetypes.md +158 -0
  232. package/docs/CONVENTIONS-failures.md +46 -0
  233. package/docs/CONVENTIONS-game-designs.md +75 -0
  234. package/docs/CONVENTIONS-hardware-reference.md +184 -0
  235. package/docs/CONVENTIONS-pitfalls.md +91 -0
  236. package/docs/CONVENTIONS-recipes.md +106 -0
  237. package/docs/CONVENTIONS-techniques.md +309 -0
  238. package/docs/CONVENTIONS-toolchain-reference.md +89 -0
  239. package/docs/ONTOLOGY.md +655 -0
  240. package/docs/art/art-production-reference.md +157 -0
  241. package/docs/art/asset-pipelines.md +586 -0
  242. package/docs/c64-failure-patterns.md +693 -0
  243. package/docs/demo-design/demo-composition.md +349 -0
  244. package/docs/demo-design/demo-design-philosophy.md +320 -0
  245. package/docs/demo-design/intro-cracktro-patterns.md +435 -0
  246. package/docs/figures/ctm-spd-embed-probe.png +0 -0
  247. package/docs/figures/dot-flag-mid-5000000.png +0 -0
  248. package/docs/figures/dot-flag-no-erase-12000000.png +0 -0
  249. package/docs/figures/edges.json +1 -0
  250. package/docs/figures/fig6-architecture.png +0 -0
  251. package/docs/figures/fig7-ontology.png +0 -0
  252. package/docs/figures/screen-dissolve-mid-frame25.png +0 -0
  253. package/docs/figures/screen-dissolve-wipe-frame25.png +0 -0
  254. package/docs/figures/spindle-two-parts-part1.png +0 -0
  255. package/docs/figures/spindle-two-parts-part2.png +0 -0
  256. package/docs/figures/starters/action-puzzle.png +0 -0
  257. package/docs/figures/starters/adventure.png +0 -0
  258. package/docs/figures/starters/beat-em-up.png +0 -0
  259. package/docs/figures/starters/demo.png +0 -0
  260. package/docs/figures/starters/platformer.png +0 -0
  261. package/docs/figures/starters/shmup-vertical.png +0 -0
  262. package/docs/figures/tape-kernal-master-ntsc.png +0 -0
  263. package/docs/figures/tape-kernal-master-pal.png +0 -0
  264. package/docs/figures/tape-turbo-loader-ntsc.png +0 -0
  265. package/docs/figures/tape-turbo-loader-pal.png +0 -0
  266. package/docs/figures/tape-turbo-master-ntsc.png +0 -0
  267. package/docs/figures/tape-turbo-master-pal.png +0 -0
  268. package/docs/figures/tech-tech-pad2-12000000.png +0 -0
  269. package/docs/figures/tech-tech-pad4-12000000.png +0 -0
  270. package/docs/figures/tech-tech-seven-12000000.png +0 -0
  271. package/docs/figures/twister-mid-5000000.png +0 -0
  272. package/docs/figures/twister-straight-12000000.png +0 -0
  273. package/docs/figures/vector-balls-nosort-ntsc.png +0 -0
  274. package/docs/figures/vector-balls-nosort.png +0 -0
  275. package/docs/formats/c64-file-formats.md +1212 -0
  276. package/docs/formats/iec-disk-reference.md +754 -0
  277. package/docs/game-design/c64-game-archetypes.md +273 -0
  278. package/docs/game-design/designs/falling-blocks.md +54 -0
  279. package/docs/game-design/designs/platformer-scaffold.md +48 -0
  280. package/docs/game-design/designs/simple-shmup.md +27 -0
  281. package/docs/game-design/enemy-behaviour-and-difficulty.md +349 -0
  282. package/docs/game-design/game-design-patterns.md +1189 -0
  283. package/docs/game-design/game-structure.md +354 -0
  284. package/docs/game-design/production-planning.md +349 -0
  285. package/docs/game-design/reference-game-sources.md +90 -0
  286. package/docs/hardware/6502-illegal-opcodes.md +1353 -0
  287. package/docs/hardware/6510-cpu-reference.md +2112 -0
  288. package/docs/hardware/c64-memory-map.md +3184 -0
  289. package/docs/hardware/c64-registers-reference.md +996 -0
  290. package/docs/hardware/cia-reference.md +1281 -0
  291. package/docs/hardware/kernal-routines-reference.md +1836 -0
  292. package/docs/hardware/pal-ntsc-reference.md +813 -0
  293. package/docs/hardware/sid-reference.md +1137 -0
  294. package/docs/hardware/vic-ii-reference.md +1714 -0
  295. package/docs/music/music-production-reference.md +403 -0
  296. package/docs/pitfalls/banking.md +1067 -0
  297. package/docs/pitfalls/cia.md +1147 -0
  298. package/docs/pitfalls/cpu.md +874 -0
  299. package/docs/pitfalls/input.md +282 -0
  300. package/docs/pitfalls/kernal-and-io.md +1615 -0
  301. package/docs/pitfalls/loader.md +1191 -0
  302. package/docs/pitfalls/logic.md +94 -0
  303. package/docs/pitfalls/maths.md +187 -0
  304. package/docs/pitfalls/raster-and-badline.md +721 -0
  305. package/docs/pitfalls/region-timing.md +660 -0
  306. package/docs/pitfalls/scroll.md +207 -0
  307. package/docs/pitfalls/sid.md +738 -0
  308. package/docs/pitfalls/sprite.md +979 -0
  309. package/docs/pitfalls/text-mode-render.md +1172 -0
  310. package/docs/recipes/cc65/cartridge-8k.md +398 -0
  311. package/docs/recipes/cc65/hello-world-conio.md +76 -0
  312. package/docs/recipes/cc65/memory-layout.md +312 -0
  313. package/docs/recipes/cc65/screenshots/README.md +14 -0
  314. package/docs/recipes/cc65/screenshots/cartridge-8k-ntsc.png +0 -0
  315. package/docs/recipes/cc65/screenshots/cartridge-8k.png +0 -0
  316. package/docs/recipes/cc65/screenshots/hello-world-conio.png +0 -0
  317. package/docs/recipes/cc65/screenshots/memory-layout-ntsc.png +0 -0
  318. package/docs/recipes/cc65/screenshots/memory-layout.png +0 -0
  319. package/docs/recipes/kickassembler/basic-float-calls.md +578 -0
  320. package/docs/recipes/kickassembler/basic-wedge.md +398 -0
  321. package/docs/recipes/kickassembler/big-font-scroller.md +620 -0
  322. package/docs/recipes/kickassembler/charset-copy-rom-to-ram.md +623 -0
  323. package/docs/recipes/kickassembler/cia-revision-detect.md +366 -0
  324. package/docs/recipes/kickassembler/colour-cycling.md +343 -0
  325. package/docs/recipes/kickassembler/colour-fade.md +303 -0
  326. package/docs/recipes/kickassembler/compare-16bit-signed.md +1007 -0
  327. package/docs/recipes/kickassembler/cracktro-template.md +526 -0
  328. package/docs/recipes/kickassembler/crt-banked.md +276 -0
  329. package/docs/recipes/kickassembler/dos-error-codes.md +466 -0
  330. package/docs/recipes/kickassembler/dot-flag.md +576 -0
  331. package/docs/recipes/kickassembler/drive-job-queue.md +667 -0
  332. package/docs/recipes/kickassembler/drive-via-probe.md +690 -0
  333. package/docs/recipes/kickassembler/dycp-scroller.md +568 -0
  334. package/docs/recipes/kickassembler/dypp-sprite-scroller.md +681 -0
  335. package/docs/recipes/kickassembler/dysp.md +670 -0
  336. package/docs/recipes/kickassembler/easyflash-save.md +800 -0
  337. package/docs/recipes/kickassembler/eight-way-scroll.md +1484 -0
  338. package/docs/recipes/kickassembler/file-io-roundtrip.md +546 -0
  339. package/docs/recipes/kickassembler/fire-effect.md +420 -0
  340. package/docs/recipes/kickassembler/fld.md +414 -0
  341. package/docs/recipes/kickassembler/fli-image.md +336 -0
  342. package/docs/recipes/kickassembler/four-player-read.md +672 -0
  343. package/docs/recipes/kickassembler/headless-verify.md +228 -0
  344. package/docs/recipes/kickassembler/hello-world.md +98 -0
  345. package/docs/recipes/kickassembler/high-score-insert.md +660 -0
  346. package/docs/recipes/kickassembler/hires-plot-line.md +723 -0
  347. package/docs/recipes/kickassembler/irq-chain.md +316 -0
  348. package/docs/recipes/kickassembler/irq-owns-port.md +668 -0
  349. package/docs/recipes/kickassembler/isometric-room.md +1145 -0
  350. package/docs/recipes/kickassembler/light-pen-read.md +926 -0
  351. package/docs/recipes/kickassembler/logic-rate-decoupling.md +670 -0
  352. package/docs/recipes/kickassembler/mci-interlace.md +543 -0
  353. package/docs/recipes/kickassembler/memory-layout.md +277 -0
  354. package/docs/recipes/kickassembler/mouse-1351-read.md +801 -0
  355. package/docs/recipes/kickassembler/nmi-timer-tick.md +562 -0
  356. package/docs/recipes/kickassembler/own-keyscan.md +881 -0
  357. package/docs/recipes/kickassembler/paddle-read.md +751 -0
  358. package/docs/recipes/kickassembler/palette-cells.md +219 -0
  359. package/docs/recipes/kickassembler/pseudo-3d-road.md +930 -0
  360. package/docs/recipes/kickassembler/pwm-digi.md +463 -0
  361. package/docs/recipes/kickassembler/raster-bars.md +264 -0
  362. package/docs/recipes/kickassembler/reu-dma.md +1019 -0
  363. package/docs/recipes/kickassembler/runtime-relocation.md +743 -0
  364. package/docs/recipes/kickassembler/screen-dissolve.md +545 -0
  365. package/docs/recipes/kickassembler/screen-wipe.md +408 -0
  366. package/docs/recipes/kickassembler/screenshots/README.md +72 -0
  367. package/docs/recipes/kickassembler/screenshots/basic-float-calls-ntsc.png +0 -0
  368. package/docs/recipes/kickassembler/screenshots/basic-float-calls.png +0 -0
  369. package/docs/recipes/kickassembler/screenshots/basic-wedge-ntsc.png +0 -0
  370. package/docs/recipes/kickassembler/screenshots/basic-wedge.png +0 -0
  371. package/docs/recipes/kickassembler/screenshots/big-font-scroller-ntsc.png +0 -0
  372. package/docs/recipes/kickassembler/screenshots/big-font-scroller.png +0 -0
  373. package/docs/recipes/kickassembler/screenshots/charset-copy-rom-to-ram-ntsc.png +0 -0
  374. package/docs/recipes/kickassembler/screenshots/charset-copy-rom-to-ram.png +0 -0
  375. package/docs/recipes/kickassembler/screenshots/cia-revision-detect-ntsc.png +0 -0
  376. package/docs/recipes/kickassembler/screenshots/cia-revision-detect.png +0 -0
  377. package/docs/recipes/kickassembler/screenshots/colour-cycling-ntsc.png +0 -0
  378. package/docs/recipes/kickassembler/screenshots/colour-cycling.png +0 -0
  379. package/docs/recipes/kickassembler/screenshots/colour-fade-ntsc.png +0 -0
  380. package/docs/recipes/kickassembler/screenshots/colour-fade.png +0 -0
  381. package/docs/recipes/kickassembler/screenshots/compare-16bit-signed-ntsc.png +0 -0
  382. package/docs/recipes/kickassembler/screenshots/compare-16bit-signed.png +0 -0
  383. package/docs/recipes/kickassembler/screenshots/cracktro-template.png +0 -0
  384. package/docs/recipes/kickassembler/screenshots/crt-banked-ntsc.png +0 -0
  385. package/docs/recipes/kickassembler/screenshots/crt-banked.png +0 -0
  386. package/docs/recipes/kickassembler/screenshots/dos-error-codes-ntsc.png +0 -0
  387. package/docs/recipes/kickassembler/screenshots/dos-error-codes.png +0 -0
  388. package/docs/recipes/kickassembler/screenshots/dot-flag-ntsc.png +0 -0
  389. package/docs/recipes/kickassembler/screenshots/dot-flag.png +0 -0
  390. package/docs/recipes/kickassembler/screenshots/drive-job-queue-ntsc.png +0 -0
  391. package/docs/recipes/kickassembler/screenshots/drive-job-queue.png +0 -0
  392. package/docs/recipes/kickassembler/screenshots/drive-via-probe-ntsc.png +0 -0
  393. package/docs/recipes/kickassembler/screenshots/drive-via-probe.png +0 -0
  394. package/docs/recipes/kickassembler/screenshots/dycp-scroller-ntsc.png +0 -0
  395. package/docs/recipes/kickassembler/screenshots/dycp-scroller.png +0 -0
  396. package/docs/recipes/kickassembler/screenshots/dypp-sprite-scroller-ntsc.png +0 -0
  397. package/docs/recipes/kickassembler/screenshots/dypp-sprite-scroller.png +0 -0
  398. package/docs/recipes/kickassembler/screenshots/dysp-ntsc.png +0 -0
  399. package/docs/recipes/kickassembler/screenshots/dysp.png +0 -0
  400. package/docs/recipes/kickassembler/screenshots/easyflash-save-ntsc-run2.png +0 -0
  401. package/docs/recipes/kickassembler/screenshots/easyflash-save-ntsc.png +0 -0
  402. package/docs/recipes/kickassembler/screenshots/easyflash-save-run2.png +0 -0
  403. package/docs/recipes/kickassembler/screenshots/easyflash-save.png +0 -0
  404. package/docs/recipes/kickassembler/screenshots/eight-way-scroll-ntsc.png +0 -0
  405. package/docs/recipes/kickassembler/screenshots/eight-way-scroll.png +0 -0
  406. package/docs/recipes/kickassembler/screenshots/file-io-roundtrip-ntsc.png +0 -0
  407. package/docs/recipes/kickassembler/screenshots/file-io-roundtrip.png +0 -0
  408. package/docs/recipes/kickassembler/screenshots/fire-effect-ntsc.png +0 -0
  409. package/docs/recipes/kickassembler/screenshots/fire-effect.png +0 -0
  410. package/docs/recipes/kickassembler/screenshots/fld-ntsc.png +0 -0
  411. package/docs/recipes/kickassembler/screenshots/fld.png +0 -0
  412. package/docs/recipes/kickassembler/screenshots/fli-image.png +0 -0
  413. package/docs/recipes/kickassembler/screenshots/four-player-read-ntsc.png +0 -0
  414. package/docs/recipes/kickassembler/screenshots/four-player-read.png +0 -0
  415. package/docs/recipes/kickassembler/screenshots/headless-verify-ntsc.png +0 -0
  416. package/docs/recipes/kickassembler/screenshots/headless-verify.png +0 -0
  417. package/docs/recipes/kickassembler/screenshots/hello-world.png +0 -0
  418. package/docs/recipes/kickassembler/screenshots/high-score-insert-ntsc.png +0 -0
  419. package/docs/recipes/kickassembler/screenshots/high-score-insert.png +0 -0
  420. package/docs/recipes/kickassembler/screenshots/hires-plot-line-ntsc.png +0 -0
  421. package/docs/recipes/kickassembler/screenshots/hires-plot-line.png +0 -0
  422. package/docs/recipes/kickassembler/screenshots/irq-chain-ntsc.png +0 -0
  423. package/docs/recipes/kickassembler/screenshots/irq-chain.png +0 -0
  424. package/docs/recipes/kickassembler/screenshots/irq-owns-port-ntsc.png +0 -0
  425. package/docs/recipes/kickassembler/screenshots/irq-owns-port.png +0 -0
  426. package/docs/recipes/kickassembler/screenshots/isometric-room-ntsc.png +0 -0
  427. package/docs/recipes/kickassembler/screenshots/isometric-room.png +0 -0
  428. package/docs/recipes/kickassembler/screenshots/light-pen-read-ntsc.png +0 -0
  429. package/docs/recipes/kickassembler/screenshots/light-pen-read.png +0 -0
  430. package/docs/recipes/kickassembler/screenshots/logic-rate-decoupling-ntsc.png +0 -0
  431. package/docs/recipes/kickassembler/screenshots/logic-rate-decoupling.png +0 -0
  432. package/docs/recipes/kickassembler/screenshots/mci-interlace-average.png +0 -0
  433. package/docs/recipes/kickassembler/screenshots/mci-interlace-frame-b.png +0 -0
  434. package/docs/recipes/kickassembler/screenshots/mci-interlace-ntsc.png +0 -0
  435. package/docs/recipes/kickassembler/screenshots/mci-interlace.png +0 -0
  436. package/docs/recipes/kickassembler/screenshots/memory-layout-ntsc.png +0 -0
  437. package/docs/recipes/kickassembler/screenshots/memory-layout.png +0 -0
  438. package/docs/recipes/kickassembler/screenshots/mouse-1351-read-ntsc.png +0 -0
  439. package/docs/recipes/kickassembler/screenshots/mouse-1351-read.png +0 -0
  440. package/docs/recipes/kickassembler/screenshots/nmi-timer-tick-ntsc.png +0 -0
  441. package/docs/recipes/kickassembler/screenshots/nmi-timer-tick.png +0 -0
  442. package/docs/recipes/kickassembler/screenshots/own-keyscan-ntsc.png +0 -0
  443. package/docs/recipes/kickassembler/screenshots/own-keyscan.png +0 -0
  444. package/docs/recipes/kickassembler/screenshots/paddle-read-ntsc.png +0 -0
  445. package/docs/recipes/kickassembler/screenshots/paddle-read.png +0 -0
  446. package/docs/recipes/kickassembler/screenshots/palette-cells-ntsc.png +0 -0
  447. package/docs/recipes/kickassembler/screenshots/palette-cells.png +0 -0
  448. package/docs/recipes/kickassembler/screenshots/pseudo-3d-road-ntsc.png +0 -0
  449. package/docs/recipes/kickassembler/screenshots/pseudo-3d-road.png +0 -0
  450. package/docs/recipes/kickassembler/screenshots/pwm-digi-ntsc.png +0 -0
  451. package/docs/recipes/kickassembler/screenshots/pwm-digi.png +0 -0
  452. package/docs/recipes/kickassembler/screenshots/raster-bars.png +0 -0
  453. package/docs/recipes/kickassembler/screenshots/reu-dma-ntsc.png +0 -0
  454. package/docs/recipes/kickassembler/screenshots/reu-dma.png +0 -0
  455. package/docs/recipes/kickassembler/screenshots/runtime-relocation-ntsc.png +0 -0
  456. package/docs/recipes/kickassembler/screenshots/runtime-relocation.png +0 -0
  457. package/docs/recipes/kickassembler/screenshots/screen-dissolve-ntsc.png +0 -0
  458. package/docs/recipes/kickassembler/screenshots/screen-dissolve.png +0 -0
  459. package/docs/recipes/kickassembler/screenshots/screen-wipe-ntsc.png +0 -0
  460. package/docs/recipes/kickassembler/screenshots/screen-wipe.png +0 -0
  461. package/docs/recipes/kickassembler/screenshots/scroll-panel-split-ntsc.png +0 -0
  462. package/docs/recipes/kickassembler/screenshots/scroll-panel-split.png +0 -0
  463. package/docs/recipes/kickassembler/screenshots/sfx-in-player-ntsc.png +0 -0
  464. package/docs/recipes/kickassembler/screenshots/sfx-in-player.png +0 -0
  465. package/docs/recipes/kickassembler/screenshots/shadebobs-ntsc.png +0 -0
  466. package/docs/recipes/kickassembler/screenshots/shadebobs.png +0 -0
  467. package/docs/recipes/kickassembler/screenshots/sideborder-open.png +0 -0
  468. package/docs/recipes/kickassembler/screenshots/sine-scroller.png +0 -0
  469. package/docs/recipes/kickassembler/screenshots/sine-table-runtime-ntsc.png +0 -0
  470. package/docs/recipes/kickassembler/screenshots/sine-table-runtime.png +0 -0
  471. package/docs/recipes/kickassembler/screenshots/software-sprite-preshifted-ntsc.png +0 -0
  472. package/docs/recipes/kickassembler/screenshots/software-sprite-preshifted.png +0 -0
  473. package/docs/recipes/kickassembler/screenshots/speedcode-generator-ntsc.png +0 -0
  474. package/docs/recipes/kickassembler/screenshots/speedcode-generator.png +0 -0
  475. package/docs/recipes/kickassembler/screenshots/sprite-border-scroller-ntsc.png +0 -0
  476. package/docs/recipes/kickassembler/screenshots/sprite-border-scroller.png +0 -0
  477. package/docs/recipes/kickassembler/screenshots/sprite-cache-flip-ntsc.png +0 -0
  478. package/docs/recipes/kickassembler/screenshots/sprite-cache-flip.png +0 -0
  479. package/docs/recipes/kickassembler/screenshots/sprite-multiplex-24.png +0 -0
  480. package/docs/recipes/kickassembler/screenshots/sprite-multiplex-game-ntsc.png +0 -0
  481. package/docs/recipes/kickassembler/screenshots/sprite-multiplex-game.png +0 -0
  482. package/docs/recipes/kickassembler/screenshots/sprite-priority-classes-ntsc.png +0 -0
  483. package/docs/recipes/kickassembler/screenshots/sprite-priority-classes.png +0 -0
  484. package/docs/recipes/kickassembler/screenshots/sprite-sine-chain-ntsc.png +0 -0
  485. package/docs/recipes/kickassembler/screenshots/sprite-sine-chain.png +0 -0
  486. package/docs/recipes/kickassembler/screenshots/sprite-stretcher-ntsc.png +0 -0
  487. package/docs/recipes/kickassembler/screenshots/sprite-stretcher.png +0 -0
  488. package/docs/recipes/kickassembler/screenshots/sprites-only-screen-ntsc.png +0 -0
  489. package/docs/recipes/kickassembler/screenshots/sprites-only-screen.png +0 -0
  490. package/docs/recipes/kickassembler/screenshots/sqrt-atan2-ntsc.png +0 -0
  491. package/docs/recipes/kickassembler/screenshots/sqrt-atan2.png +0 -0
  492. package/docs/recipes/kickassembler/screenshots/stable-raster-irq-control.png +0 -0
  493. package/docs/recipes/kickassembler/screenshots/stable-raster-irq.png +0 -0
  494. package/docs/recipes/kickassembler/screenshots/tech-tech-ntsc.png +0 -0
  495. package/docs/recipes/kickassembler/screenshots/tech-tech.png +0 -0
  496. package/docs/recipes/kickassembler/screenshots/tod-alarm-ntsc.png +0 -0
  497. package/docs/recipes/kickassembler/screenshots/tod-alarm.png +0 -0
  498. package/docs/recipes/kickassembler/screenshots/topbottom-border-open.png +0 -0
  499. package/docs/recipes/kickassembler/screenshots/twister-ntsc.png +0 -0
  500. package/docs/recipes/kickassembler/screenshots/twister.png +0 -0
  501. package/docs/recipes/kickassembler/screenshots/unit-test-driver-ntsc.png +0 -0
  502. package/docs/recipes/kickassembler/screenshots/unit-test-driver.png +0 -0
  503. package/docs/recipes/kickassembler/screenshots/vector-balls-ntsc.png +0 -0
  504. package/docs/recipes/kickassembler/screenshots/vector-balls.png +0 -0
  505. package/docs/recipes/kickassembler/screenshots/wireframe-ships-ntsc.png +0 -0
  506. package/docs/recipes/kickassembler/screenshots/wireframe-ships.png +0 -0
  507. package/docs/recipes/kickassembler/scroll-panel-split.md +381 -0
  508. package/docs/recipes/kickassembler/sfx-in-player.md +907 -0
  509. package/docs/recipes/kickassembler/shadebobs.md +522 -0
  510. package/docs/recipes/kickassembler/sideborder-open.md +338 -0
  511. package/docs/recipes/kickassembler/sine-scroller.md +321 -0
  512. package/docs/recipes/kickassembler/sine-table-runtime.md +815 -0
  513. package/docs/recipes/kickassembler/software-sprite-preshifted.md +725 -0
  514. package/docs/recipes/kickassembler/speedcode-generator.md +606 -0
  515. package/docs/recipes/kickassembler/sprite-border-scroller.md +696 -0
  516. package/docs/recipes/kickassembler/sprite-cache-flip.md +1071 -0
  517. package/docs/recipes/kickassembler/sprite-multiplex-24.md +326 -0
  518. package/docs/recipes/kickassembler/sprite-multiplex-game.md +986 -0
  519. package/docs/recipes/kickassembler/sprite-priority-classes.md +463 -0
  520. package/docs/recipes/kickassembler/sprite-sine-chain.md +338 -0
  521. package/docs/recipes/kickassembler/sprite-stretcher.md +501 -0
  522. package/docs/recipes/kickassembler/sprites-only-screen.md +550 -0
  523. package/docs/recipes/kickassembler/sqrt-atan2.md +1014 -0
  524. package/docs/recipes/kickassembler/stable-raster-irq.md +360 -0
  525. package/docs/recipes/kickassembler/tape-turbo-loader.md +875 -0
  526. package/docs/recipes/kickassembler/tech-tech.md +679 -0
  527. package/docs/recipes/kickassembler/tod-alarm.md +754 -0
  528. package/docs/recipes/kickassembler/topbottom-border-open.md +407 -0
  529. package/docs/recipes/kickassembler/twister.md +518 -0
  530. package/docs/recipes/kickassembler/unit-test-driver.md +314 -0
  531. package/docs/recipes/kickassembler/vector-balls.md +545 -0
  532. package/docs/recipes/kickassembler/wireframe-ships.md +2324 -0
  533. package/docs/recipes/oscar64/actor-activation-window.md +807 -0
  534. package/docs/recipes/oscar64/adventure-engine.md +1094 -0
  535. package/docs/recipes/oscar64/attract-replay.md +465 -0
  536. package/docs/recipes/oscar64/beat-em-up-lanes.md +563 -0
  537. package/docs/recipes/oscar64/bfs-distance-map.md +723 -0
  538. package/docs/recipes/oscar64/bitmap-koala-viewer.md +355 -0
  539. package/docs/recipes/oscar64/car-contact.md +950 -0
  540. package/docs/recipes/oscar64/cave-scan.md +609 -0
  541. package/docs/recipes/oscar64/char-bullets.md +477 -0
  542. package/docs/recipes/oscar64/charset-animation.md +383 -0
  543. package/docs/recipes/oscar64/charset-parallax.md +401 -0
  544. package/docs/recipes/oscar64/destructible-terrain.md +1099 -0
  545. package/docs/recipes/oscar64/difficulty-tables.md +654 -0
  546. package/docs/recipes/oscar64/dig-and-guards.md +975 -0
  547. package/docs/recipes/oscar64/directory-reader.md +535 -0
  548. package/docs/recipes/oscar64/divide-check.md +581 -0
  549. package/docs/recipes/oscar64/double-buffer-nomirror.md +205 -0
  550. package/docs/recipes/oscar64/double-buffer.md +255 -0
  551. package/docs/recipes/oscar64/falling-blocks.md +874 -0
  552. package/docs/recipes/oscar64/fixed-point-jump-velocity.md +289 -0
  553. package/docs/recipes/oscar64/fixed-point-jump.md +210 -0
  554. package/docs/recipes/oscar64/flip-screen-rooms.md +793 -0
  555. package/docs/recipes/oscar64/frame-sync-loop-overrun.md +214 -0
  556. package/docs/recipes/oscar64/frame-sync-loop.md +254 -0
  557. package/docs/recipes/oscar64/game-tree-search.md +743 -0
  558. package/docs/recipes/oscar64/ghost-targeting.md +867 -0
  559. package/docs/recipes/oscar64/headless-verify.md +345 -0
  560. package/docs/recipes/oscar64/hello-world.md +83 -0
  561. package/docs/recipes/oscar64/high-score-persist.md +772 -0
  562. package/docs/recipes/oscar64/iffl-kernal-skip.md +421 -0
  563. package/docs/recipes/oscar64/joystick-input.md +358 -0
  564. package/docs/recipes/oscar64/lane-pursuit.md +1028 -0
  565. package/docs/recipes/oscar64/level-rle-decoder.md +620 -0
  566. package/docs/recipes/oscar64/lfsr-random-seed2.md +338 -0
  567. package/docs/recipes/oscar64/lfsr-random.md +403 -0
  568. package/docs/recipes/oscar64/load-asset-runtime.md +492 -0
  569. package/docs/recipes/oscar64/memory-layout.md +232 -0
  570. package/docs/recipes/oscar64/mixed-fighters.md +781 -0
  571. package/docs/recipes/oscar64/multi-sprite-object.md +533 -0
  572. package/docs/recipes/oscar64/nav-area-pathfinding.md +881 -0
  573. package/docs/recipes/oscar64/object-pool.md +572 -0
  574. package/docs/recipes/oscar64/pal-ntsc-detect.md +257 -0
  575. package/docs/recipes/oscar64/password-state.md +532 -0
  576. package/docs/recipes/oscar64/per-frame-hitbox.md +651 -0
  577. package/docs/recipes/oscar64/petscii-screen-codes.md +428 -0
  578. package/docs/recipes/oscar64/platformer-scaffold.md +1467 -0
  579. package/docs/recipes/oscar64/print-number.md +367 -0
  580. package/docs/recipes/oscar64/raster-bars.md +239 -0
  581. package/docs/recipes/oscar64/raster-profile-bars.md +404 -0
  582. package/docs/recipes/oscar64/rel-side-sectors.md +391 -0
  583. package/docs/recipes/oscar64/relative-file-records.md +378 -0
  584. package/docs/recipes/oscar64/save-load-seq-file.md +365 -0
  585. package/docs/recipes/oscar64/screenshots/README.md +99 -0
  586. package/docs/recipes/oscar64/screenshots/actor-activation-window-ntsc.png +0 -0
  587. package/docs/recipes/oscar64/screenshots/actor-activation-window.png +0 -0
  588. package/docs/recipes/oscar64/screenshots/adventure-engine-ntsc.png +0 -0
  589. package/docs/recipes/oscar64/screenshots/adventure-engine.png +0 -0
  590. package/docs/recipes/oscar64/screenshots/attract-replay-ntsc.png +0 -0
  591. package/docs/recipes/oscar64/screenshots/attract-replay.png +0 -0
  592. package/docs/recipes/oscar64/screenshots/beat-em-up-lanes-ntsc.png +0 -0
  593. package/docs/recipes/oscar64/screenshots/beat-em-up-lanes.png +0 -0
  594. package/docs/recipes/oscar64/screenshots/bfs-distance-map-ntsc.png +0 -0
  595. package/docs/recipes/oscar64/screenshots/bfs-distance-map.png +0 -0
  596. package/docs/recipes/oscar64/screenshots/bitmap-koala-viewer.png +0 -0
  597. package/docs/recipes/oscar64/screenshots/car-contact-ntsc.png +0 -0
  598. package/docs/recipes/oscar64/screenshots/car-contact.png +0 -0
  599. package/docs/recipes/oscar64/screenshots/cave-scan-ntsc.png +0 -0
  600. package/docs/recipes/oscar64/screenshots/cave-scan.png +0 -0
  601. package/docs/recipes/oscar64/screenshots/char-bullets-ntsc.png +0 -0
  602. package/docs/recipes/oscar64/screenshots/char-bullets.png +0 -0
  603. package/docs/recipes/oscar64/screenshots/charset-animation-ntsc.png +0 -0
  604. package/docs/recipes/oscar64/screenshots/charset-animation.png +0 -0
  605. package/docs/recipes/oscar64/screenshots/charset-parallax-ntsc.png +0 -0
  606. package/docs/recipes/oscar64/screenshots/charset-parallax.png +0 -0
  607. package/docs/recipes/oscar64/screenshots/destructible-terrain-ntsc.png +0 -0
  608. package/docs/recipes/oscar64/screenshots/destructible-terrain.png +0 -0
  609. package/docs/recipes/oscar64/screenshots/difficulty-tables-ntsc.png +0 -0
  610. package/docs/recipes/oscar64/screenshots/difficulty-tables.png +0 -0
  611. package/docs/recipes/oscar64/screenshots/dig-and-guards-ntsc.png +0 -0
  612. package/docs/recipes/oscar64/screenshots/dig-and-guards.png +0 -0
  613. package/docs/recipes/oscar64/screenshots/directory-reader-ntsc.png +0 -0
  614. package/docs/recipes/oscar64/screenshots/directory-reader.png +0 -0
  615. package/docs/recipes/oscar64/screenshots/divide-check-ntsc.png +0 -0
  616. package/docs/recipes/oscar64/screenshots/divide-check.png +0 -0
  617. package/docs/recipes/oscar64/screenshots/double-buffer-nomirror.png +0 -0
  618. package/docs/recipes/oscar64/screenshots/double-buffer-ntsc.png +0 -0
  619. package/docs/recipes/oscar64/screenshots/double-buffer.png +0 -0
  620. package/docs/recipes/oscar64/screenshots/falling-blocks-ntsc.png +0 -0
  621. package/docs/recipes/oscar64/screenshots/falling-blocks.png +0 -0
  622. package/docs/recipes/oscar64/screenshots/fixed-point-jump-ntsc.png +0 -0
  623. package/docs/recipes/oscar64/screenshots/fixed-point-jump-velocity-ntsc.png +0 -0
  624. package/docs/recipes/oscar64/screenshots/fixed-point-jump-velocity.png +0 -0
  625. package/docs/recipes/oscar64/screenshots/fixed-point-jump.png +0 -0
  626. package/docs/recipes/oscar64/screenshots/flip-screen-rooms-ntsc.png +0 -0
  627. package/docs/recipes/oscar64/screenshots/flip-screen-rooms.png +0 -0
  628. package/docs/recipes/oscar64/screenshots/frame-sync-loop-ntsc.png +0 -0
  629. package/docs/recipes/oscar64/screenshots/frame-sync-loop-overrun.png +0 -0
  630. package/docs/recipes/oscar64/screenshots/frame-sync-loop.png +0 -0
  631. package/docs/recipes/oscar64/screenshots/game-tree-search-ntsc.png +0 -0
  632. package/docs/recipes/oscar64/screenshots/game-tree-search.png +0 -0
  633. package/docs/recipes/oscar64/screenshots/ghost-targeting-ntsc.png +0 -0
  634. package/docs/recipes/oscar64/screenshots/ghost-targeting.png +0 -0
  635. package/docs/recipes/oscar64/screenshots/headless-verify-ntsc.png +0 -0
  636. package/docs/recipes/oscar64/screenshots/headless-verify.png +0 -0
  637. package/docs/recipes/oscar64/screenshots/hello-world.png +0 -0
  638. package/docs/recipes/oscar64/screenshots/high-score-persist-ntsc.png +0 -0
  639. package/docs/recipes/oscar64/screenshots/high-score-persist.png +0 -0
  640. package/docs/recipes/oscar64/screenshots/iffl-kernal-skip-ntsc.png +0 -0
  641. package/docs/recipes/oscar64/screenshots/iffl-kernal-skip.png +0 -0
  642. package/docs/recipes/oscar64/screenshots/joystick-input-ntsc.png +0 -0
  643. package/docs/recipes/oscar64/screenshots/joystick-input.png +0 -0
  644. package/docs/recipes/oscar64/screenshots/lane-pursuit-ntsc.png +0 -0
  645. package/docs/recipes/oscar64/screenshots/lane-pursuit.png +0 -0
  646. package/docs/recipes/oscar64/screenshots/level-rle-decoder-ntsc.png +0 -0
  647. package/docs/recipes/oscar64/screenshots/level-rle-decoder.png +0 -0
  648. package/docs/recipes/oscar64/screenshots/lfsr-random-ntsc.png +0 -0
  649. package/docs/recipes/oscar64/screenshots/lfsr-random-seed2.png +0 -0
  650. package/docs/recipes/oscar64/screenshots/lfsr-random.png +0 -0
  651. package/docs/recipes/oscar64/screenshots/load-asset-runtime-ntsc.png +0 -0
  652. package/docs/recipes/oscar64/screenshots/load-asset-runtime.png +0 -0
  653. package/docs/recipes/oscar64/screenshots/memory-layout-ntsc.png +0 -0
  654. package/docs/recipes/oscar64/screenshots/memory-layout.png +0 -0
  655. package/docs/recipes/oscar64/screenshots/mixed-fighters-ntsc.png +0 -0
  656. package/docs/recipes/oscar64/screenshots/mixed-fighters.png +0 -0
  657. package/docs/recipes/oscar64/screenshots/multi-sprite-object-ntsc.png +0 -0
  658. package/docs/recipes/oscar64/screenshots/multi-sprite-object.png +0 -0
  659. package/docs/recipes/oscar64/screenshots/nav-area-pathfinding-ntsc.png +0 -0
  660. package/docs/recipes/oscar64/screenshots/nav-area-pathfinding.png +0 -0
  661. package/docs/recipes/oscar64/screenshots/object-pool-ntsc.png +0 -0
  662. package/docs/recipes/oscar64/screenshots/object-pool.png +0 -0
  663. package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-ntsc.png +0 -0
  664. package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-oldntsc.png +0 -0
  665. package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-pal.png +0 -0
  666. package/docs/recipes/oscar64/screenshots/password-state-ntsc.png +0 -0
  667. package/docs/recipes/oscar64/screenshots/password-state.png +0 -0
  668. package/docs/recipes/oscar64/screenshots/per-frame-hitbox-ntsc.png +0 -0
  669. package/docs/recipes/oscar64/screenshots/per-frame-hitbox.png +0 -0
  670. package/docs/recipes/oscar64/screenshots/petscii-screen-codes-ntsc.png +0 -0
  671. package/docs/recipes/oscar64/screenshots/petscii-screen-codes.png +0 -0
  672. package/docs/recipes/oscar64/screenshots/platformer-scaffold-d64-ntsc.png +0 -0
  673. package/docs/recipes/oscar64/screenshots/platformer-scaffold-d64.png +0 -0
  674. package/docs/recipes/oscar64/screenshots/platformer-scaffold-ntsc.png +0 -0
  675. package/docs/recipes/oscar64/screenshots/platformer-scaffold.png +0 -0
  676. package/docs/recipes/oscar64/screenshots/print-number-ntsc.png +0 -0
  677. package/docs/recipes/oscar64/screenshots/print-number.png +0 -0
  678. package/docs/recipes/oscar64/screenshots/raster-bars.png +0 -0
  679. package/docs/recipes/oscar64/screenshots/raster-profile-bars-ntsc.png +0 -0
  680. package/docs/recipes/oscar64/screenshots/raster-profile-bars.png +0 -0
  681. package/docs/recipes/oscar64/screenshots/rel-side-sectors-ntsc.png +0 -0
  682. package/docs/recipes/oscar64/screenshots/rel-side-sectors.png +0 -0
  683. package/docs/recipes/oscar64/screenshots/relative-file-records-ntsc.png +0 -0
  684. package/docs/recipes/oscar64/screenshots/relative-file-records.png +0 -0
  685. package/docs/recipes/oscar64/screenshots/save-load-seq-file-ntsc.png +0 -0
  686. package/docs/recipes/oscar64/screenshots/save-load-seq-file.png +0 -0
  687. package/docs/recipes/oscar64/screenshots/seeded-level-fill-ntsc.png +0 -0
  688. package/docs/recipes/oscar64/screenshots/seeded-level-fill.png +0 -0
  689. package/docs/recipes/oscar64/screenshots/sfx-engine-ntsc.png +0 -0
  690. package/docs/recipes/oscar64/screenshots/sfx-engine.png +0 -0
  691. package/docs/recipes/oscar64/screenshots/sid-music-player.png +0 -0
  692. package/docs/recipes/oscar64/screenshots/simple-shmup.png +0 -0
  693. package/docs/recipes/oscar64/screenshots/slope-collision-ntsc.png +0 -0
  694. package/docs/recipes/oscar64/screenshots/slope-collision.png +0 -0
  695. package/docs/recipes/oscar64/screenshots/soft-scroll-h.png +0 -0
  696. package/docs/recipes/oscar64/screenshots/sprite-animation-table-ntsc.png +0 -0
  697. package/docs/recipes/oscar64/screenshots/sprite-animation-table.png +0 -0
  698. package/docs/recipes/oscar64/screenshots/sprite-multiplex-8.png +0 -0
  699. package/docs/recipes/oscar64/screenshots/stable-raster-irq.png +0 -0
  700. package/docs/recipes/oscar64/screenshots/text-input-ntsc.png +0 -0
  701. package/docs/recipes/oscar64/screenshots/text-input.png +0 -0
  702. package/docs/recipes/oscar64/screenshots/text-overlay-playfield-gameover.png +0 -0
  703. package/docs/recipes/oscar64/screenshots/text-overlay-playfield-notrail-control.png +0 -0
  704. package/docs/recipes/oscar64/screenshots/text-overlay-playfield-ntsc.png +0 -0
  705. package/docs/recipes/oscar64/screenshots/text-overlay-playfield-ungated-control.png +0 -0
  706. package/docs/recipes/oscar64/screenshots/text-overlay-playfield.png +0 -0
  707. package/docs/recipes/oscar64/screenshots/text-window-menu-ntsc.png +0 -0
  708. package/docs/recipes/oscar64/screenshots/text-window-menu.png +0 -0
  709. package/docs/recipes/oscar64/screenshots/tile-grid-collision-ntsc.png +0 -0
  710. package/docs/recipes/oscar64/screenshots/tile-grid-collision.png +0 -0
  711. package/docs/recipes/oscar64/screenshots/tile-map-render-ntsc.png +0 -0
  712. package/docs/recipes/oscar64/screenshots/tile-map-render.png +0 -0
  713. package/docs/recipes/oscar64/screenshots/two-player-ntsc.png +0 -0
  714. package/docs/recipes/oscar64/screenshots/two-player.png +0 -0
  715. package/docs/recipes/oscar64/screenshots/two-word-parser-ntsc.png +0 -0
  716. package/docs/recipes/oscar64/screenshots/two-word-parser.png +0 -0
  717. package/docs/recipes/oscar64/screenshots/vehicle-control-ntsc.png +0 -0
  718. package/docs/recipes/oscar64/screenshots/vehicle-control.png +0 -0
  719. package/docs/recipes/oscar64/screenshots/wave-director-ntsc.png +0 -0
  720. package/docs/recipes/oscar64/screenshots/wave-director.png +0 -0
  721. package/docs/recipes/oscar64/seeded-level-fill.md +364 -0
  722. package/docs/recipes/oscar64/sfx-engine.md +514 -0
  723. package/docs/recipes/oscar64/sid-music-player.md +441 -0
  724. package/docs/recipes/oscar64/simple-shmup.md +1000 -0
  725. package/docs/recipes/oscar64/slope-collision.md +610 -0
  726. package/docs/recipes/oscar64/soft-scroll-h.md +261 -0
  727. package/docs/recipes/oscar64/sprite-animation-table.md +671 -0
  728. package/docs/recipes/oscar64/sprite-multiplex-8.md +314 -0
  729. package/docs/recipes/oscar64/stable-raster-irq.md +236 -0
  730. package/docs/recipes/oscar64/text-input.md +274 -0
  731. package/docs/recipes/oscar64/text-overlay-playfield.md +617 -0
  732. package/docs/recipes/oscar64/text-window-menu.md +551 -0
  733. package/docs/recipes/oscar64/tile-grid-collision.md +563 -0
  734. package/docs/recipes/oscar64/tile-map-render.md +395 -0
  735. package/docs/recipes/oscar64/two-player.md +528 -0
  736. package/docs/recipes/oscar64/two-word-parser.md +510 -0
  737. package/docs/recipes/oscar64/vehicle-control.md +771 -0
  738. package/docs/recipes/oscar64/wave-director.md +787 -0
  739. package/docs/recipes/runs.json +1606 -0
  740. package/docs/runtime/sim6502-reference.md +466 -0
  741. package/docs/runtime/vice-mcp-reference.md +351 -0
  742. package/docs/runtime/vice-reference.md +1393 -0
  743. package/docs/techniques/bitmap-modes.md +685 -0
  744. package/docs/techniques/cpu-cycle-tricks.md +1570 -0
  745. package/docs/techniques/effects-vector-3d.md +1431 -0
  746. package/docs/techniques/file-io.md +1427 -0
  747. package/docs/techniques/input.md +1072 -0
  748. package/docs/techniques/loaders-packers.md +768 -0
  749. package/docs/techniques/logic.md +3091 -0
  750. package/docs/techniques/maths.md +1524 -0
  751. package/docs/techniques/memory-banking.md +1686 -0
  752. package/docs/techniques/music-sid.md +1144 -0
  753. package/docs/techniques/raster.md +1543 -0
  754. package/docs/techniques/scroll.md +1292 -0
  755. package/docs/techniques/sprite.md +2566 -0
  756. package/docs/techniques/text-mode-render.md +717 -0
  757. package/docs/techniques/text.md +1095 -0
  758. package/docs/techniques/transitions.md +405 -0
  759. package/docs/toolchains/cartconv-reference.md +262 -0
  760. package/docs/toolchains/cc1541-reference.md +316 -0
  761. package/docs/toolchains/cc65-reference.md +667 -0
  762. package/docs/toolchains/error-sources/cc65/README.md +1 -0
  763. package/docs/toolchains/error-sources/cc65/missing-setcpu.s +4 -0
  764. package/docs/toolchains/error-sources/cc65/range-error.s +4 -0
  765. package/docs/toolchains/error-sources/cc65/segment-overflow.c +6 -0
  766. package/docs/toolchains/error-sources/cc65/unresolved-external.c +6 -0
  767. package/docs/toolchains/error-sources/kickassembler/README.md +1 -0
  768. package/docs/toolchains/error-sources/kickassembler/branch-out-of-range.asm +4 -0
  769. package/docs/toolchains/error-sources/kickassembler/failed-assert.asm +3 -0
  770. package/docs/toolchains/error-sources/kickassembler/flat-lib.asm +4 -0
  771. package/docs/toolchains/error-sources/kickassembler/import-source-no-namespace.asm +5 -0
  772. package/docs/toolchains/error-sources/kickassembler/macro-without-parentheses.asm +6 -0
  773. package/docs/toolchains/error-sources/kickassembler/memory-block-overlap.asm +4 -0
  774. package/docs/toolchains/error-sources/kickassembler/symbol-clash-lib.asm +5 -0
  775. package/docs/toolchains/error-sources/kickassembler/symbol-clash-main.asm +6 -0
  776. package/docs/toolchains/error-sources/kickassembler/unknown-symbol.asm +4 -0
  777. package/docs/toolchains/error-sources/oscar64/README.md +1 -0
  778. package/docs/toolchains/error-sources/oscar64/asm-addressing-mode.c +7 -0
  779. package/docs/toolchains/error-sources/oscar64/region-overflow.c +6 -0
  780. package/docs/toolchains/error-sources/oscar64/undefined-extern-var.c +5 -0
  781. package/docs/toolchains/error-sources/oscar64/undefined-extern.c +6 -0
  782. package/docs/toolchains/error-sources/oscar64/unknown-vic-field.c +6 -0
  783. package/docs/toolchains/error-sources/oscar64/void-main.c +3 -0
  784. package/docs/toolchains/kickassembler-reference.md +1020 -0
  785. package/docs/toolchains/memory-layout-planning.md +362 -0
  786. package/docs/toolchains/oscar64-headers-reference.md +583 -0
  787. package/docs/toolchains/oscar64-reference.md +892 -0
  788. package/docs/toolchains/petcat-reference.md +290 -0
  789. package/docs/toolchains/png2prg.md +188 -0
  790. package/docs/toolchains/release-disk.md +314 -0
  791. package/docs/toolchains/sidreloc.md +236 -0
  792. package/docs/toolchains/spindle-reference.md +487 -0
  793. package/docs/toolchains/tape-mastering.md +592 -0
  794. package/docs/toolchains/unit-testing-6502.md +281 -0
  795. package/docs/workflow/agent-harness.md +478 -0
  796. package/package.json +105 -0
  797. package/templates/_harness/CLAUDE.md.template +91 -0
  798. package/templates/_harness/PLAN.md.template +72 -0
  799. package/templates/_harness/check.py +442 -0
  800. package/templates/_harness/claude/settings.json +17 -0
  801. package/templates/_harness/gen-asm-header.py +96 -0
  802. package/templates/_harness/harness.mk +273 -0
  803. package/templates/_harness/hooks/plan-gate.py +185 -0
  804. package/templates/_harness/meter/frame_meter.asm +311 -0
  805. package/templates/_harness/meter/frame_meter.c +155 -0
  806. package/templates/_harness/meter/frame_meter.h +80 -0
  807. package/templates/_harness/zp-used.py +104 -0
  808. package/templates/action-puzzle/.claude/settings.json +17 -0
  809. package/templates/action-puzzle/.mcp.json +10 -0
  810. package/templates/action-puzzle/CLAUDE.md +120 -0
  811. package/templates/action-puzzle/Makefile +125 -0
  812. package/templates/action-puzzle/PLAN.md +388 -0
  813. package/templates/action-puzzle/README.md +240 -0
  814. package/templates/action-puzzle/expect.json +25 -0
  815. package/templates/action-puzzle/src/cave.c +280 -0
  816. package/templates/action-puzzle/src/cave.h +80 -0
  817. package/templates/action-puzzle/src/gen_autopilot.h +20 -0
  818. package/templates/action-puzzle/src/gen_caves.h +59 -0
  819. package/templates/action-puzzle/src/gen_notes.h +16 -0
  820. package/templates/action-puzzle/src/hiscore.c +152 -0
  821. package/templates/action-puzzle/src/hiscore.h +41 -0
  822. package/templates/action-puzzle/src/level.c +64 -0
  823. package/templates/action-puzzle/src/level.h +24 -0
  824. package/templates/action-puzzle/src/main.c +599 -0
  825. package/templates/action-puzzle/src/render.c +239 -0
  826. package/templates/action-puzzle/src/render.h +31 -0
  827. package/templates/action-puzzle/src/sound.c +158 -0
  828. package/templates/action-puzzle/src/sound.h +22 -0
  829. package/templates/action-puzzle/tools/disk_check.py +77 -0
  830. package/templates/action-puzzle/tools/drive.py +106 -0
  831. package/templates/action-puzzle/tools/gen.py +454 -0
  832. package/templates/action-puzzle/tools/model_check.py +49 -0
  833. package/templates/action-puzzle/tools/verdict_codes.py +24 -0
  834. package/templates/adventure/.claude/settings.json +17 -0
  835. package/templates/adventure/.mcp.json +10 -0
  836. package/templates/adventure/CLAUDE.md +118 -0
  837. package/templates/adventure/Makefile +113 -0
  838. package/templates/adventure/PLAN.md +396 -0
  839. package/templates/adventure/README.md +277 -0
  840. package/templates/adventure/expect.json +25 -0
  841. package/templates/adventure/src/engine.c +543 -0
  842. package/templates/adventure/src/engine.h +48 -0
  843. package/templates/adventure/src/gen_script.h +141 -0
  844. package/templates/adventure/src/gen_world.h +511 -0
  845. package/templates/adventure/src/main.c +439 -0
  846. package/templates/adventure/src/picture.c +83 -0
  847. package/templates/adventure/src/picture.h +16 -0
  848. package/templates/adventure/src/save.c +159 -0
  849. package/templates/adventure/src/save.h +25 -0
  850. package/templates/adventure/src/sound.c +71 -0
  851. package/templates/adventure/src/sound.h +16 -0
  852. package/templates/adventure/src/text.c +349 -0
  853. package/templates/adventure/src/text.h +40 -0
  854. package/templates/adventure/tools/disk_check.py +128 -0
  855. package/templates/adventure/tools/drive.py +119 -0
  856. package/templates/adventure/tools/gen.py +1033 -0
  857. package/templates/adventure/tools/world.py +356 -0
  858. package/templates/beat-em-up/.claude/settings.json +17 -0
  859. package/templates/beat-em-up/.mcp.json +10 -0
  860. package/templates/beat-em-up/CLAUDE.md +136 -0
  861. package/templates/beat-em-up/Makefile +96 -0
  862. package/templates/beat-em-up/PLAN.md +502 -0
  863. package/templates/beat-em-up/README.md +315 -0
  864. package/templates/beat-em-up/expect-gameover.json +8 -0
  865. package/templates/beat-em-up/expect.json +29 -0
  866. package/templates/beat-em-up/src/anim.c +70 -0
  867. package/templates/beat-em-up/src/art.c +344 -0
  868. package/templates/beat-em-up/src/autopilot.h +154 -0
  869. package/templates/beat-em-up/src/brute.c +273 -0
  870. package/templates/beat-em-up/src/enemy.c +281 -0
  871. package/templates/beat-em-up/src/engine.asm +512 -0
  872. package/templates/beat-em-up/src/fighter.c +384 -0
  873. package/templates/beat-em-up/src/game.h +269 -0
  874. package/templates/beat-em-up/src/hud.c +135 -0
  875. package/templates/beat-em-up/src/main.c +341 -0
  876. package/templates/beat-em-up/src/sound.c +44 -0
  877. package/templates/beat-em-up/src/street.c +93 -0
  878. package/templates/beat-em-up/src/verdict.h +523 -0
  879. package/templates/beat-em-up/src/view.c +362 -0
  880. package/templates/beat-em-up/tools/drive.py +105 -0
  881. package/templates/beat-em-up/tools/flickercheck.py +273 -0
  882. package/templates/demo/.claude/settings.json +17 -0
  883. package/templates/demo/.mcp.json +10 -0
  884. package/templates/demo/CLAUDE.md +129 -0
  885. package/templates/demo/Makefile +66 -0
  886. package/templates/demo/PLAN.md +401 -0
  887. package/templates/demo/README.md +322 -0
  888. package/templates/demo/expect.json +945 -0
  889. package/templates/demo/src/config.asm +80 -0
  890. package/templates/demo/src/framework.asm +390 -0
  891. package/templates/demo/src/main.asm +139 -0
  892. package/templates/demo/src/music.asm +197 -0
  893. package/templates/demo/src/part_main.asm +279 -0
  894. package/templates/demo/src/part_title.asm +79 -0
  895. package/templates/demo/src/parts.asm +43 -0
  896. package/templates/demo/src/tables.asm +73 -0
  897. package/templates/demo/src/verdict.asm +179 -0
  898. package/templates/demo/tools/audio.py +80 -0
  899. package/templates/demo/tools/gen_expect.py +167 -0
  900. package/templates/demo/tools/probe.py +95 -0
  901. package/templates/hello/.claude/settings.json +17 -0
  902. package/templates/hello/.mcp.json +10 -0
  903. package/templates/hello/CLAUDE.md +100 -0
  904. package/templates/hello/Makefile +38 -0
  905. package/templates/hello/PLAN.md +200 -0
  906. package/templates/hello/expect.json +20 -0
  907. package/templates/hello/src/main.c +161 -0
  908. package/templates/hello/src/sprite.asm +27 -0
  909. package/templates/hello-kick/.claude/settings.json +17 -0
  910. package/templates/hello-kick/.mcp.json +10 -0
  911. package/templates/hello-kick/CLAUDE.md +100 -0
  912. package/templates/hello-kick/Makefile +17 -0
  913. package/templates/hello-kick/PLAN.md +199 -0
  914. package/templates/hello-kick/expect.json +20 -0
  915. package/templates/hello-kick/src/main.asm +233 -0
  916. package/templates/platformer/.claude/settings.json +17 -0
  917. package/templates/platformer/.mcp.json +10 -0
  918. package/templates/platformer/CLAUDE.md +125 -0
  919. package/templates/platformer/Makefile +86 -0
  920. package/templates/platformer/PLAN.md +360 -0
  921. package/templates/platformer/README.md +277 -0
  922. package/templates/platformer/expect.json +32 -0
  923. package/templates/platformer/src/actors.c +289 -0
  924. package/templates/platformer/src/anim.c +51 -0
  925. package/templates/platformer/src/art.c +212 -0
  926. package/templates/platformer/src/autopilot.h +30 -0
  927. package/templates/platformer/src/engine.asm +253 -0
  928. package/templates/platformer/src/game.h +248 -0
  929. package/templates/platformer/src/hud.c +90 -0
  930. package/templates/platformer/src/level.c +184 -0
  931. package/templates/platformer/src/main.c +306 -0
  932. package/templates/platformer/src/player.c +268 -0
  933. package/templates/platformer/src/sound.c +42 -0
  934. package/templates/platformer/src/verdict.h +206 -0
  935. package/templates/platformer/src/view.c +331 -0
  936. package/templates/platformer/stage-expect.json +6 -0
  937. package/templates/platformer/tools/drive.py +112 -0
  938. package/templates/platformer/tools/tearcheck.py +229 -0
  939. package/templates/shmup-vertical/.claude/settings.json +17 -0
  940. package/templates/shmup-vertical/.mcp.json +10 -0
  941. package/templates/shmup-vertical/CLAUDE.md +137 -0
  942. package/templates/shmup-vertical/Makefile +131 -0
  943. package/templates/shmup-vertical/PLAN.md +569 -0
  944. package/templates/shmup-vertical/README.md +189 -0
  945. package/templates/shmup-vertical/expect.json +38 -0
  946. package/templates/shmup-vertical/src/bullets.c +158 -0
  947. package/templates/shmup-vertical/src/bullets.h +35 -0
  948. package/templates/shmup-vertical/src/display.c +239 -0
  949. package/templates/shmup-vertical/src/display.h +40 -0
  950. package/templates/shmup-vertical/src/game.h +85 -0
  951. package/templates/shmup-vertical/src/glyph.asm +358 -0
  952. package/templates/shmup-vertical/src/hiscore.c +141 -0
  953. package/templates/shmup-vertical/src/hiscore.h +16 -0
  954. package/templates/shmup-vertical/src/hit.asm +180 -0
  955. package/templates/shmup-vertical/src/hitbox.c +59 -0
  956. package/templates/shmup-vertical/src/hitbox.h +25 -0
  957. package/templates/shmup-vertical/src/kernel.asm +330 -0
  958. package/templates/shmup-vertical/src/level.c +174 -0
  959. package/templates/shmup-vertical/src/level.h +27 -0
  960. package/templates/shmup-vertical/src/main.c +824 -0
  961. package/templates/shmup-vertical/src/mux.asm +321 -0
  962. package/templates/shmup-vertical/src/sound.asm +255 -0
  963. package/templates/shmup-vertical/src/step.asm +169 -0
  964. package/templates/shmup-vertical/src/waves.c +195 -0
  965. package/templates/shmup-vertical/src/waves.h +33 -0
  966. package/templates/shmup-vertical/stage-expect.json +6 -0
  967. package/templates/shmup-vertical/tools/drive.py +152 -0
  968. package/templates/shmup-vertical/tools/joytest.py +159 -0
  969. package/templates/shmup-vertical/tools/meter.py +15 -0
  970. package/templates/shmup-vertical/tools/phases.py +73 -0
@@ -0,0 +1,1028 @@
1
+ ---
2
+ recipe: lane-pursuit
3
+ toolchain: oscar64
4
+ output_format: PRG
5
+ region: both
6
+ techniques: [lane_pursuit_ai]
7
+ file_formats: [PRG]
8
+ uses_registers: [D000, D001, D002, D003, D004, D005, D006, D007, D008, D009, D010, D011, D015, D020, D021, D022, D023, D027, D028, D029, D02A, D02B, DC06, DC07, DC0F]
9
+ uses_kernal: []
10
+ ---
11
+
12
+ <!-- doc-type: recipe -->
13
+
14
+ # Oscar64 Lane Pursuit: road cars that pull alongside, ram and avoid the edges, checked against a model
15
+
16
+ ## Synopsis
17
+
18
+ A road-game pursuit AI on an original scrolling road. The road narrows
19
+ from the left, widens again, and is then split in two by a water island,
20
+ the fork-like obstacle. The player car (yellow, sprite 0) drives on
21
+ autopilot, weaving, and takes the left channel at the island. Four
22
+ pursuit cars (sprites 1 to 4) enter from behind and ahead on a spawn
23
+ table and run one state machine: approach a slot beside the player, hold
24
+ it, ram with a lead on the player's sideways velocity, back off, and
25
+ leave when shot. Each car steers with a capped proportional rule in 8.8
26
+ fixed point, keeps a spacing box from the other pursuers, and probes the
27
+ map ahead of its nose so it never drives onto the verge or the water.
28
+ A "frame" on this page is one logic step. Pass 1 runs 780 of them on
29
+ screen, one every second video frame (measured below), and folds every
30
+ car's state and x into a checksum each step; every 78 steps it is
31
+ compared with a Python model of the same rules, compiled in. Then, with
32
+ the display off, pass 2 reruns the 780 steps under CIA1 timer B, and a
33
+ built worst frame is timed.
34
+ `$02FF` = `01` and a green border on pass, `02` and red on fail. This is
35
+ `lane_pursuit_ai` in `docs/techniques/logic.md`; the result-byte contract
36
+ is `headless-verify.md`.
37
+
38
+ ## Source
39
+
40
+ ```c
41
+ // lane-pursuit.c
42
+ //
43
+ // Lane-pursuit AI for a vertical road game. A "frame" below is one logic
44
+ // step. A road scrolls down the screen at 2 pixels a step: a wide
45
+ // stretch, a narrowing from the left, and a water island that splits the
46
+ // road in two. The player car (sprite 0) is on autopilot: it steers
47
+ // toward the centre of the leftmost road run 32 pixels ahead, plus a
48
+ // triangle weave. Four pursuit cars (sprites 1 to 4)
49
+ // enter from behind or ahead on a spawn table and run one state machine:
50
+ // APPROACH to a slot 24 pixels beside the player (one car per side),
51
+ // ALONG for 40 frames, RAM toward the player's position led by 8 frames of
52
+ // its lateral velocity, BACK off 40 pixels behind, and LEAVE when shot
53
+ // (car 3 at frame 330), freeing the slot 150 pixels behind. Steering is a
54
+ // proportional rule in 8.8 with a speed cap and an acceleration cap;
55
+ // forward speed is held relative to the player's the same way. Each car
56
+ // keeps a spacing box from the other pursuers, probes the map row 32
57
+ // pixels ahead of its nose across the span it will sweep, and when that is
58
+ // blocked steers 32 pixels toward the side with more road; otherwise a side
59
+ // probe 12 pixels beyond its body stops it steering into an edge.
60
+ // Pass 1 runs 780 frames on screen, one every second video frame because a
61
+ // step and its redraw overrun one; each frame every car's state and x are
62
+ // folded into a checksum, checked every 78 frames against a Python model of
63
+ // the same rules compiled in below. Then, with the display and sprites
64
+ // off, pass 2 reruns the 780 frames under CIA1 timer B, and a built worst
65
+ // frame is timed.
66
+ // Row 0: frame, checksum, checkpoints matched, result, leaves. Row 1:
67
+ // contacts, rams, avoid frames, hold frames, spacing frames. Row 23: CIA1
68
+ // cycles per car update, mean and worst of pass 2, built worst car, the
69
+ // edge probes on their own. Row 24: worst frame of pass 2, built worst
70
+ // frame, empty timer pair.
71
+ // $02FF = 01 and a green border on pass, 02 and red on fail.
72
+ // Build: oscar64 -tm=c64 -O2 -o=lane-pursuit.prg lane-pursuit.c
73
+
74
+ #include <c64/vic.h>
75
+ #include <c64/cia.h>
76
+ #include <string.h>
77
+
78
+ #define SCREEN ((char *)0x0400)
79
+ #define COLOUR ((char *)0xd800)
80
+ #define SPRPTR ((char *)0x07f8)
81
+ #define SPRDATA ((char *)0x0340) // blocks 13 and 14, cassette buffer
82
+ #define RESULT (*(volatile char *)0x02ff)
83
+ #define CODE_PASS 0x01
84
+ #define CODE_FAIL 0x02
85
+
86
+ #define NCAR 4
87
+ #define FRAMES 780
88
+ #define CKSTEP 78
89
+ #define NCK 10
90
+ #define MW 32 // map tiles across
91
+ #define MH 64 // map rows per loop
92
+ #define ROAD 0x20 // ECM: space on background 0
93
+ #define VERGE 0x60 // space on background 1
94
+ #define WATER 0xa0 // space on background 2
95
+ #define PSPD 32 // player speed, 1/16 px a frame
96
+ #define CW 12 // car body, pixels
97
+ #define CH 16
98
+ #define LOOK 32 // look-ahead beyond the nose, px
99
+ #define MARGIN 12 // side probe beyond the body, px
100
+ #define SEP_X 20 // spacing box between pursuers, px
101
+ #define SEP_Y 32
102
+ #define SIDE_DX 24
103
+ #define BACK_DX 40
104
+ #define BACK_DY 40
105
+ #define LEAD 8 // frames of player velocity led
106
+ #define T_ALONG 40
107
+ #define T_RAM 24
108
+ #define T_BACK 50
109
+ #define VAVOID 512 // lateral caps while avoiding, 8.8
110
+ #define AAVOID 128
111
+ #define RSMAX 24 // relative forward caps, 1/16 px
112
+ #define RSLEAVE 48
113
+ #define FACC 2
114
+ #define NONE 0xff
115
+
116
+ #define APPROACH 1
117
+ #define ALONG 2
118
+ #define RAM 3
119
+ #define BACK 4
120
+ #define LEAVE 5
121
+
122
+ #define COL0 4 // playfield: text columns 4 to 35
123
+ #define PROW 14 // text row the player's tile is on
124
+
125
+ // The Python model's checksum every 78 frames and its event counts.
126
+ const unsigned exp_ck[NCK] = {
127
+ 0xf84d, 0x45ab, 0x6a75, 0xd437, 0x8f47, 0xebe2, 0x37fc, 0x8460, 0xd465, 0xba59
128
+ };
129
+ #define EXP_CONTACTS 3
130
+ #define EXP_RAMS 7
131
+ #define EXP_AVOIDS 60
132
+ #define EXP_HOLDS 318
133
+ #define EXP_SEPS 865
134
+ #define EXP_LEAVES 1
135
+
136
+ const int vmax_t[6] = {0, 256, 256, 768, 256, 256}; // lateral speed cap
137
+ const int acc_t[6] = {0, 32, 32, 96, 32, 32}; // lateral accel cap
138
+
139
+ // Spawn table: frame, slot, x, offset in px (+ ahead, - behind), shot frame.
140
+ #define NSPAWN 5
141
+ const unsigned sp_f[NSPAWN] = {8, 8, 40, 90, 420};
142
+ const char sp_k[NSPAWN] = {0, 1, 2, 3, 3};
143
+ const char sp_x[NSPAWN] = {56, 184, 176, 64, 176};
144
+ const signed char sp_dy[NSPAWN] = {-72, 104, -72, 104, -72};
145
+ const unsigned sp_shot[NSPAWN] = {0, 0, 0, 330, 0};
146
+
147
+ char map[MH][MW];
148
+ char cen[MH]; // centre of leftmost road run, px
149
+
150
+ // Player and cars. y is world position in 1/16 px (up is +), x is 8.8.
151
+ unsigned P;
152
+ int ppx;
153
+ signed char pvx;
154
+ char st[NCAR], tm[NCAR], owner[2];
155
+ signed char side[NCAR], rs[NCAR];
156
+ unsigned x[NCAR], y[NCAR], shot[NCAR];
157
+ int vx[NCAR];
158
+ char sp;
159
+ unsigned contacts, rams, avoids, holds, seps, leaves;
160
+
161
+ static inline int iabs(int v) { return v < 0 ? -v : v; }
162
+ static inline int clampi(int v, int a, int b) { return v < a ? a : (v > b ? b : v); }
163
+ static inline int sm(int e, char s) { return e >= 0 ? e >> s : -((-e) >> s); }
164
+ static inline char tile(unsigned y16, int px) { return map[(y16 >> 7) & 63][(px >> 3) & 31]; }
165
+
166
+ void build_map(void)
167
+ {
168
+ for (char r = 0; r < MH; r++) {
169
+ char L = 6, R = 26, i0 = 0, i1 = 0;
170
+ if (r >= 16 && r <= 23) L = 6 + (r - 15);
171
+ else if (r >= 24 && r <= 31) L = 14;
172
+ else if (r >= 32 && r <= 35) L = 14 - 2 * (r - 31);
173
+ if (r >= 38 && r <= 49) { i0 = 14; i1 = 18; }
174
+ for (char c = 0; c < MW; c++) {
175
+ char t = ROAD;
176
+ if (c < L || c >= R) t = VERGE;
177
+ else if (c >= i0 && c < i1) t = WATER;
178
+ map[r][c] = t;
179
+ }
180
+ char e = L;
181
+ while (e < MW && map[r][e] == ROAD)
182
+ e++;
183
+ cen[r] = (L + e) * 4;
184
+ }
185
+ }
186
+
187
+ void init(void)
188
+ {
189
+ P = 1024 * 16;
190
+ ppx = cen[(P >> 7) & 63] - 6;
191
+ pvx = 0;
192
+ for (char i = 0; i < NCAR; i++) {
193
+ st[i] = 0; x[i] = 0; vx[i] = 0; y[i] = 0; rs[i] = 0;
194
+ side[i] = 1; tm[i] = 0; shot[i] = 0;
195
+ }
196
+ contacts = rams = avoids = holds = seps = leaves = 0;
197
+ sp = 0;
198
+ owner[0] = owner[1] = NONE;
199
+ }
200
+
201
+ static inline int tri(unsigned f)
202
+ {
203
+ char t = f & 63;
204
+ return t < 32 ? (int)t - 16 : 48 - (int)t;
205
+ }
206
+
207
+ // The autopilot stands in for the joystick; it is not the technique.
208
+ void player_step(unsigned f)
209
+ {
210
+ P += PSPD;
211
+ int tp = cen[((P + 32 * 16) >> 7) & 63] + tri(f) - 6;
212
+ pvx = clampi(tp - ppx, -2, 2);
213
+ ppx += pvx;
214
+ }
215
+
216
+ void spawn(unsigned f)
217
+ {
218
+ while (sp < NSPAWN && sp_f[sp] == f) {
219
+ char k = sp_k[sp];
220
+ st[k] = APPROACH;
221
+ x[k] = (unsigned)sp_x[sp] << 8;
222
+ vx[k] = 0;
223
+ y[k] = P + sp_dy[sp] * 16;
224
+ rs[k] = 0;
225
+ shot[k] = sp_shot[sp];
226
+ sp++;
227
+ }
228
+ }
229
+
230
+ // True when any tile from pixel a to pixel b on the row of y16 is not road,
231
+ // probing every 8 pixels and at b, so no tile between is skipped.
232
+ bool blocked(unsigned y16, int a, int b)
233
+ {
234
+ const char *row = map[(y16 >> 7) & 63];
235
+ while (a < b) {
236
+ if (row[(a >> 3) & 31] != ROAD)
237
+ return true;
238
+ a += 8;
239
+ }
240
+ return row[(b >> 3) & 31] != ROAD;
241
+ }
242
+
243
+ char room(char r, signed char c, signed char d)
244
+ {
245
+ char n = 0;
246
+ for (char i = 1; i <= 8; i++) {
247
+ c += d;
248
+ if (c >= 0 && c < MW && map[r][c] == ROAD)
249
+ n++;
250
+ }
251
+ return n;
252
+ }
253
+
254
+ static inline void release(char i)
255
+ {
256
+ if (owner[0] == i) owner[0] = NONE;
257
+ if (owner[1] == i) owner[1] = NONE;
258
+ }
259
+
260
+ // One pursuit car's update for frame f: state, spacing, edge probes,
261
+ // lateral and forward controllers.
262
+ void car_step(char i, unsigned f)
263
+ {
264
+ char s = st[i];
265
+ if (!s)
266
+ return;
267
+ if (shot[i] == f && s != LEAVE) {
268
+ s = LEAVE;
269
+ leaves++;
270
+ release(i);
271
+ }
272
+ int px = x[i] >> 8;
273
+ int rel = (int)(y[i] - P);
274
+ int dx = px - ppx;
275
+ int tx, rt = 0;
276
+
277
+ switch (s) {
278
+ case APPROACH: {
279
+ char sd = dx >= 0 ? 1 : 0;
280
+ if (owner[sd] != NONE && owner[sd] != i)
281
+ sd ^= 1;
282
+ if (owner[sd] == NONE || owner[sd] == i) {
283
+ owner[sd] = i;
284
+ side[i] = sd ? 1 : -1;
285
+ tx = side[i] > 0 ? ppx + SIDE_DX : ppx - SIDE_DX;
286
+ if (iabs(rel) < 8 * 16 && iabs(px - tx) < 6) {
287
+ s = ALONG;
288
+ tm[i] = T_ALONG;
289
+ }
290
+ } else { // both sides taken: queue behind
291
+ tx = px;
292
+ rt = -BACK_DY * 16;
293
+ }
294
+ break;
295
+ }
296
+ case ALONG:
297
+ tx = side[i] > 0 ? ppx + SIDE_DX : ppx - SIDE_DX;
298
+ if (--tm[i] == 0) {
299
+ s = RAM;
300
+ tm[i] = T_RAM;
301
+ rams++;
302
+ }
303
+ break;
304
+ case RAM:
305
+ tx = ppx + (pvx << 3); // LEAD = 8 frames
306
+ tm[i]--;
307
+ if (iabs(dx) < 14 && iabs(rel) < 16 * 16) {
308
+ contacts++;
309
+ s = BACK;
310
+ tm[i] = T_BACK;
311
+ } else if (tm[i] == 0) {
312
+ s = BACK;
313
+ tm[i] = T_BACK;
314
+ }
315
+ if (s == BACK)
316
+ release(i);
317
+ break;
318
+ case BACK:
319
+ tx = side[i] > 0 ? ppx + BACK_DX : ppx - BACK_DX;
320
+ rt = -BACK_DY * 16;
321
+ if (--tm[i] == 0)
322
+ s = APPROACH;
323
+ break;
324
+ default:
325
+ tx = px;
326
+ rt = -200 * 16;
327
+ }
328
+
329
+ // Spacing: steer and hold back from the first pursuer too close.
330
+ for (char j = 0; j < NCAR; j++) {
331
+ if (j == i || !st[j])
332
+ continue;
333
+ int ddx = (int)(x[j] >> 8) - px;
334
+ int ddy = (int)(y[j] - y[i]);
335
+ if (iabs(ddx) < SEP_X && iabs(ddy) < SEP_Y * 16) {
336
+ tx = (ddx > 0 || (ddx == 0 && i < j)) ? px - SEP_X : px + SEP_X;
337
+ if (ddy > 0)
338
+ rt = rel + ddy - SEP_Y * 16;
339
+ seps++;
340
+ break;
341
+ }
342
+ }
343
+
344
+ // Edges: probe the row LOOK pixels past the nose across the swept span.
345
+ int vmax = vmax_t[s], acc = acc_t[s];
346
+ unsigned ahead = y[i] + (CH - 1 + LOOK) * 16;
347
+ int qx = clampi(px + sm(vx[i], 4), 0, 255 - CW);
348
+ int a = px < qx ? px : qx, b = (px > qx ? px : qx) + CW - 1;
349
+ if (blocked(ahead, a, b)) {
350
+ char r = (ahead >> 7) & 63;
351
+ signed char c = (qx + 6) >> 3;
352
+ tx = room(r, c, -1) > room(r, c, 1) ? px - 32 : px + 32;
353
+ vmax = VAVOID;
354
+ acc = AAVOID;
355
+ avoids++;
356
+ } else { // side probe toward the target
357
+ int sx = px;
358
+ if (tx > px) sx = px + CW - 1 + MARGIN;
359
+ else if (tx < px) sx = px - MARGIN;
360
+ if (sx >= 0 && sx <= 255 &&
361
+ (tile(y[i] + (CH - 1 + 8) * 16, sx) != ROAD || tile(y[i], sx) != ROAD)) {
362
+ tx = px;
363
+ acc = AAVOID;
364
+ holds++;
365
+ }
366
+ }
367
+ tx = clampi(tx, 8, 236);
368
+
369
+ // Lateral: proportional, speed cap, acceleration cap, all 8.8.
370
+ int want = clampi((tx - px) << 4, -vmax, vmax);
371
+ int v = vx[i];
372
+ if (want > v + acc) v += acc;
373
+ else if (want < v - acc) v -= acc;
374
+ else v = want;
375
+ vx[i] = v;
376
+ x[i] += v;
377
+
378
+ // Forward: speed relative to the player's, toward the wanted offset.
379
+ int cap = s == LEAVE ? RSLEAVE : RSMAX;
380
+ want = clampi(sm(rt - rel, 3), -cap, cap);
381
+ int r = rs[i];
382
+ if (want > r + FACC) r += FACC;
383
+ else if (want < r - FACC) r -= FACC;
384
+ else r = want;
385
+ rs[i] = r;
386
+ y[i] += PSPD + r;
387
+ if (s == LEAVE && (int)(y[i] - P) < -150 * 16)
388
+ s = 0;
389
+ st[i] = s;
390
+ }
391
+
392
+ // --- CIA1 timer B ------------------------------------------------------------
393
+ static inline void timer_start(void)
394
+ {
395
+ cia1.crb = 0x00;
396
+ cia1.tb = 0xffff;
397
+ cia1.crb = 0x11;
398
+ }
399
+
400
+ static inline unsigned timer_stop(void)
401
+ {
402
+ cia1.crb = 0x00;
403
+ return 0xffff - cia1.tb;
404
+ }
405
+
406
+ // Built worst frame: the table's two frame-8 spawns land, then all four cars
407
+ // are in RAM with the timer running out (car 0 in contact with the player),
408
+ // a full spacing scan, moving right at the RAM speed cap so the ahead probe
409
+ // sweeps 60 pixels and finds the verge only at its last point, and both
410
+ // room scans run. Set-up is outside the timer.
411
+ unsigned worst_frame(void)
412
+ {
413
+ init();
414
+ timer_start();
415
+ spawn(sp_f[0]);
416
+ unsigned t = timer_stop();
417
+ P = 0x4000;
418
+ ppx = 145; pvx = 2;
419
+ for (char i = 0; i < NCAR; i++) {
420
+ st[i] = RAM; tm[i] = 1; side[i] = 1;
421
+ x[i] = 150 << 8; vx[i] = 768; rs[i] = 0;
422
+ y[i] = P + 128 + i * (SEP_Y * 16 + 64);
423
+ shot[i] = 0;
424
+ }
425
+ owner[1] = 0;
426
+ timer_start();
427
+ for (char i = 0; i < NCAR; i++)
428
+ car_step(i, 1);
429
+ return t + timer_stop();
430
+ }
431
+
432
+ // --- screen ------------------------------------------------------------------
433
+ void put_str(char *p, const char *s)
434
+ {
435
+ while (*s)
436
+ *p++ = *s++;
437
+ }
438
+
439
+ void put_dec(char *p, unsigned v, char digits)
440
+ {
441
+ for (char i = digits; i > 0; i--) {
442
+ p[i - 1] = 0x30 + (char)(v % 10);
443
+ v /= 10;
444
+ }
445
+ }
446
+
447
+ void put_hex(char *p, unsigned v)
448
+ {
449
+ for (char i = 4; i > 0; i--) {
450
+ char n = v & 15;
451
+ p[i - 1] = n < 10 ? 0x30 + n : n - 9;
452
+ v >>= 4;
453
+ }
454
+ }
455
+
456
+ // Text row r (2 to 22) shows map row prow + PROW - r.
457
+ void draw_road(void)
458
+ {
459
+ char prow = (P >> 7) & 63;
460
+ for (char r = 2; r <= 22; r++)
461
+ memcpy(SCREEN + r * 40 + COL0, map[(prow + PROW - r) & 63], MW);
462
+ }
463
+
464
+ // A world y shows at sprite Y 42 + 8 PROW - (y - P floored to a tile), px,
465
+ // so a car's rear row sits on the text row of its tile.
466
+ void place(char n, int px, unsigned wy)
467
+ {
468
+ int d = (int)(((wy - (P & 0xff80)) + 0x4000) >> 4) - 0x400;
469
+ int sy = 42 + 8 * PROW - d;
470
+ unsigned sx = 24 + 8 * COL0 + px;
471
+ char bit = 1 << n;
472
+ if (sy < 50 || sy > 220) {
473
+ vic.spr_enable &= ~bit;
474
+ return;
475
+ }
476
+ vic.spr_pos[n].x = sx;
477
+ vic.spr_pos[n].y = sy;
478
+ if (sx & 0x100) vic.spr_msbx |= bit;
479
+ else vic.spr_msbx &= ~bit;
480
+ vic.spr_enable |= bit;
481
+ }
482
+
483
+ void place_all(void)
484
+ {
485
+ place(0, ppx, P);
486
+ for (char i = 0; i < NCAR; i++)
487
+ if (st[i]) place(i + 1, x[i] >> 8, y[i]);
488
+ else vic.spr_enable &= ~(2 << i);
489
+ }
490
+
491
+ int main(void)
492
+ {
493
+ __asm { sei }
494
+ vic.color_border = VCOL_BLACK;
495
+ vic.color_back = VCOL_DARK_GREY; // road
496
+ vic.color_back1 = VCOL_GREEN; // verge
497
+ vic.color_back2 = VCOL_BLUE; // water
498
+ vic.ctrl1 |= VIC_CTRL1_ECM;
499
+ memset(SCREEN, ROAD, 1000);
500
+ memset(COLOUR, VCOL_WHITE, 1000);
501
+ build_map();
502
+
503
+ // Sprite shapes: block 13 a 12 x 16 outline (a car), block 14 the same.
504
+ memset(SPRDATA, 0, 128);
505
+ for (char b = 0; b < 2; b++) {
506
+ char *d = SPRDATA + 64 * b;
507
+ d[0] = 0xff; d[1] = 0xf0;
508
+ d[45] = 0xff; d[46] = 0xf0;
509
+ for (char r = 1; r < 15; r++) {
510
+ d[r * 3] = 0x80;
511
+ d[r * 3 + 1] = 0x10;
512
+ }
513
+ }
514
+ SPRPTR[0] = 14;
515
+ for (char s = 1; s <= NCAR; s++)
516
+ SPRPTR[s] = 13;
517
+ vic.spr_color[0] = VCOL_YELLOW;
518
+ vic.spr_color[1] = VCOL_RED;
519
+ vic.spr_color[2] = VCOL_PURPLE;
520
+ vic.spr_color[3] = VCOL_CYAN;
521
+ vic.spr_color[4] = VCOL_ORANGE;
522
+ vic.spr_msbx = 0;
523
+ vic.spr_enable = 0;
524
+
525
+ // Pass 1, on screen: state and x of every car into the checksum each
526
+ // frame, checked every 78 frames.
527
+ init();
528
+ put_str(SCREEN, s"f ck ok r");
529
+ unsigned ck = 0;
530
+ char ok = 0;
531
+ for (unsigned f = 1; f <= FRAMES; f++) {
532
+ vic_waitFrame();
533
+ player_step(f);
534
+ spawn(f);
535
+ for (char i = 0; i < NCAR; i++)
536
+ car_step(i, f);
537
+ for (char i = 0; i < NCAR; i++) { // the per-frame log
538
+ ck = ck * 33 + st[i];
539
+ ck = ck * 33 + (char)(x[i] >> 8);
540
+ }
541
+ if (f % CKSTEP == 0 && ck == exp_ck[f / CKSTEP - 1])
542
+ ok++;
543
+ draw_road();
544
+ place_all();
545
+ put_dec(SCREEN + 2, f, 4);
546
+ put_hex(SCREEN + 10, ck);
547
+ }
548
+ unsigned c1 = contacts, r1 = rams, a1 = avoids, h1 = holds, s1 = seps, l1 = leaves;
549
+ unsigned end_x[NCAR], end_y[NCAR];
550
+ for (char i = 0; i < NCAR; i++) {
551
+ end_x[i] = x[i];
552
+ end_y[i] = y[i];
553
+ }
554
+ int end_p = ppx;
555
+
556
+ // Display off (no badlines) and sprites off (no sprite DMA).
557
+ char en = vic.spr_enable;
558
+ vic.spr_enable = 0;
559
+ vic.ctrl1 &= ~VIC_CTRL1_DEN;
560
+ vic_waitFrame();
561
+
562
+ unsigned t_empty;
563
+ timer_start();
564
+ t_empty = timer_stop();
565
+
566
+ unsigned t_built = worst_frame();
567
+ // One car of the built frame on its own: car 0, in contact, with the
568
+ // others far off.
569
+ init();
570
+ P = 0x4000; ppx = 145; pvx = 2;
571
+ st[0] = RAM; tm[0] = 1; x[0] = 150 << 8; vx[0] = 768;
572
+ y[0] = P + 128;
573
+ for (char i = 1; i < NCAR; i++) { // three distant cars: full scan
574
+ st[i] = BACK; tm[i] = 9; x[i] = 60 << 8; y[i] = P - 64 * 16 * i;
575
+ }
576
+ timer_start();
577
+ car_step(0, 1);
578
+ unsigned t_bcar = timer_stop();
579
+ // The edge probes alone: a 52-pixel span on a clear row, so every
580
+ // probe runs and none hits, then both room scans.
581
+ timer_start();
582
+ blocked(0, 150, 209 - 8);
583
+ room(0, 20, -1);
584
+ room(0, 20, 1);
585
+ unsigned t_probe = timer_stop();
586
+
587
+ // Pass 2, timed: the same 780 frames back to back, each car update
588
+ // timed on its own. The frame figure is the spawn and the four cars.
589
+ init();
590
+ unsigned t_car = 0, t_frame = 0, n_upd = 0;
591
+ unsigned long t_sum = 0;
592
+ for (unsigned f = 1; f <= FRAMES; f++) {
593
+ player_step(f);
594
+ timer_start();
595
+ spawn(f);
596
+ unsigned tf = timer_stop();
597
+ for (char i = 0; i < NCAR; i++) {
598
+ char live = st[i];
599
+ timer_start();
600
+ car_step(i, f);
601
+ unsigned t = timer_stop();
602
+ tf += t;
603
+ if (live) {
604
+ t_sum += t;
605
+ n_upd++;
606
+ if (t > t_car) t_car = t;
607
+ }
608
+ }
609
+ if (tf > t_frame) t_frame = tf;
610
+ }
611
+ char same = contacts == c1 && rams == r1 && avoids == a1 && ppx == end_p;
612
+ for (char i = 0; i < NCAR; i++)
613
+ if (x[i] != end_x[i] || y[i] != end_y[i])
614
+ same = 0;
615
+ unsigned t_mean = (unsigned)(t_sum / n_upd);
616
+
617
+ // Pass 2 ends where pass 1 ended: that is the picture.
618
+ draw_road();
619
+ vic.ctrl1 |= VIC_CTRL1_DEN;
620
+ vic.spr_enable = en;
621
+ place_all();
622
+
623
+ put_str(SCREEN + 40, s"con ram avd hld sep");
624
+ put_dec(SCREEN + 44, c1, 2);
625
+ put_dec(SCREEN + 51, r1, 2);
626
+ put_dec(SCREEN + 58, a1, 3);
627
+ put_dec(SCREEN + 66, h1, 3);
628
+ put_dec(SCREEN + 74, s1, 3);
629
+ put_str(SCREEN + 28, s"lv");
630
+ put_dec(SCREEN + 31, l1, 1);
631
+ put_str(SCREEN + 920, s"car mean wst blt ");
632
+ put_dec(SCREEN + 929, t_mean, 4);
633
+ put_dec(SCREEN + 938, t_car, 4);
634
+ put_dec(SCREEN + 947, t_bcar, 4);
635
+ put_str(SCREEN + 952, s"prb");
636
+ put_dec(SCREEN + 956, t_probe, 4);
637
+ put_str(SCREEN + 960, s"frame wst blt tmr ");
638
+ put_dec(SCREEN + 970, t_frame, 5);
639
+ put_dec(SCREEN + 980, t_built, 5);
640
+ put_dec(SCREEN + 990, t_empty, 2);
641
+
642
+ char fault = 0;
643
+ if (ok != NCK) fault = 1;
644
+ else if (c1 != EXP_CONTACTS || r1 != EXP_RAMS || a1 != EXP_AVOIDS ||
645
+ h1 != EXP_HOLDS || s1 != EXP_SEPS || l1 != EXP_LEAVES) fault = 2;
646
+ else if (!same) fault = 3;
647
+ put_dec(SCREEN + 19, ok, 2);
648
+ char code = fault ? CODE_FAIL : CODE_PASS;
649
+ RESULT = code;
650
+ vic.color_border = fault ? VCOL_RED : VCOL_GREEN;
651
+ SCREEN[24] = 0x30 + code;
652
+ SCREEN[25] = 0x30 + fault;
653
+ for (;;)
654
+ ;
655
+ return 0;
656
+ }
657
+ ```
658
+
659
+ ## Build
660
+
661
+ ```bash
662
+ oscar64 -tm=c64 -O2 -o=lane-pursuit.prg lane-pursuit.c
663
+ ```
664
+
665
+ Oscar64 1.32.271 builds it with no warnings; the PRG is 6,009 bytes.
666
+
667
+ The checksums and the event counts come from this script (Python 3). It
668
+ restates the rules independently of the C: the same road, autopilot,
669
+ spawn table, states, spacing, probes and controllers, in the same integer
670
+ arithmetic. It prints the ten `exp_ck` values and
671
+ `contacts 3 rams 7 avoids 60 leaves 1 freed 1 offroad 0 holds 318 seps 865`.
672
+ `offroad` counts car-frames with any tile under a car's 12 x 16 body not
673
+ road; the listing does not compute it, and the model's 0 is the claim
674
+ that the probes keep every car on the road.
675
+
676
+ ```python
677
+ # lane_model.py: the reference for lane-pursuit.c. Same road, autopilot,
678
+ # spawn table and pursuit rules, written again in Python with the same
679
+ # integer arithmetic. Prints the checkpoint checksums and the event
680
+ # counts the listing compiles in.
681
+
682
+ NCAR, FRAMES, CKSTEP = 4, 780, 78
683
+ MW, MH = 32, 64 # map tiles across, rows per loop
684
+ ROAD, VERGE, WATER = 0x20, 0x60, 0xA0 # ECM screen codes: bg0, bg1, bg2
685
+ PSPD = 32 # player speed, 1/16 px a frame (2 px)
686
+ CW, CH = 12, 16 # car body in pixels
687
+ LOOK = 32 # look-ahead in pixels
688
+ APPROACH, ALONG, RAM, BACK, LEAVE = 1, 2, 3, 4, 5
689
+ VMAX = [0, 256, 256, 768, 256, 256] # lateral speed cap per state, 8.8
690
+ ACC = [0, 32, 32, 96, 32, 32] # lateral acceleration cap, 8.8
691
+ VAVOID, AAVOID = 512, 128 # caps while avoiding
692
+ RSMAX, RSLEAVE, FACC = 24, 48, 2 # relative forward speed caps, 1/16 px
693
+ SIDE_DX, BACK_DX, BACK_DY = 24, 40, 40
694
+ LEAD = 8 # frames of player velocity to lead
695
+ T_ALONG, T_RAM, T_BACK = 40, 24, 50
696
+ SEP_X, SEP_Y = 20, 32 # spacing box between pursuers, px
697
+ MARGIN = 12 # side probe beyond the body, px
698
+
699
+ def build_map():
700
+ m = []
701
+ for r in range(MH):
702
+ L, R, i0, i1 = 6, 26, 0, 0
703
+ if 16 <= r <= 23: L = 6 + (r - 15)
704
+ elif 24 <= r <= 31: L = 14
705
+ elif 32 <= r <= 35: L = 14 - 2 * (r - 31)
706
+ if 38 <= r <= 49: i0, i1 = 14, 18
707
+ row = []
708
+ for c in range(MW):
709
+ if c < L or c >= R: row.append(VERGE)
710
+ elif i0 <= c < i1: row.append(WATER)
711
+ else: row.append(ROAD)
712
+ m.append(row)
713
+ cen = []
714
+ for r in range(MH):
715
+ L = min(c for c in range(MW) if m[r][c] == ROAD)
716
+ R = L
717
+ while R < MW and m[r][R] == ROAD: R += 1
718
+ cen.append((L + R) * 4) # centre of the leftmost road run, px
719
+ return m, cen
720
+
721
+ MAP, CEN = build_map()
722
+ # spawn table: frame, slot, x px, offset px (+ ahead, - behind), shot frame
723
+ SPAWNS = [(8, 0, 56, -72, 0), (8, 1, 184, 104, 0), (40, 2, 176, -72, 0),
724
+ (90, 3, 64, 104, 330), (420, 3, 176, -72, 0)]
725
+
726
+ def s16(v): return ((v + 0x8000) & 0xFFFF) - 0x8000
727
+ def sm(e, s): return e >> s if e >= 0 else -((-e) >> s)
728
+ def clamp(v, a, b): return a if v < a else b if v > b else v
729
+ def tile(y16, px): return MAP[(y16 >> 7) & 63][(px >> 3) & 31]
730
+
731
+ class S: pass
732
+
733
+ def init():
734
+ s = S()
735
+ s.P = 1024 * 16; s.ppx = CEN[(s.P >> 7) & 63] - 6; s.pvx = 0
736
+ s.st = [0] * NCAR; s.x = [0] * NCAR; s.vx = [0] * NCAR; s.y = [0] * NCAR
737
+ s.rs = [0] * NCAR; s.side = [1] * NCAR; s.tm = [0] * NCAR; s.shot = [0] * NCAR
738
+ s.contacts = s.rams = s.avoids = s.leaves = s.offroad = s.freed = s.holds = s.seps = 0
739
+ s.sp = 0; s.owner = [0xFF, 0xFF]
740
+ return s
741
+
742
+ def tri(f):
743
+ t = f & 63
744
+ return t - 16 if t < 32 else 48 - t
745
+
746
+ def player_step(s, f):
747
+ s.P = (s.P + PSPD) & 0xFFFF
748
+ tp = CEN[((s.P + 32 * 16) >> 7) & 63] + tri(f) - 6
749
+ s.pvx = clamp(tp - s.ppx, -2, 2)
750
+ s.ppx += s.pvx
751
+
752
+ def spawn(s, f):
753
+ while s.sp < len(SPAWNS) and SPAWNS[s.sp][0] == f:
754
+ _, k, x, dy, shot = SPAWNS[s.sp]
755
+ s.st[k] = APPROACH; s.x[k] = x << 8; s.vx[k] = 0
756
+ s.y[k] = (s.P + dy * 16) & 0xFFFF; s.rs[k] = 0; s.shot[k] = shot
757
+ s.sp += 1
758
+
759
+ def room(row, c, d):
760
+ n = 0
761
+ for i in range(1, 9):
762
+ cc = c + d * i
763
+ if 0 <= cc < MW and MAP[row][cc] == ROAD: n += 1
764
+ return n
765
+
766
+ def blocked(y16, a, b): # probe a..b every 8 px and at b
767
+ while a < b:
768
+ if tile(y16, a) != ROAD: return True
769
+ a += 8
770
+ return tile(y16, b) != ROAD
771
+
772
+ def release(s, i):
773
+ for k in (0, 1):
774
+ if s.owner[k] == i: s.owner[k] = 0xFF
775
+
776
+ def car_step(s, i, f):
777
+ st = s.st[i]
778
+ if st == 0: return
779
+ if s.shot[i] == f and st != LEAVE:
780
+ st = LEAVE; s.leaves += 1; release(s, i)
781
+ px = s.x[i] >> 8
782
+ rel = s16(s.y[i] - s.P)
783
+ dx = px - s.ppx
784
+ # --- state transitions and pursuit targets ---
785
+ if st == APPROACH:
786
+ sd = 1 if dx >= 0 else 0
787
+ if s.owner[sd] != 0xFF and s.owner[sd] != i: sd ^= 1
788
+ if s.owner[sd] == 0xFF or s.owner[sd] == i:
789
+ s.owner[sd] = i; s.side[i] = 2 * sd - 1
790
+ tx = s.ppx + s.side[i] * SIDE_DX; rt = 0
791
+ if abs(rel) < 8 * 16 and abs(px - tx) < 6:
792
+ st = ALONG; s.tm[i] = T_ALONG
793
+ else: # both sides taken: queue behind
794
+ tx = px; rt = -BACK_DY * 16
795
+ elif st == ALONG:
796
+ tx = s.ppx + s.side[i] * SIDE_DX; rt = 0
797
+ s.tm[i] -= 1
798
+ if s.tm[i] == 0:
799
+ st = RAM; s.tm[i] = T_RAM; s.rams += 1
800
+ elif st == RAM:
801
+ tx = s.ppx + s.pvx * LEAD; rt = 0
802
+ s.tm[i] -= 1
803
+ if abs(dx) < 14 and abs(rel) < 16 * 16:
804
+ s.contacts += 1; st = BACK; s.tm[i] = T_BACK
805
+ elif s.tm[i] == 0:
806
+ st = BACK; s.tm[i] = T_BACK
807
+ if st == BACK: release(s, i)
808
+ elif st == BACK:
809
+ tx = s.ppx + s.side[i] * BACK_DX; rt = -BACK_DY * 16
810
+ s.tm[i] -= 1
811
+ if s.tm[i] == 0: st = APPROACH
812
+ else:
813
+ tx = px; rt = -200 * 16
814
+ # --- spacing: steer and hold back from the first pursuer too close ---
815
+ for j in range(NCAR):
816
+ if j == i or s.st[j] == 0: continue
817
+ ddx = (s.x[j] >> 8) - px
818
+ ddy = s16(s.y[j] - s.y[i])
819
+ if abs(ddx) < SEP_X and abs(ddy) < SEP_Y * 16:
820
+ tx = px - SEP_X if ddx > 0 or (ddx == 0 and i < j) else px + SEP_X
821
+ if ddy > 0: rt = rel + ddy - SEP_Y * 16
822
+ s.seps += 1
823
+ break
824
+ # --- edge avoidance: probe the row LOOK pixels ahead ---
825
+ vmax, acc = VMAX[st], ACC[st]
826
+ ahead = (s.y[i] + (CH - 1 + LOOK) * 16) & 0xFFFF
827
+ qx = clamp(px + sm(s.vx[i], 4), 0, 255 - CW) # where the car will be
828
+ if blocked(ahead, min(px, qx), max(px, qx) + CW - 1):
829
+ row = (ahead >> 7) & 63; c = (qx + 6) >> 3
830
+ tx = px - 32 if room(row, c, -1) > room(row, c, 1) else px + 32
831
+ vmax, acc = VAVOID, AAVOID; s.avoids += 1
832
+ else: # side probe: next row, toward tx
833
+ near = (s.y[i] + (CH - 1 + 8) * 16) & 0xFFFF
834
+ if tx > px: sx = px + CW - 1 + MARGIN
835
+ elif tx < px: sx = px - MARGIN
836
+ else: sx = px
837
+ if 0 <= sx <= 255 and (tile(near, sx) != ROAD or tile(s.y[i], sx) != ROAD):
838
+ tx = px; acc = AAVOID; s.holds += 1
839
+ tx = clamp(tx, 8, 236)
840
+ # --- lateral: proportional with a speed cap and an acceleration cap ---
841
+ want = clamp((tx - px) * 16, -vmax, vmax)
842
+ v = s.vx[i]
843
+ if want > v + acc: v += acc
844
+ elif want < v - acc: v -= acc
845
+ else: v = want
846
+ s.vx[i] = v; s.x[i] = (s.x[i] + v) & 0xFFFF
847
+ # --- forward: relative speed toward the wanted offset ---
848
+ cap = RSLEAVE if st == LEAVE else RSMAX
849
+ want = clamp(sm(rt - rel, 3), -cap, cap)
850
+ r = s.rs[i]
851
+ if want > r + FACC: r += FACC
852
+ elif want < r - FACC: r -= FACC
853
+ else: r = want
854
+ s.rs[i] = r; s.y[i] = (s.y[i] + PSPD + r) & 0xFFFF
855
+ if st == LEAVE and s16(s.y[i] - s.P) < -150 * 16:
856
+ st = 0; s.freed += 1
857
+ s.st[i] = st
858
+ if st:
859
+ px = s.x[i] >> 8
860
+ top = (s.y[i] + (CH - 1) * 16) & 0xFFFF
861
+ if blocked(s.y[i], px, px + CW - 1) or blocked(top, px, px + CW - 1):
862
+ s.offroad += 1
863
+
864
+ def run(trace=False):
865
+ s = init(); ck = 0; cks = []; hist = set()
866
+ for f in range(1, FRAMES + 1):
867
+ player_step(s, f)
868
+ spawn(s, f)
869
+ for i in range(NCAR): car_step(s, i, f)
870
+ for i in range(NCAR):
871
+ ck = (ck * 33 + s.st[i]) & 0xFFFF
872
+ ck = (ck * 33 + (s.x[i] >> 8)) & 0xFFFF
873
+ hist.add((i, s.st[i]))
874
+ if f % CKSTEP == 0: cks.append(ck)
875
+ if trace and f % 10 == 0:
876
+ print(f, s.ppx, s.pvx, [(s.st[i], s.x[i] >> 8, s16(s.y[i] - s.P) // 16) for i in range(NCAR)])
877
+ return s, cks, hist
878
+
879
+ if __name__ == '__main__':
880
+ import sys
881
+ s, cks, hist = run('-t' in sys.argv)
882
+ print('exp_ck', ', '.join('0x%04x' % c for c in cks))
883
+ print('contacts', s.contacts, 'rams', s.rams, 'avoids', s.avoids,
884
+ 'leaves', s.leaves, 'freed', s.freed, 'offroad', s.offroad, 'holds', s.holds, 'seps', s.seps)
885
+ print('end', s.ppx, [(s.st[i], s.x[i] >> 8, s16(s.y[i] - s.P)) for i in range(NCAR)])
886
+ print('states seen', sorted(hist))
887
+ ```
888
+
889
+ ## Expected output
890
+
891
+ A grey road on green verges, with the HUD in white on the grey. Five
892
+ 12 x 16 outline sprites: the player in yellow, cars 0 to 3 in red,
893
+ purple, cyan and orange. The border is green: (98, 213, 50) on PAL,
894
+ (114, 189, 103) on NTSC.
895
+
896
+ HUD at 45,000,000 cycles (measured in VICE x64sc 3.10, decoded against
897
+ the character ROM), identical on PAL and NTSC:
898
+
899
+ | Row | Text |
900
+ |---|---|
901
+ | 0 | `F 0780 CK BA59 OK 10 R 10 LV 1` |
902
+ | 1 | `CON 03 RAM 07 AVD 060 HLD 318 SEP 865` |
903
+ | 23 | `CAR MEAN 1981 WST 3162 BLT 3361 PRB 1649` |
904
+ | 24 | `FRAME WST 09871 BLT 14204 TMR 05` |
905
+
906
+ `F` is the frame, `CK` the final checksum, `OK 10` the checkpoints that
907
+ matched, `R 10` result code 1 and fault 0, `LV 1` the cars that left when
908
+ shot. Row 1 is pass 1's event counts, each equal to the model's: `CON`
909
+ rams that made contact, `RAM` rams started, `AVD` car-frames steering
910
+ away from a blocked probe, `HLD` car-frames held by the side probe, `SEP`
911
+ car-frames steered by the spacing box. Rows 23 and 24 are CIA1 cycles:
912
+ `CAR MEAN` the mean of pass 2's live car updates, `WST` the worst one,
913
+ `BLT` one car on the built worst path (in contact with the player) with
914
+ three others to scan, `PRB` the edge probes alone (a 52-pixel span of
915
+ eight probes and both 8-tile room scans); `FRAME WST` pass 2's worst
916
+ frame (spawn and four car updates), `BLT` the built worst frame (the
917
+ table's two frame-8 spawns, then four cars on the built path, car 0 in
918
+ contact), `TMR` the empty timer pair. Fault
919
+ 1 is a checkpoint mismatch, 2 an event count that is not the model's, 3
920
+ pass 2 ending in another state than pass 1.
921
+
922
+ The timed figures are the same on both models because the display and
923
+ sprites are off while they run: no badlines, no sprite DMA. With the
924
+ display on, pass 1 does not keep one step per video frame. The frame
925
+ counter reads `0433` at 20,000,000 cycles and `0687` at 30,000,000 on
926
+ PAL: 254 steps in 10,000,000 cycles, 39,370 cycles a step, 2.00 PAL
927
+ frames. NTSC reads `0197` at 10,000,000 and `0490` at 20,000,000: 293
928
+ steps, 34,130 cycles a step, 2.00 NTSC frames (VICE x64sc 3.10). The
929
+ step, the redraw of 21 text rows and the sprite placement overrun one
930
+ frame, so each step waits for the next one. On screen the road
931
+ therefore moves 1 pixel a video frame, not 2. This is the AI plus a
932
+ naive redraw not fitting a frame with badlines and sprite DMA.
933
+
934
+ What the run exercises, from the model: seven rams, at frames 98, 144,
935
+ 248, 414, 558, 667 and 770; three of them make contact (cars 0, 2 and 1,
936
+ at frames 109, 263 and 572). Car 0, spawned behind on the left, is the
937
+ first to meet an edge: at frame 66 the road starts to narrow from the
938
+ left under its look-ahead, and it steers right. Every car avoids at
939
+ least once (28, 15, 6 and 11 car-frames). Car 3 is shot at frame 330,
940
+ drops back and frees its slot at frame 392; the table respawns slot 3
941
+ behind the player at frame 420. No car-frame has a car off the road.
942
+
943
+ At the island the pursuers usually end in the other channel from the
944
+ player. Counting car-frames with the player's and a pursuer's rear rows
945
+ both beside the island, 375 of 452 have the pursuer across the water.
946
+ Cars 1 and 2 are on the player's right when the autopilot dives into the
947
+ left channel, 75 pixels or more from it. The technique page's "Why it
948
+ works" gives the cause and the fixes that were tried in the model.
949
+
950
+ Screenshots at 45,000,000 cycles, both after the verdict and each
951
+ identical over two runs (PAL is still in pass 2 at 40,000,000 and shows the verdict at 42,000,000):
952
+ `screenshots/lane-pursuit.png` (PAL) and
953
+ `screenshots/lane-pursuit-ntsc.png` (NTSC). Measured on both with PIL:
954
+ each sprite is a 12 x 16 outline of 52 pixels at the position the model
955
+ ends on (the player at x 118, car 3 in RAM at 132 one pixel ahead of it,
956
+ cars 0 to 2 approaching 38 to 40 pixels behind at 185, 164 and 143); the
957
+ 140 pixels inside every outline are road grey; and the centre pixel of
958
+ each of the 672 playfield tiles has the colour of the model's map row
959
+ (no water is in view at the end). The script, with the model saved as
960
+ `lane_model.py`:
961
+
962
+ ```python
963
+ # Measures the exit screenshots of lane-pursuit.prg against lane_model.py.
964
+ from PIL import Image
965
+ import lane_model as m
966
+ PAL = {'row': 16, 'road': (98, 98, 98), 'verge': (98, 213, 50),
967
+ 'cars': [(255, 255, 70), (175, 60, 88), (170, 64, 245), (126, 243, 214), (183, 99, 30)]}
968
+ NTSC = {'row': 28, 'road': (98, 98, 98), 'verge': (114, 189, 103),
969
+ 'cars': [(255, 248, 141), (169, 71, 100), (154, 88, 185), (138, 230, 203), (196, 98, 65)]}
970
+ s, _, _ = m.run()
971
+ base = s.P & 0xFF80
972
+ def spot(wy, px): # screenshot x, first row of a sprite
973
+ d = ((wy - base + 0x4000) & 0xFFFF) // 16 - 0x400
974
+ return px + 64, 42 + 8 * 14 - d + 1
975
+ want = [spot(s.P, s.ppx)] + [spot(s.y[i], s.x[i] >> 8) for i in range(4)]
976
+ def check(png, g):
977
+ im = Image.open(png).convert('RGB'); px = im.load(); W, H = im.size
978
+ for k, c in enumerate(g['cars']):
979
+ pts = [(x, y) for y in range(H) for x in range(W) if px[x, y] == c]
980
+ x0, y0 = min(p[0] for p in pts), min(p[1] for p in pts)
981
+ x1, y1 = max(p[0] for p in pts), max(p[1] for p in pts)
982
+ wx, wy = want[k]
983
+ assert (x0, y0 + g['row']) == (wx, wy), (png, k, x0, y0, want[k])
984
+ assert (x1 - x0, y1 - y0, len(pts)) == (11, 15, 52), (png, k)
985
+ inner = {px[x, y] for x in range(x0 + 1, x1) for y in range(y0 + 1, y1)}
986
+ assert inner == {g['road']}, (png, k, inner)
987
+ # road rows 2..22: the centre pixel of every tile against the model map
988
+ prow = (s.P >> 7) & 63; bad = n = 0
989
+ for r in range(2, 23):
990
+ for c in range(32):
991
+ p = px[64 + 8 * c + 3, 51 + 8 * r + 3 - g['row']]
992
+ if p in g['cars']: continue
993
+ t = m.MAP[(prow + 14 - r) & 63][c]
994
+ n += 1
995
+ bad += p != (g['road'] if t == m.ROAD else g['verge'])
996
+ print(png, 'tiles sampled', n)
997
+ return bad
998
+ for png, g in (('pal1.png', PAL), ('ntsc1.png', NTSC)):
999
+ print(png, 'tile mismatches', check(png, g))
1000
+ print('model end: player', s.ppx, 'cars', [(s.st[i], s.x[i] >> 8) for i in range(4)])
1001
+ ```
1002
+
1003
+ ## Why this works
1004
+
1005
+ Every quantity the controllers touch is an integer: lateral position in
1006
+ 8.8, lateral speed in 8.8, world y and forward speed in 1/16 pixel. The
1007
+ one rounding the rules need, `sm`, shifts the magnitude and restores the
1008
+ sign, so C and Python agree on negative values without relying on how
1009
+ either shifts a negative number. That is why the model can be exact and
1010
+ the checksum can hold for 780 frames of four interacting cars.
1011
+
1012
+ The swept probe is what keeps a car off the island. A car probes the row
1013
+ 32 pixels past its nose, not at the x it has now but across the span
1014
+ from now to where its lateral speed puts it 16 frames later, one probe
1015
+ every 8 pixels so no tile between two probes is skipped. An early draft
1016
+ of the model, which probed at the car's current x only, 24 pixels ahead
1017
+ and with no side probe, counted 226 off-road car-frames: ramming cars
1018
+ slid onto the island. A later draft that measured the look-ahead from
1019
+ the car's rear rather than its nose counted 10. The side probe covers the other
1020
+ case: a target beside the player that lies on the verge or the water.
1021
+ It holds the lateral target rather than steering away, so a car beside
1022
+ a narrowing edge waits instead of weaving.
1023
+
1024
+ What the listing leaves out: contact between cars and between a car and
1025
+ the player is a count, not a push (`car_contact_response` is the page
1026
+ for that); there is no shooting, only a scripted shot; the scroll is
1027
+ coarse, one text row every four steps (eight video frames in pass 1). The pursuit code does not change
1028
+ when those are added.