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,867 @@
1
+ ---
2
+ recipe: ghost-targeting
3
+ toolchain: oscar64
4
+ output_format: PRG
5
+ region: both
6
+ techniques: [ghost_target_tile_ai]
7
+ file_formats: [PRG]
8
+ uses_registers: [D000, D001, D010, D011, D015, D020, D021, D027, D028, D029, D02A, D02B, DC06, DC07, DC0F]
9
+ uses_kernal: []
10
+ ---
11
+
12
+ <!-- doc-type: recipe -->
13
+
14
+ # Oscar64 Ghost Targeting: maze-chase ghosts steered by target tiles, checked against a model
15
+
16
+ ## Synopsis
17
+
18
+ Four ghosts (sprites) chase a Pac-Man stand-in through an original
19
+ 27 x 22 character maze by the arcade's target-tile rule: at each
20
+ junction, one tile ahead, a ghost takes the non-reversing exit whose
21
+ next tile is nearest its target, ties going up, left, down, right. Each
22
+ ghost has its own chase target, fixed scatter corners outside the maze,
23
+ and a PRNG in frightened mode. A mode timer runs the level-1
24
+ scatter/chase table with each arcade second shortened to 16 frames, and
25
+ one frightened spell starts at frame 400. The stand-in runs a scripted
26
+ loop, so no joystick is needed, and faces up on one leg of it, so the
27
+ up-direction bug that the listing reproduces is exercised. Pass 1 runs
28
+ 1,600 frames on screen and folds the four ghost tiles into a checksum
29
+ every frame; every 100 frames the checksum is compared with a Python
30
+ model of the same rules, compiled in. Then, with the display off, a
31
+ bench times one decision and two built worst frames, and pass 2 reruns
32
+ the 1,600 frames under the CIA1 timer. `$02FF` = `01` and a green
33
+ border on pass, `02` and red on fail. This is `ghost_target_tile_ai` in
34
+ `docs/techniques/logic.md`; the result-byte contract is
35
+ `headless-verify.md`.
36
+
37
+ ## Source
38
+
39
+ ```c
40
+ // ghost-targeting.c
41
+ //
42
+ // Maze-chase ghost AI by target tiles. Four ghosts and a Pac-Man stand-in
43
+ // move one tile every 4 frames through a character maze. On entering a
44
+ // tile a ghost looks one tile ahead along its heading and picks the exit
45
+ // it will take there: of the exits that do not reverse, the one whose next
46
+ // tile is nearest its target tile by squared straight-line distance, ties
47
+ // going up, left, down, right. A tile with one such exit costs no distance
48
+ // sums. Chase targets: Blinky the stand-in's tile; Pinky 4 tiles ahead of
49
+ // it; Inky the tile 2 ahead, doubled from Blinky; Clyde the stand-in's
50
+ // tile when more than 8 tiles away, else his corner. Scatter targets are
51
+ // fixed tiles outside the maze. A mode timer runs the level-1 scatter and
52
+ // chase table with each arcade second shortened to 16 frames; every
53
+ // scatter/chase switch and the start of frightened mode reverse every
54
+ // ghost; frightened ghosts try a pseudo-random exit first and the timer
55
+ // pauses. The "up" offset bug of Pinky and Inky is reproduced (UP_BUG).
56
+ // Pass 1 runs 1600 frames on screen: each frame the four ghost tiles are
57
+ // folded into a checksum, compared every 100 frames with the Python
58
+ // model's value compiled in below. Then, with the display and sprites off,
59
+ // a bench times single decisions and two built worst frames, and pass 2
60
+ // reruns the 1600 frames back to back under the CIA1 timer.
61
+ // Row 0: frame, phase, decisions, empty timer pair, worst frame with
62
+ // frightened. Row 1: CIA1 cycles for one 3-way decision, one 1-exit tile,
63
+ // the built worst frame, the worst frame of the run. Row 2: checksum,
64
+ // checkpoints matched, result. $02FF = 01 and a green border on pass, 02
65
+ // and red on fail.
66
+ // Build: oscar64 -tm=c64 -O2 -o=ghost-targeting.prg ghost-targeting.c
67
+ #include <c64/vic.h>
68
+ #include <c64/cia.h>
69
+ #include <string.h>
70
+
71
+ #define SCREEN ((char *)0x0400)
72
+ #define COLOUR ((char *)0xd800)
73
+ #define SPRPTR ((char *)0x07f8)
74
+ #define SPRDATA ((char *)0x0340) // blocks 13 and 14, cassette buffer
75
+ #define RESULT (*(volatile char *)0x02ff)
76
+ #define CODE_PASS 0x01
77
+ #define CODE_FAIL 0x02
78
+
79
+ // The maze: '#' wall, '.' path. No dead ends, so a ghost that may not
80
+ // reverse always has an exit. Tile (x, y) is screen column x + 1, row y + 3.
81
+ #define MW 27
82
+ #define MH 22
83
+ #define COL0 1
84
+ #define ROW0 3
85
+ const char maze[MH][MW + 1] = {
86
+ "###########################",
87
+ "#............#............#",
88
+ "#.####.#####.#.#####.####.#",
89
+ "#.####.#####.#.#####.####.#",
90
+ "#.........................#",
91
+ "#.####.#.#########.#.####.#",
92
+ "#......#.....#.....#......#",
93
+ "######.#####.#.#####.######",
94
+ "######.#...........#.######",
95
+ "######.#.#########.#.######",
96
+ "######...#########...######",
97
+ "######.#.#########.#.######",
98
+ "######.#...........#.######",
99
+ "######.#.#########.#.######",
100
+ "#............#............#",
101
+ "#.####.#####.#.#####.####.#",
102
+ "#...##...............##...#",
103
+ "###.##.#.#########.#.##.###",
104
+ "#......#.....#.....#......#",
105
+ "#.##########.#.##########.#",
106
+ "#.........................#",
107
+ "###########################",
108
+ };
109
+
110
+ // Directions in tie order: the lowest number wins a tie.
111
+ #define UP 0
112
+ #define LEFT 1
113
+ #define DOWN 2
114
+ #define RIGHT 3
115
+ const signed char dx[4] = { 0, -1, 0, 1 };
116
+ const signed char dy[4] = { -1, 0, 1, 0 };
117
+ const char bit[4] = { 1, 2, 4, 8 };
118
+ // Lowest set bit of a 4-bit exit mask, as a direction.
119
+ const char first_dir[16] = { 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 };
120
+
121
+ char exits[MH][32]; // bit d set: the tile in direction d is open;
122
+ // a row stride of 32 makes the index a shift
123
+ unsigned sq[64]; // sq[n] = n * n; |d| < 58 in this maze
124
+
125
+ #define UP_BUG 1 // 1 reproduces the arcade's up offset bug
126
+ #define SEC 16 // frames per arcade second
127
+ #define NPHASE 7 // phase 7 is chase for good
128
+ const unsigned phase_len[NPHASE] = {
129
+ 7 * SEC, 20 * SEC, 7 * SEC, 20 * SEC, 5 * SEC, 20 * SEC, 5 * SEC
130
+ };
131
+ #define ENERGIZER_FRAME 400
132
+ #define FRIGHT_LEN (6 * SEC)
133
+ #define STEP 4
134
+ #define FRAMES 1600
135
+
136
+ // Ghost 0 Blinky, 1 Pinky, 2 Inky, 3 Clyde.
137
+ const signed char corner_x[4] = { 24, 2, 26, 0 };
138
+ const signed char corner_y[4] = { -3, -3, 24, 24 };
139
+ const signed char start_x[4] = { 13, 13, 1, 25 };
140
+ const signed char start_y[4] = { 8, 12, 20, 20 };
141
+ const char start_d[4] = { LEFT, RIGHT, RIGHT, LEFT };
142
+
143
+ // The stand-in's loop: straight runs between these tiles.
144
+ #define NWP 8
145
+ const signed char wp_x[NWP] = { 6, 6, 12, 12, 25, 25, 20, 20 };
146
+ const signed char wp_y[NWP] = { 16, 1, 1, 4, 4, 6, 6, 16 };
147
+
148
+ // From ghost_model.py: the checksum after frames 100, 200, ... 1600.
149
+ #define NCK 16
150
+ const unsigned exp_ck[NCK] = {
151
+ 0x5870, 0x4080, 0xdece, 0x11aa, 0xf808, 0x2f38, 0xc4d2, 0xf482,
152
+ 0xa896, 0x57fc, 0x9cce, 0x6be2, 0x03cc, 0x9a8c, 0x7050, 0xe89a
153
+ };
154
+ #define EXP_DECISIONS 382
155
+
156
+ signed char gx[4], gy[4];
157
+ char gcur[4], gplan[4], gcame[4]; // heading, exit planned at the next tile, last move
158
+ signed char tx[4], ty[4]; // target tiles
159
+ signed char px, py;
160
+ char pdir, pwk;
161
+ char phase, fright;
162
+ unsigned mode_t, fright_left, rng = 0xace1;
163
+ unsigned decisions;
164
+
165
+ static inline unsigned sqd(signed char v)
166
+ {
167
+ if (v < 0)
168
+ v = -v;
169
+ return sq[v];
170
+ }
171
+
172
+ static inline char chasing(void)
173
+ {
174
+ return (phase & 1) || phase == NPHASE;
175
+ }
176
+
177
+ unsigned rand16(void)
178
+ {
179
+ rng ^= rng << 7;
180
+ rng ^= rng >> 9;
181
+ rng ^= rng << 8;
182
+ return rng;
183
+ }
184
+
185
+ void set_target(char g)
186
+ {
187
+ if (!chasing()) {
188
+ tx[g] = corner_x[g];
189
+ ty[g] = corner_y[g];
190
+ return;
191
+ }
192
+ signed char ax, ay;
193
+ switch (g) {
194
+ case 0:
195
+ tx[0] = px; ty[0] = py;
196
+ break;
197
+ case 1:
198
+ case 2: {
199
+ signed char n = g == 1 ? 4 : 2;
200
+ ax = px + n * dx[pdir];
201
+ ay = py + n * dy[pdir];
202
+ if (UP_BUG && pdir == UP)
203
+ ax -= n;
204
+ if (g == 1) {
205
+ tx[1] = ax; ty[1] = ay;
206
+ } else {
207
+ tx[2] = 2 * ax - gx[0];
208
+ ty[2] = 2 * ay - gy[0];
209
+ }
210
+ break;
211
+ }
212
+ default:
213
+ if (sqd(gx[3] - px) + sqd(gy[3] - py) > 64) {
214
+ tx[3] = px; ty[3] = py;
215
+ } else {
216
+ tx[3] = corner_x[3]; ty[3] = corner_y[3];
217
+ }
218
+ }
219
+ }
220
+
221
+ // The exit ghost g takes at tile (x, y), arriving with heading d.
222
+ char choose(signed char x, signed char y, char d, char g)
223
+ {
224
+ char m = exits[y][x] & ~bit[d ^ 2];
225
+ if (!(m & (m - 1))) // one exit: no decision
226
+ return first_dir[m];
227
+ decisions++;
228
+ if (fright) {
229
+ char r = rand16() & 3;
230
+ return (m & bit[r]) ? r : first_dir[m];
231
+ }
232
+ signed char ex = x - tx[g], ey = y - ty[g];
233
+ unsigned best = 0xffff;
234
+ char bd = 0;
235
+ for (char k = 0; k < 4; k++)
236
+ if (m & bit[k]) {
237
+ unsigned dd = sqd(ex + dx[k]) + sqd(ey + dy[k]);
238
+ if (dd < best) {
239
+ best = dd;
240
+ bd = k;
241
+ }
242
+ }
243
+ return bd;
244
+ }
245
+
246
+ void reverse_all(void)
247
+ {
248
+ for (char g = 0; g < 4; g++) {
249
+ char d = gcame[g] ^ 2; // back the way it came
250
+ gcur[g] = d;
251
+ set_target(g);
252
+ gplan[g] = choose(gx[g] + dx[d], gy[g] + dy[d], d, g);
253
+ }
254
+ }
255
+
256
+ void ghost_step(char g)
257
+ {
258
+ char d = gcur[g];
259
+ gx[g] += dx[d];
260
+ gy[g] += dy[d];
261
+ gcame[g] = d;
262
+ d = gplan[g];
263
+ gcur[g] = d;
264
+ set_target(g);
265
+ gplan[g] = choose(gx[g] + dx[d], gy[g] + dy[d], d, g);
266
+ }
267
+
268
+ char toward(char k)
269
+ {
270
+ if (wp_x[k] > px) return RIGHT;
271
+ if (wp_x[k] < px) return LEFT;
272
+ if (wp_y[k] > py) return DOWN;
273
+ return UP;
274
+ }
275
+
276
+ void pac_step(void)
277
+ {
278
+ px += dx[pdir];
279
+ py += dy[pdir];
280
+ if (px == wp_x[pwk] && py == wp_y[pwk]) {
281
+ pwk++;
282
+ if (pwk == NWP)
283
+ pwk = 0;
284
+ pdir = toward(pwk);
285
+ }
286
+ }
287
+
288
+ // The technique's work for one frame, in two parts: the mode timer, then
289
+ // on a step frame every ghost's move, target and look-ahead decision. The
290
+ // stand-in moves between the two.
291
+ void mode_tick(unsigned f)
292
+ {
293
+ if (fright) {
294
+ if (--fright_left == 0)
295
+ fright = 0; // no reversal leaving frightened
296
+ } else if (phase < NPHASE) {
297
+ if (++mode_t == phase_len[phase]) {
298
+ phase++;
299
+ mode_t = 0;
300
+ reverse_all();
301
+ }
302
+ }
303
+ if (f == ENERGIZER_FRAME) {
304
+ fright = 1;
305
+ fright_left = FRIGHT_LEN;
306
+ reverse_all();
307
+ }
308
+ }
309
+
310
+ void init_actors(void)
311
+ {
312
+ phase = 0; mode_t = 0; fright = 0; rng = 0xace1;
313
+ px = wp_x[0]; py = wp_y[0]; pwk = 1;
314
+ pdir = toward(1);
315
+ for (char g = 0; g < 4; g++) {
316
+ gx[g] = start_x[g];
317
+ gy[g] = start_y[g];
318
+ gcur[g] = gcame[g] = start_d[g];
319
+ }
320
+ for (char g = 0; g < 4; g++) {
321
+ set_target(g);
322
+ char d = gcur[g];
323
+ gplan[g] = choose(gx[g] + dx[d], gy[g] + dy[d], d, g);
324
+ }
325
+ decisions = 0;
326
+ }
327
+
328
+ // --- CIA1 timer B ----------------------------------------------------------
329
+ static inline void timer_start(void)
330
+ {
331
+ cia1.crb = 0x00;
332
+ cia1.tb = 0xffff;
333
+ cia1.crb = 0x11;
334
+ }
335
+
336
+ static inline unsigned timer_stop(void)
337
+ {
338
+ cia1.crb = 0x00;
339
+ return 0xffff - cia1.tb;
340
+ }
341
+
342
+ // A built worst frame: scatter ends this frame, so every ghost reverses
343
+ // and makes a 3-way decision at the 4-way junction (6, 14) it just left;
344
+ // then it steps, and its look-ahead lands on the same junction heading up,
345
+ // another 3-way decision, with chase targets (the stand-in faces up, the
346
+ // bug path). Given f = ENERGIZER_FRAME, frightened mode starts in the same
347
+ // frame: a second reversal, and the step's decisions go to the PRNG. The
348
+ // state set-up between the two timed parts is outside the timer.
349
+ unsigned worst_frame(unsigned f)
350
+ {
351
+ init_actors();
352
+ phase = 0;
353
+ mode_t = phase_len[0] - 1;
354
+ px = 13; py = 16; pdir = UP;
355
+ for (char g = 0; g < 4; g++) {
356
+ gx[g] = 6; gy[g] = 13; gcame[g] = UP;
357
+ }
358
+ timer_start();
359
+ mode_tick(f);
360
+ unsigned t = timer_stop();
361
+ for (char g = 0; g < 4; g++) {
362
+ gx[g] = 6; gy[g] = 16; gcur[g] = UP; gplan[g] = UP;
363
+ }
364
+ timer_start();
365
+ for (char g = 0; g < 4; g++)
366
+ ghost_step(g);
367
+ return t + timer_stop();
368
+ }
369
+
370
+ // --- screen ------------------------------------------------------------------
371
+ void put_str(char *p, const char *s)
372
+ {
373
+ while (*s)
374
+ *p++ = *s++;
375
+ }
376
+
377
+ void put_dec(char *p, unsigned v, char digits)
378
+ {
379
+ for (char i = digits; i > 0; i--) {
380
+ p[i - 1] = 0x30 + (char)(v % 10);
381
+ v /= 10;
382
+ }
383
+ }
384
+
385
+ void put_hex(char *p, unsigned v)
386
+ {
387
+ for (char i = 4; i > 0; i--) {
388
+ char n = v & 15;
389
+ p[i - 1] = n < 10 ? 0x30 + n : n - 9;
390
+ v >>= 4;
391
+ }
392
+ }
393
+
394
+ // Ghost g is a 4 x 4 block in quadrant g of its tile, so no two ghosts
395
+ // hide each other; the stand-in is an 8 x 8 ring under them.
396
+ void place_sprites(void)
397
+ {
398
+ for (char g = 0; g < 4; g++) {
399
+ vic.spr_pos[g].x = 24 + 8 * (COL0 + gx[g]) + 4 * (g & 1);
400
+ vic.spr_pos[g].y = 50 + 8 * (ROW0 + gy[g]) + 4 * (g >> 1);
401
+ }
402
+ vic.spr_pos[4].x = 24 + 8 * (COL0 + px);
403
+ vic.spr_pos[4].y = 50 + 8 * (ROW0 + py);
404
+ }
405
+
406
+ int main(void)
407
+ {
408
+ __asm { sei }
409
+ vic.color_border = VCOL_BLACK;
410
+ vic.color_back = VCOL_BLACK;
411
+ memset(SCREEN, 0x20, 1000);
412
+ memset(COLOUR, VCOL_LT_GREY, 1000);
413
+
414
+ for (char i = 0; i < 64; i++)
415
+ sq[i] = (unsigned)i * i;
416
+ for (char y = 0; y < MH; y++)
417
+ for (char x = 0; x < MW; x++) {
418
+ unsigned o = (y + ROW0) * 40 + x + COL0;
419
+ char e = 0;
420
+ if (maze[y][x] == '#') {
421
+ SCREEN[o] = 0xa0;
422
+ COLOUR[o] = VCOL_BLUE;
423
+ } else {
424
+ SCREEN[o] = 0x2e;
425
+ COLOUR[o] = VCOL_DARK_GREY;
426
+ for (char k = 0; k < 4; k++) {
427
+ signed char nx = x + dx[k], ny = y + dy[k];
428
+ if (nx >= 0 && nx < MW && ny >= 0 && ny < MH && maze[ny][nx] != '#')
429
+ e |= bit[k];
430
+ }
431
+ }
432
+ exits[y][x] = e;
433
+ }
434
+
435
+ // Sprite shapes: block 13 a 4 x 4 block, block 14 an 8 x 8 ring.
436
+ memset(SPRDATA, 0, 128);
437
+ for (char r = 0; r < 4; r++)
438
+ SPRDATA[r * 3] = 0xf0;
439
+ SPRDATA[64] = 0xff;
440
+ SPRDATA[64 + 21] = 0xff;
441
+ for (char r = 1; r < 7; r++)
442
+ SPRDATA[64 + r * 3] = 0x81;
443
+ for (char s = 0; s < 4; s++)
444
+ SPRPTR[s] = 13;
445
+ SPRPTR[4] = 14;
446
+ vic.spr_color[0] = VCOL_RED;
447
+ vic.spr_color[1] = VCOL_LT_RED;
448
+ vic.spr_color[2] = VCOL_CYAN;
449
+ vic.spr_color[3] = VCOL_ORANGE;
450
+ vic.spr_color[4] = VCOL_YELLOW;
451
+ vic.spr_msbx = 0;
452
+ vic.spr_enable = 0x1f;
453
+
454
+ // Pass 1, on screen: one frame per frame, the ghost tiles folded into
455
+ // the checksum every frame and checked every 100 frames.
456
+ init_actors();
457
+ place_sprites();
458
+ put_str(SCREEN, s"f ph dec tmr bnf");
459
+ put_str(SCREEN + 40, s"dec one bnd wst");
460
+ put_str(SCREEN + 80, s"ck ok r");
461
+ unsigned ck = 0;
462
+ char ok = 0;
463
+ for (unsigned f = 1; f <= FRAMES; f++) {
464
+ vic_waitFrame();
465
+ mode_tick(f);
466
+ if ((f & (STEP - 1)) == 0) {
467
+ pac_step();
468
+ for (char g = 0; g < 4; g++)
469
+ ghost_step(g);
470
+ }
471
+ place_sprites();
472
+ for (char g = 0; g < 4; g++) { // the per-frame log
473
+ ck = ck * 33 + (char)gx[g];
474
+ ck = ck * 33 + (char)gy[g];
475
+ }
476
+ if (f % 100 == 0 && ck == exp_ck[f / 100 - 1])
477
+ ok++;
478
+ put_dec(SCREEN + 2, f, 4);
479
+ put_dec(SCREEN + 10, phase, 1);
480
+ put_dec(SCREEN + 16, decisions, 4);
481
+ put_hex(SCREEN + 83, ck);
482
+ }
483
+ unsigned dec1 = decisions;
484
+ signed char end_x[4], end_y[4];
485
+ for (char g = 0; g < 4; g++) {
486
+ end_x[g] = gx[g];
487
+ end_y[g] = gy[g];
488
+ }
489
+
490
+ // Display off (no badlines) and sprites off (no sprite DMA) from the
491
+ // next frame, so the CIA counts only the CPU's own cycles.
492
+ vic.spr_enable = 0;
493
+ vic.ctrl1 &= ~VIC_CTRL1_DEN;
494
+ vic_waitFrame();
495
+
496
+ // Bench: one 3-way decision (a 4-way junction entered from below,
497
+ // chase targets) and one 1-exit tile, worst of the four ghosts.
498
+ init_actors();
499
+ phase = 1;
500
+ px = 13; py = 16; pdir = UP;
501
+ unsigned t_empty, t_dec = 0, t_one = 0;
502
+ timer_start();
503
+ t_empty = timer_stop();
504
+ for (char g = 0; g < 4; g++) {
505
+ set_target(g);
506
+ timer_start();
507
+ choose(6, 14, UP, g);
508
+ unsigned t = timer_stop();
509
+ if (t > t_dec) t_dec = t;
510
+ timer_start();
511
+ choose(3, 17, DOWN, g);
512
+ t = timer_stop();
513
+ if (t > t_one) t_one = t;
514
+ }
515
+ unsigned t_bound = worst_frame(1);
516
+ unsigned t_bound_fr = worst_frame(ENERGIZER_FRAME);
517
+
518
+ // Pass 2, timed: the same 1600 frames of logic back to back, the worst
519
+ // frame of the technique's own work (the stand-in's move is outside
520
+ // the timer). Its end tiles must equal pass 1's.
521
+ init_actors();
522
+ unsigned t_worst = 0;
523
+ for (unsigned f = 1; f <= FRAMES; f++) {
524
+ timer_start();
525
+ mode_tick(f);
526
+ unsigned t = timer_stop();
527
+ if ((f & (STEP - 1)) == 0) {
528
+ pac_step();
529
+ timer_start();
530
+ for (char g = 0; g < 4; g++)
531
+ ghost_step(g);
532
+ t += timer_stop();
533
+ }
534
+ if (t > t_worst) t_worst = t;
535
+ }
536
+ char same = decisions == dec1;
537
+ for (char g = 0; g < 4; g++)
538
+ if (gx[g] != end_x[g] || gy[g] != end_y[g])
539
+ same = 0;
540
+
541
+ vic.ctrl1 |= VIC_CTRL1_DEN;
542
+ vic.spr_enable = 0x1f;
543
+ put_dec(SCREEN + 26, t_empty, 2);
544
+ put_dec(SCREEN + 44, t_dec, 4);
545
+ put_dec(SCREEN + 53, t_one, 4);
546
+ put_dec(SCREEN + 62, t_bound, 5);
547
+ put_dec(SCREEN + 72, t_worst, 5);
548
+ put_dec(SCREEN + 33, t_bound_fr, 5);
549
+
550
+ char fault = 0;
551
+ if (ok != NCK) fault = 1;
552
+ else if (dec1 != EXP_DECISIONS) fault = 2;
553
+ else if (!same) fault = 3;
554
+ put_dec(SCREEN + 92, ok, 2);
555
+ char code = fault ? CODE_FAIL : CODE_PASS;
556
+ RESULT = code;
557
+ vic.color_border = fault ? VCOL_RED : VCOL_GREEN;
558
+ SCREEN[80 + 16] = 0x30 + code;
559
+ SCREEN[80 + 17] = 0x30 + fault;
560
+ for (;;)
561
+ ;
562
+ return 0;
563
+ }
564
+ ```
565
+
566
+ ## Build
567
+
568
+ ```bash
569
+ oscar64 -tm=c64 -O2 -o=ghost-targeting.prg ghost-targeting.c
570
+ ```
571
+
572
+ Oscar64 1.32.271 builds it with no warnings; the PRG is 4,247 bytes.
573
+
574
+ The checksums, the decision count and the end tiles come from this
575
+ script (Python 3). It restates the rules independently of the C: the
576
+ same maze, targets, tie order, look-ahead, reversals, timer and PRNG.
577
+ It prints the sixteen `exp_ck` values, `decisions 382 phase 7`, and
578
+ the end tiles `ghosts [(19, 16), (17, 14), (20, 9), (9, 18)] pac (14, 16)`.
579
+
580
+ ```python
581
+ # ghost_model.py: the reference for ghost-targeting.c. Same maze and
582
+ # rules, written again in Python; prints the checkpoint checksums, the
583
+ # decision count and the final tiles the listing compiles in.
584
+ MAZE = [
585
+ "###########################",
586
+ "#............#............#",
587
+ "#.####.#####.#.#####.####.#",
588
+ "#.####.#####.#.#####.####.#",
589
+ "#.........................#",
590
+ "#.####.#.#########.#.####.#",
591
+ "#......#.....#.....#......#",
592
+ "######.#####.#.#####.######",
593
+ "######.#...........#.######",
594
+ "######.#.#########.#.######",
595
+ "######...#########...######",
596
+ "######.#.#########.#.######",
597
+ "######.#...........#.######",
598
+ "######.#.#########.#.######",
599
+ "#............#............#",
600
+ "#.####.#####.#.#####.####.#",
601
+ "#...##...............##...#",
602
+ "###.##.#.#########.#.##.###",
603
+ "#......#.....#.....#......#",
604
+ "#.##########.#.##########.#",
605
+ "#.........................#",
606
+ "###########################",
607
+ ]
608
+ MW, MH = 27, 22
609
+ DX, DY = (0, -1, 0, 1), (-1, 0, 1, 0) # up, left, down, right
610
+ UP = 0
611
+ UP_BUG = True
612
+ SEC = 16 # frames per arcade second
613
+ PHASES = [7, 20, 7, 20, 5, 20, 5] # level 1: S C S C S C S, then C
614
+ ENERGIZER_FRAME, FRIGHT_LEN = 400, 6 * SEC
615
+ FRAMES, STEP = 1600, 4
616
+ CORNER = [(24, -3), (2, -3), (26, 24), (0, 24)]
617
+ START = [(13, 8, 1), (13, 12, 3), (1, 20, 3), (25, 20, 1)] # x, y, heading
618
+ WP = [(6, 16), (6, 1), (12, 1), (12, 4), (25, 4), (25, 6), (20, 6), (20, 16)]
619
+
620
+ def open_(x, y):
621
+ return 0 <= x < MW and 0 <= y < MH and MAZE[y][x] != '#'
622
+
623
+ EXITS = [[sum(1 << d for d in range(4) if open_(x + DX[d], y + DY[d]))
624
+ if open_(x, y) else 0 for x in range(MW)] for y in range(MH)]
625
+
626
+ def check_maze():
627
+ for y in range(MH):
628
+ assert len(MAZE[y]) == MW
629
+ for x in range(MW):
630
+ if open_(x, y):
631
+ assert bin(EXITS[y][x]).count('1') >= 2, ('dead end', x, y)
632
+
633
+ class Sim:
634
+ def __init__(s):
635
+ s.rng = 0xACE1
636
+ s.phase, s.mode_t, s.fright, s.fright_left = 0, 0, False, 0
637
+ s.g = [[x, y, d, 0, d] for x, y, d in START] # x, y, cur, plan, came
638
+ s.t = [(0, 0)] * 4
639
+ s.px, s.py = WP[0]; s.wk = 1; s.pdir = s.toward(WP[1])
640
+ s.decisions = 0
641
+ for i in range(4):
642
+ s.target(i)
643
+ x, y, d, _, _ = s.g[i]
644
+ s.g[i][3] = s.choose(x + DX[d], y + DY[d], d, i)
645
+ s.decisions = 0 # the listing counts from here
646
+
647
+ def toward(s, w):
648
+ if w[0] > s.px: return 3
649
+ if w[0] < s.px: return 1
650
+ if w[1] > s.py: return 2
651
+ return 0
652
+
653
+ def chase(s):
654
+ return s.phase & 1 or s.phase == 7
655
+
656
+ def rand(s):
657
+ x = s.rng
658
+ x ^= (x << 7) & 0xFFFF
659
+ x ^= x >> 9
660
+ x ^= (x << 8) & 0xFFFF
661
+ s.rng = x
662
+ return x
663
+
664
+ def target(s, i):
665
+ if not s.chase():
666
+ s.t[i] = CORNER[i]; return
667
+ px, py, d = s.px, s.py, s.pdir
668
+ if i == 0:
669
+ s.t[i] = (px, py)
670
+ elif i in (1, 2):
671
+ n = 4 if i == 1 else 2
672
+ ax, ay = px + n * DX[d], py + n * DY[d]
673
+ if UP_BUG and d == UP:
674
+ ax -= n
675
+ if i == 1:
676
+ s.t[i] = (ax, ay)
677
+ else:
678
+ bx, by = s.g[0][0], s.g[0][1]
679
+ s.t[i] = (2 * ax - bx, 2 * ay - by)
680
+ else:
681
+ cx, cy = s.g[3][0], s.g[3][1]
682
+ s.t[i] = (px, py) if (cx - px) ** 2 + (cy - py) ** 2 > 64 else CORNER[3]
683
+
684
+ def choose(s, x, y, d, i):
685
+ m = EXITS[y][x] & ~(1 << (d ^ 2))
686
+ first = next(k for k in range(4) if m >> k & 1)
687
+ if m & (m - 1) == 0:
688
+ return first
689
+ s.decisions += 1
690
+ if s.fright:
691
+ r = s.rand() & 3
692
+ return r if m >> r & 1 else first
693
+ tx, ty = s.t[i]
694
+ best, bd = None, 0
695
+ for k in range(4):
696
+ if m >> k & 1:
697
+ dd = (x + DX[k] - tx) ** 2 + (y + DY[k] - ty) ** 2
698
+ if best is None or dd < best:
699
+ best, bd = dd, k
700
+ return bd
701
+
702
+ def reverse_all(s):
703
+ for i in range(4):
704
+ g = s.g[i]
705
+ g[2] = g[4] ^ 2 # back the way it came
706
+ s.target(i)
707
+ g[3] = s.choose(g[0] + DX[g[2]], g[1] + DY[g[2]], g[2], i)
708
+
709
+ def ghost_step(s, i):
710
+ g = s.g[i]
711
+ g[0] += DX[g[2]]; g[1] += DY[g[2]]
712
+ assert open_(g[0], g[1])
713
+ g[4] = g[2]
714
+ g[2] = g[3]
715
+ s.target(i)
716
+ g[3] = s.choose(g[0] + DX[g[2]], g[1] + DY[g[2]], g[2], i)
717
+
718
+ def pac_step(s):
719
+ s.px += DX[s.pdir]; s.py += DY[s.pdir]
720
+ assert open_(s.px, s.py)
721
+ if (s.px, s.py) == WP[s.wk]:
722
+ s.wk = (s.wk + 1) % len(WP)
723
+ s.pdir = s.toward(WP[s.wk])
724
+
725
+ def tick(s, f):
726
+ if s.fright:
727
+ s.fright_left -= 1
728
+ if s.fright_left == 0:
729
+ s.fright = False
730
+ elif s.phase < 7:
731
+ s.mode_t += 1
732
+ if s.mode_t == PHASES[s.phase] * SEC:
733
+ s.phase += 1; s.mode_t = 0
734
+ s.reverse_all()
735
+ if f == ENERGIZER_FRAME:
736
+ s.fright, s.fright_left = True, FRIGHT_LEN
737
+ s.reverse_all()
738
+ if f % STEP == 0:
739
+ s.pac_step()
740
+ for i in range(4):
741
+ s.ghost_step(i)
742
+
743
+ def run():
744
+ check_maze()
745
+ s = Sim()
746
+ ck, cks = 0, []
747
+ for f in range(1, FRAMES + 1):
748
+ s.tick(f)
749
+ for g in s.g:
750
+ for b in (g[0], g[1]):
751
+ ck = (ck * 33 + (b & 0xFF)) & 0xFFFF
752
+ if f % 100 == 0:
753
+ cks.append(ck)
754
+ return s, cks
755
+
756
+ if __name__ == '__main__':
757
+ s, cks = run()
758
+ print('exp_ck', ', '.join('0x%04x' % c for c in cks))
759
+ print('decisions', s.decisions, 'phase', s.phase)
760
+ print('ghosts', [(g[0], g[1]) for g in s.g], 'pac', (s.px, s.py))
761
+ ```
762
+
763
+ ## Expected output
764
+
765
+ Black background, a blue maze with grey dots on the paths, and five
766
+ sprites: four 4 x 4 blocks, red (Blinky), light red (Pinky), cyan
767
+ (Inky) and orange (Clyde), each in its own quadrant of its tile so none
768
+ hides another, and a yellow 8 x 8 ring (the stand-in) under them. The
769
+ border is green: (98, 213, 50) on PAL, (114, 189, 103) on NTSC.
770
+
771
+ HUD at 38,000,000 cycles (measured in VICE x64sc 3.10, decoded against
772
+ the character ROM), identical on PAL and NTSC:
773
+
774
+ | Row | Text |
775
+ |---|---|
776
+ | 0 | `F 1600 PH 7 DEC 0382 TMR 05 BNF 07227` |
777
+ | 1 | `DEC 0578 ONE 0089 BND 06943 WST 04171` |
778
+ | 2 | `CK E89A OK 16 R10` |
779
+
780
+ `F` is the frame, `PH 7` the last phase (chase for good), `DEC` on row
781
+ 0 the decisions made in pass 1, equal to the model's 382. `TMR` is the
782
+ empty timer pair. Row 1 is CIA1 cycles: `DEC` one 3-way decision, `ONE`
783
+ a 1-exit tile, `BND` the built worst frame (scatter ends: every ghost
784
+ reverses onto a 4-way junction, then steps onto another 3-way decision)
785
+ and `WST` the worst frame of pass 2. `BNF` on row 0 is the built worst
786
+ frame with frightened mode starting in it as well. `CK` is the final
787
+ checksum, `OK 16` the checkpoints that matched, `R 10` result code 1,
788
+ fault 0. Fault 1 is a checkpoint mismatch, 2 a decision count that is
789
+ not the model's, 3 pass 2 ending on other tiles than pass 1.
790
+
791
+ The timed figures are the same on both models because the display and
792
+ sprites are off while they run: no badlines, no sprite DMA.
793
+
794
+ Screenshots at 38,000,000 cycles, both after the verdict and each
795
+ identical over two runs: `screenshots/ghost-targeting.png` (PAL) and
796
+ `screenshots/ghost-targeting-ntsc.png` (NTSC). Measured on both with
797
+ PIL: each ghost's colour covers exactly one 4 x 4 block, in the
798
+ quadrant of the tile the model ends on, (19, 16), (17, 14), (20, 9) and
799
+ (9, 18), and the whole 28-pixel yellow ring is at (14, 16). A sprite
800
+ at Y = 50 + 8r first shows on raster line 51 + 8r, the top line of text
801
+ row r, so sprites and characters line up. The script, with the model
802
+ saved as `ghost_model.py`:
803
+
804
+ ```python
805
+ from PIL import Image
806
+ import ghost_model
807
+ PAL = {'top': 35, 'ghosts': [(175, 60, 88), (238, 123, 149), (126, 243, 214), (183, 99, 30)],
808
+ 'pac': (255, 255, 70)}
809
+ NTSC = {'top': 23, 'ghosts': [(169, 71, 100), (230, 134, 163), (138, 230, 203), (196, 98, 65)],
810
+ 'pac': (255, 248, 141)}
811
+ def tiles(png, m):
812
+ im = Image.open(png).convert('RGB'); px = im.load()
813
+ def box(c):
814
+ pts = [(x, y) for y in range(im.size[1]) for x in range(im.size[0]) if px[x, y] == c]
815
+ return min(p[0] for p in pts), min(p[1] for p in pts), len(pts)
816
+ y0 = m['top'] + 8 * 3 # tile row 0 is text row 3
817
+ out = []
818
+ for g, c in enumerate(m['ghosts']): # ghost g sits in quadrant g
819
+ x, y, n = box(c)
820
+ assert n == 16 # a whole 4 x 4 block
821
+ out.append(((x - 4 * (g & 1) - 40) // 8, (y - 4 * (g >> 1) - y0) // 8))
822
+ x, y, n = box(m['pac'])
823
+ return out, ((x - 40) // 8, (y - y0) // 8), n
824
+ s, _ = ghost_model.run()
825
+ want = [(g[0], g[1]) for g in s.g], (s.px, s.py)
826
+ for png, m in (('ghost-targeting.png', PAL), ('ghost-targeting-ntsc.png', NTSC)):
827
+ g, p, n = tiles(png, m)
828
+ assert (g, p) == want and n == 28
829
+ ```
830
+
831
+ The run exercises every rule. The model counts 90 chase-mode target
832
+ computations for Pinky and Inky with the stand-in facing up, the bug
833
+ path; Clyde's target takes both values, the stand-in and his corner;
834
+ 23 of the 382 decisions are frightened ones; and 612 of the 1,632
835
+ target computations lie outside the maze (counted by wrapping the model's
836
+ `target` and `choose` in counters; the wrapper is not shown).
837
+
838
+ ## Why this works
839
+
840
+ The exit mask per tile turns the junction test into one AND and one
841
+ test: clear the reverse bit, and if one bit is left, the ghost follows
842
+ it without arithmetic (89 cycles). Only real junctions pay for distance
843
+ sums, one pair of table lookups per candidate, and there are never more
844
+ than three candidates (578 cycles). The squares table has 64 entries
845
+ because no coordinate difference in this maze reaches 64: the farthest
846
+ target, Inky's, is at most 57 tiles from any tile along either axis
847
+ (arithmetic from the maze size and the offsets). A larger maze needs a larger table or a
848
+ clamp.
849
+
850
+ The look-ahead follows the arcade's rule. A
851
+ ghost decides its turn at tile N+1 as it enters tile N, using the
852
+ targets of that moment, and stores it in `gplan`. A forced reversal
853
+ turns the ghost back toward the tile it came from (`gcame`), not
854
+ against its planned turn, which may point into a wall at a corner, and
855
+ plans afresh from there.
856
+
857
+ The mode timer counts frames, which is right only because the whole
858
+ program counts frames: a game that shows the arcade's seconds on both
859
+ models must scale the table on NTSC, as `difficulty_ramp_tables` does.
860
+ The timer does not advance during frightened mode, and leaving
861
+ frightened mode does not reverse the ghosts; both follow the Dossier.
862
+
863
+ What the listing leaves out: the ghost house and its release rules,
864
+ the side tunnels, speeds, the no-up zones, and collisions between ghost
865
+ and stand-in. The ghosts start in the maze already moving. Each is a
866
+ separate addition to the same loop and does not change the choice
867
+ code.