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.
- package/CHANGELOG.md +2082 -0
- package/LICENSE +29 -0
- package/README.md +559 -0
- package/VERSION +25 -0
- package/dist/cli/setup.js +66 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli-memorize-audit.js +97 -0
- package/dist/cli-memorize-audit.js.map +1 -0
- package/dist/cli.js +335 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.js +83 -0
- package/dist/config.js.map +1 -0
- package/dist/context.js +53 -0
- package/dist/context.js.map +1 -0
- package/dist/domain/budget-notes.js +104 -0
- package/dist/domain/budget-notes.js.map +1 -0
- package/dist/domain/budget.js +417 -0
- package/dist/domain/budget.js.map +1 -0
- package/dist/domain/game-design.js +77 -0
- package/dist/domain/game-design.js.map +1 -0
- package/dist/domain/timing.js +39 -0
- package/dist/domain/timing.js.map +1 -0
- package/dist/graph/apply.js +132 -0
- package/dist/graph/apply.js.map +1 -0
- package/dist/graph/claims.js +231 -0
- package/dist/graph/claims.js.map +1 -0
- package/dist/graph/extract/archetype.js +118 -0
- package/dist/graph/extract/archetype.js.map +1 -0
- package/dist/graph/extract/common.js +69 -0
- package/dist/graph/extract/common.js.map +1 -0
- package/dist/graph/extract/failure.js +37 -0
- package/dist/graph/extract/failure.js.map +1 -0
- package/dist/graph/extract/game-design.js +189 -0
- package/dist/graph/extract/game-design.js.map +1 -0
- package/dist/graph/extract/hardware.js +124 -0
- package/dist/graph/extract/hardware.js.map +1 -0
- package/dist/graph/extract/pitfall.js +62 -0
- package/dist/graph/extract/pitfall.js.map +1 -0
- package/dist/graph/extract/raster-band.js +56 -0
- package/dist/graph/extract/raster-band.js.map +1 -0
- package/dist/graph/extract/recipe.js +71 -0
- package/dist/graph/extract/recipe.js.map +1 -0
- package/dist/graph/extract/technique-entities.js +180 -0
- package/dist/graph/extract/technique-entities.js.map +1 -0
- package/dist/graph/extract/technique.js +179 -0
- package/dist/graph/extract/technique.js.map +1 -0
- package/dist/graph/extract/toolchain.js +67 -0
- package/dist/graph/extract/toolchain.js.map +1 -0
- package/dist/graph/extract/types.js +3 -0
- package/dist/graph/extract/types.js.map +1 -0
- package/dist/graph/extract/vocabulary.js +65 -0
- package/dist/graph/extract/vocabulary.js.map +1 -0
- package/dist/graph/extract.js +43 -0
- package/dist/graph/extract.js.map +1 -0
- package/dist/graph/kernal-clobbers.js +64 -0
- package/dist/graph/kernal-clobbers.js.map +1 -0
- package/dist/graph/machine-variants.js +208 -0
- package/dist/graph/machine-variants.js.map +1 -0
- package/dist/ingest/files.js +99 -0
- package/dist/ingest/files.js.map +1 -0
- package/dist/ingest/passes.js +104 -0
- package/dist/ingest/passes.js.map +1 -0
- package/dist/ingest/points.js +50 -0
- package/dist/ingest/points.js.map +1 -0
- package/dist/ingest/report.js +140 -0
- package/dist/ingest/report.js.map +1 -0
- package/dist/ingest/tally.js +81 -0
- package/dist/ingest/tally.js.map +1 -0
- package/dist/ingest/verified-on.js +43 -0
- package/dist/ingest/verified-on.js.map +1 -0
- package/dist/ingest.js +135 -0
- package/dist/ingest.js.map +1 -0
- package/dist/schemas/cost-basis.js +5 -0
- package/dist/schemas/cost-basis.js.map +1 -0
- package/dist/schemas/machine-variant.js +25 -0
- package/dist/schemas/machine-variant.js.map +1 -0
- package/dist/schemas/plan-budget.js +121 -0
- package/dist/schemas/plan-budget.js.map +1 -0
- package/dist/schemas/tool-outputs.js +526 -0
- package/dist/schemas/tool-outputs.js.map +1 -0
- package/dist/server/define-tool.js +52 -0
- package/dist/server/define-tool.js.map +1 -0
- package/dist/server/defined-only.js +9 -0
- package/dist/server/defined-only.js.map +1 -0
- package/dist/server/tools-briefings.js +70 -0
- package/dist/server/tools-briefings.js.map +1 -0
- package/dist/server/tools-lookup.js +180 -0
- package/dist/server/tools-lookup.js.map +1 -0
- package/dist/server/tools-maintenance.js +116 -0
- package/dist/server/tools-maintenance.js.map +1 -0
- package/dist/server/tools-pitfalls.js +89 -0
- package/dist/server/tools-pitfalls.js.map +1 -0
- package/dist/server/tools-recipes.js +296 -0
- package/dist/server/tools-recipes.js.map +1 -0
- package/dist/server/tools-runtime.js +48 -0
- package/dist/server/tools-runtime.js.map +1 -0
- package/dist/server/tools.js +43 -0
- package/dist/server/tools.js.map +1 -0
- package/dist/server.js +95 -0
- package/dist/server.js.map +1 -0
- package/dist/services/analytics/schema.js +78 -0
- package/dist/services/analytics/schema.js.map +1 -0
- package/dist/services/analytics/statements.js +99 -0
- package/dist/services/analytics/statements.js.map +1 -0
- package/dist/services/analytics.js +195 -0
- package/dist/services/analytics.js.map +1 -0
- package/dist/services/bm25.js +158 -0
- package/dist/services/bm25.js.map +1 -0
- package/dist/services/chunker.js +81 -0
- package/dist/services/chunker.js.map +1 -0
- package/dist/services/embeddings.js +58 -0
- package/dist/services/embeddings.js.map +1 -0
- package/dist/services/falkor/base.js +85 -0
- package/dist/services/falkor/base.js.map +1 -0
- package/dist/services/falkor/links.js +361 -0
- package/dist/services/falkor/links.js.map +1 -0
- package/dist/services/falkor/nodes.js +183 -0
- package/dist/services/falkor/nodes.js.map +1 -0
- package/dist/services/falkor/params.js +45 -0
- package/dist/services/falkor/params.js.map +1 -0
- package/dist/services/falkor/schema.js +77 -0
- package/dist/services/falkor/schema.js.map +1 -0
- package/dist/services/falkor.js +100 -0
- package/dist/services/falkor.js.map +1 -0
- package/dist/services/qdrant.js +238 -0
- package/dist/services/qdrant.js.map +1 -0
- package/dist/services/versions.js +73 -0
- package/dist/services/versions.js.map +1 -0
- package/dist/services/vice-bin.js +51 -0
- package/dist/services/vice-bin.js.map +1 -0
- package/dist/tools/briefings/archetype.js +174 -0
- package/dist/tools/briefings/archetype.js.map +1 -0
- package/dist/tools/briefings/budget.js +177 -0
- package/dist/tools/briefings/budget.js.map +1 -0
- package/dist/tools/briefings/build-order.js +135 -0
- package/dist/tools/briefings/build-order.js.map +1 -0
- package/dist/tools/briefings/build.js +185 -0
- package/dist/tools/briefings/build.js.map +1 -0
- package/dist/tools/briefings/discovery.js +290 -0
- package/dist/tools/briefings/discovery.js.map +1 -0
- package/dist/tools/briefings/plan-pitfalls.js +69 -0
- package/dist/tools/briefings/plan-pitfalls.js.map +1 -0
- package/dist/tools/briefings/render.js +151 -0
- package/dist/tools/briefings/render.js.map +1 -0
- package/dist/tools/briefings/rows.js +41 -0
- package/dist/tools/briefings/rows.js.map +1 -0
- package/dist/tools/briefings/toolchain.js +63 -0
- package/dist/tools/briefings/toolchain.js.map +1 -0
- package/dist/tools/briefings/why-proposed.js +69 -0
- package/dist/tools/briefings/why-proposed.js.map +1 -0
- package/dist/tools/briefings.js +44 -0
- package/dist/tools/briefings.js.map +1 -0
- package/dist/tools/feedback.js +97 -0
- package/dist/tools/feedback.js.map +1 -0
- package/dist/tools/hydrate.js +75 -0
- package/dist/tools/hydrate.js.map +1 -0
- package/dist/tools/intelligence.js +67 -0
- package/dist/tools/intelligence.js.map +1 -0
- package/dist/tools/lint/asm-decimal.js +86 -0
- package/dist/tools/lint/asm-decimal.js.map +1 -0
- package/dist/tools/lint/asm-rules.js +221 -0
- package/dist/tools/lint/asm-rules.js.map +1 -0
- package/dist/tools/lint/asm-shared.js +3 -0
- package/dist/tools/lint/asm-shared.js.map +1 -0
- package/dist/tools/lint/c-rules.js +215 -0
- package/dist/tools/lint/c-rules.js.map +1 -0
- package/dist/tools/lint/text.js +122 -0
- package/dist/tools/lint/text.js.map +1 -0
- package/dist/tools/lint/types.js +40 -0
- package/dist/tools/lint/types.js.map +1 -0
- package/dist/tools/lint.js +76 -0
- package/dist/tools/lint.js.map +1 -0
- package/dist/tools/memorization-mcp.js +111 -0
- package/dist/tools/memorization-mcp.js.map +1 -0
- package/dist/tools/pitfalls/bm25.js +53 -0
- package/dist/tools/pitfalls/bm25.js.map +1 -0
- package/dist/tools/pitfalls/format.js +58 -0
- package/dist/tools/pitfalls/format.js.map +1 -0
- package/dist/tools/pitfalls/graph.js +124 -0
- package/dist/tools/pitfalls/graph.js.map +1 -0
- package/dist/tools/pitfalls.js +134 -0
- package/dist/tools/pitfalls.js.map +1 -0
- package/dist/tools/prompts.js +81 -0
- package/dist/tools/prompts.js.map +1 -0
- package/dist/tools/query/compatibility/closure.js +62 -0
- package/dist/tools/query/compatibility/closure.js.map +1 -0
- package/dist/tools/query/compatibility/facts.js +28 -0
- package/dist/tools/query/compatibility/facts.js.map +1 -0
- package/dist/tools/query/compatibility/fetch.js +164 -0
- package/dist/tools/query/compatibility/fetch.js.map +1 -0
- package/dist/tools/query/compatibility/hard-rules.js +159 -0
- package/dist/tools/query/compatibility/hard-rules.js.map +1 -0
- package/dist/tools/query/compatibility/index.js +27 -0
- package/dist/tools/query/compatibility/index.js.map +1 -0
- package/dist/tools/query/compatibility/kernal-zp-rule.js +90 -0
- package/dist/tools/query/compatibility/kernal-zp-rule.js.map +1 -0
- package/dist/tools/query/compatibility/render.js +106 -0
- package/dist/tools/query/compatibility/render.js.map +1 -0
- package/dist/tools/query/compatibility/rules.js +337 -0
- package/dist/tools/query/compatibility/rules.js.map +1 -0
- package/dist/tools/query/compatibility/unit-rules.js +253 -0
- package/dist/tools/query/compatibility/unit-rules.js.map +1 -0
- package/dist/tools/query/game-design.js +83 -0
- package/dist/tools/query/game-design.js.map +1 -0
- package/dist/tools/query/lookups.js +220 -0
- package/dist/tools/query/lookups.js.map +1 -0
- package/dist/tools/query/plan-budget.js +266 -0
- package/dist/tools/query/plan-budget.js.map +1 -0
- package/dist/tools/query/recipes.js +214 -0
- package/dist/tools/query/recipes.js.map +1 -0
- package/dist/tools/query/retrieval.js +128 -0
- package/dist/tools/query/retrieval.js.map +1 -0
- package/dist/tools/query/shared.js +121 -0
- package/dist/tools/query/shared.js.map +1 -0
- package/dist/tools/query/techniques.js +331 -0
- package/dist/tools/query/techniques.js.map +1 -0
- package/dist/tools/query/timing.js +106 -0
- package/dist/tools/query/timing.js.map +1 -0
- package/dist/tools/query/types.js +11 -0
- package/dist/tools/query/types.js.map +1 -0
- package/dist/tools/query.js +21 -0
- package/dist/tools/query.js.map +1 -0
- package/dist/tools/resources.js +186 -0
- package/dist/tools/resources.js.map +1 -0
- package/dist/tools/run-game.js +249 -0
- package/dist/tools/run-game.js.map +1 -0
- package/dist/tools/selfimprovement.js +367 -0
- package/dist/tools/selfimprovement.js.map +1 -0
- package/docker-compose.yml +35 -0
- package/docs/ARCHITECTURE.md +164 -0
- package/docs/CONVENTIONS-archetypes.md +158 -0
- package/docs/CONVENTIONS-failures.md +46 -0
- package/docs/CONVENTIONS-game-designs.md +75 -0
- package/docs/CONVENTIONS-hardware-reference.md +184 -0
- package/docs/CONVENTIONS-pitfalls.md +91 -0
- package/docs/CONVENTIONS-recipes.md +106 -0
- package/docs/CONVENTIONS-techniques.md +309 -0
- package/docs/CONVENTIONS-toolchain-reference.md +89 -0
- package/docs/ONTOLOGY.md +655 -0
- package/docs/art/art-production-reference.md +157 -0
- package/docs/art/asset-pipelines.md +586 -0
- package/docs/c64-failure-patterns.md +693 -0
- package/docs/demo-design/demo-composition.md +349 -0
- package/docs/demo-design/demo-design-philosophy.md +320 -0
- package/docs/demo-design/intro-cracktro-patterns.md +435 -0
- package/docs/figures/ctm-spd-embed-probe.png +0 -0
- package/docs/figures/dot-flag-mid-5000000.png +0 -0
- package/docs/figures/dot-flag-no-erase-12000000.png +0 -0
- package/docs/figures/edges.json +1 -0
- package/docs/figures/fig6-architecture.png +0 -0
- package/docs/figures/fig7-ontology.png +0 -0
- package/docs/figures/screen-dissolve-mid-frame25.png +0 -0
- package/docs/figures/screen-dissolve-wipe-frame25.png +0 -0
- package/docs/figures/spindle-two-parts-part1.png +0 -0
- package/docs/figures/spindle-two-parts-part2.png +0 -0
- package/docs/figures/starters/action-puzzle.png +0 -0
- package/docs/figures/starters/adventure.png +0 -0
- package/docs/figures/starters/beat-em-up.png +0 -0
- package/docs/figures/starters/demo.png +0 -0
- package/docs/figures/starters/platformer.png +0 -0
- package/docs/figures/starters/shmup-vertical.png +0 -0
- package/docs/figures/tape-kernal-master-ntsc.png +0 -0
- package/docs/figures/tape-kernal-master-pal.png +0 -0
- package/docs/figures/tape-turbo-loader-ntsc.png +0 -0
- package/docs/figures/tape-turbo-loader-pal.png +0 -0
- package/docs/figures/tape-turbo-master-ntsc.png +0 -0
- package/docs/figures/tape-turbo-master-pal.png +0 -0
- package/docs/figures/tech-tech-pad2-12000000.png +0 -0
- package/docs/figures/tech-tech-pad4-12000000.png +0 -0
- package/docs/figures/tech-tech-seven-12000000.png +0 -0
- package/docs/figures/twister-mid-5000000.png +0 -0
- package/docs/figures/twister-straight-12000000.png +0 -0
- package/docs/figures/vector-balls-nosort-ntsc.png +0 -0
- package/docs/figures/vector-balls-nosort.png +0 -0
- package/docs/formats/c64-file-formats.md +1212 -0
- package/docs/formats/iec-disk-reference.md +754 -0
- package/docs/game-design/c64-game-archetypes.md +273 -0
- package/docs/game-design/designs/falling-blocks.md +54 -0
- package/docs/game-design/designs/platformer-scaffold.md +48 -0
- package/docs/game-design/designs/simple-shmup.md +27 -0
- package/docs/game-design/enemy-behaviour-and-difficulty.md +349 -0
- package/docs/game-design/game-design-patterns.md +1189 -0
- package/docs/game-design/game-structure.md +354 -0
- package/docs/game-design/production-planning.md +349 -0
- package/docs/game-design/reference-game-sources.md +90 -0
- package/docs/hardware/6502-illegal-opcodes.md +1353 -0
- package/docs/hardware/6510-cpu-reference.md +2112 -0
- package/docs/hardware/c64-memory-map.md +3184 -0
- package/docs/hardware/c64-registers-reference.md +996 -0
- package/docs/hardware/cia-reference.md +1281 -0
- package/docs/hardware/kernal-routines-reference.md +1836 -0
- package/docs/hardware/pal-ntsc-reference.md +813 -0
- package/docs/hardware/sid-reference.md +1137 -0
- package/docs/hardware/vic-ii-reference.md +1714 -0
- package/docs/music/music-production-reference.md +403 -0
- package/docs/pitfalls/banking.md +1067 -0
- package/docs/pitfalls/cia.md +1147 -0
- package/docs/pitfalls/cpu.md +874 -0
- package/docs/pitfalls/input.md +282 -0
- package/docs/pitfalls/kernal-and-io.md +1615 -0
- package/docs/pitfalls/loader.md +1191 -0
- package/docs/pitfalls/logic.md +94 -0
- package/docs/pitfalls/maths.md +187 -0
- package/docs/pitfalls/raster-and-badline.md +721 -0
- package/docs/pitfalls/region-timing.md +660 -0
- package/docs/pitfalls/scroll.md +207 -0
- package/docs/pitfalls/sid.md +738 -0
- package/docs/pitfalls/sprite.md +979 -0
- package/docs/pitfalls/text-mode-render.md +1172 -0
- package/docs/recipes/cc65/cartridge-8k.md +398 -0
- package/docs/recipes/cc65/hello-world-conio.md +76 -0
- package/docs/recipes/cc65/memory-layout.md +312 -0
- package/docs/recipes/cc65/screenshots/README.md +14 -0
- package/docs/recipes/cc65/screenshots/cartridge-8k-ntsc.png +0 -0
- package/docs/recipes/cc65/screenshots/cartridge-8k.png +0 -0
- package/docs/recipes/cc65/screenshots/hello-world-conio.png +0 -0
- package/docs/recipes/cc65/screenshots/memory-layout-ntsc.png +0 -0
- package/docs/recipes/cc65/screenshots/memory-layout.png +0 -0
- package/docs/recipes/kickassembler/basic-float-calls.md +578 -0
- package/docs/recipes/kickassembler/basic-wedge.md +398 -0
- package/docs/recipes/kickassembler/big-font-scroller.md +620 -0
- package/docs/recipes/kickassembler/charset-copy-rom-to-ram.md +623 -0
- package/docs/recipes/kickassembler/cia-revision-detect.md +366 -0
- package/docs/recipes/kickassembler/colour-cycling.md +343 -0
- package/docs/recipes/kickassembler/colour-fade.md +303 -0
- package/docs/recipes/kickassembler/compare-16bit-signed.md +1007 -0
- package/docs/recipes/kickassembler/cracktro-template.md +526 -0
- package/docs/recipes/kickassembler/crt-banked.md +276 -0
- package/docs/recipes/kickassembler/dos-error-codes.md +466 -0
- package/docs/recipes/kickassembler/dot-flag.md +576 -0
- package/docs/recipes/kickassembler/drive-job-queue.md +667 -0
- package/docs/recipes/kickassembler/drive-via-probe.md +690 -0
- package/docs/recipes/kickassembler/dycp-scroller.md +568 -0
- package/docs/recipes/kickassembler/dypp-sprite-scroller.md +681 -0
- package/docs/recipes/kickassembler/dysp.md +670 -0
- package/docs/recipes/kickassembler/easyflash-save.md +800 -0
- package/docs/recipes/kickassembler/eight-way-scroll.md +1484 -0
- package/docs/recipes/kickassembler/file-io-roundtrip.md +546 -0
- package/docs/recipes/kickassembler/fire-effect.md +420 -0
- package/docs/recipes/kickassembler/fld.md +414 -0
- package/docs/recipes/kickassembler/fli-image.md +336 -0
- package/docs/recipes/kickassembler/four-player-read.md +672 -0
- package/docs/recipes/kickassembler/headless-verify.md +228 -0
- package/docs/recipes/kickassembler/hello-world.md +98 -0
- package/docs/recipes/kickassembler/high-score-insert.md +660 -0
- package/docs/recipes/kickassembler/hires-plot-line.md +723 -0
- package/docs/recipes/kickassembler/irq-chain.md +316 -0
- package/docs/recipes/kickassembler/irq-owns-port.md +668 -0
- package/docs/recipes/kickassembler/isometric-room.md +1145 -0
- package/docs/recipes/kickassembler/light-pen-read.md +926 -0
- package/docs/recipes/kickassembler/logic-rate-decoupling.md +670 -0
- package/docs/recipes/kickassembler/mci-interlace.md +543 -0
- package/docs/recipes/kickassembler/memory-layout.md +277 -0
- package/docs/recipes/kickassembler/mouse-1351-read.md +801 -0
- package/docs/recipes/kickassembler/nmi-timer-tick.md +562 -0
- package/docs/recipes/kickassembler/own-keyscan.md +881 -0
- package/docs/recipes/kickassembler/paddle-read.md +751 -0
- package/docs/recipes/kickassembler/palette-cells.md +219 -0
- package/docs/recipes/kickassembler/pseudo-3d-road.md +930 -0
- package/docs/recipes/kickassembler/pwm-digi.md +463 -0
- package/docs/recipes/kickassembler/raster-bars.md +264 -0
- package/docs/recipes/kickassembler/reu-dma.md +1019 -0
- package/docs/recipes/kickassembler/runtime-relocation.md +743 -0
- package/docs/recipes/kickassembler/screen-dissolve.md +545 -0
- package/docs/recipes/kickassembler/screen-wipe.md +408 -0
- package/docs/recipes/kickassembler/screenshots/README.md +72 -0
- package/docs/recipes/kickassembler/screenshots/basic-float-calls-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/basic-float-calls.png +0 -0
- package/docs/recipes/kickassembler/screenshots/basic-wedge-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/basic-wedge.png +0 -0
- package/docs/recipes/kickassembler/screenshots/big-font-scroller-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/big-font-scroller.png +0 -0
- package/docs/recipes/kickassembler/screenshots/charset-copy-rom-to-ram-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/charset-copy-rom-to-ram.png +0 -0
- package/docs/recipes/kickassembler/screenshots/cia-revision-detect-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/cia-revision-detect.png +0 -0
- package/docs/recipes/kickassembler/screenshots/colour-cycling-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/colour-cycling.png +0 -0
- package/docs/recipes/kickassembler/screenshots/colour-fade-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/colour-fade.png +0 -0
- package/docs/recipes/kickassembler/screenshots/compare-16bit-signed-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/compare-16bit-signed.png +0 -0
- package/docs/recipes/kickassembler/screenshots/cracktro-template.png +0 -0
- package/docs/recipes/kickassembler/screenshots/crt-banked-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/crt-banked.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dos-error-codes-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dos-error-codes.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dot-flag-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dot-flag.png +0 -0
- package/docs/recipes/kickassembler/screenshots/drive-job-queue-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/drive-job-queue.png +0 -0
- package/docs/recipes/kickassembler/screenshots/drive-via-probe-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/drive-via-probe.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dycp-scroller-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dycp-scroller.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dypp-sprite-scroller-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dypp-sprite-scroller.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dysp-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/dysp.png +0 -0
- package/docs/recipes/kickassembler/screenshots/easyflash-save-ntsc-run2.png +0 -0
- package/docs/recipes/kickassembler/screenshots/easyflash-save-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/easyflash-save-run2.png +0 -0
- package/docs/recipes/kickassembler/screenshots/easyflash-save.png +0 -0
- package/docs/recipes/kickassembler/screenshots/eight-way-scroll-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/eight-way-scroll.png +0 -0
- package/docs/recipes/kickassembler/screenshots/file-io-roundtrip-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/file-io-roundtrip.png +0 -0
- package/docs/recipes/kickassembler/screenshots/fire-effect-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/fire-effect.png +0 -0
- package/docs/recipes/kickassembler/screenshots/fld-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/fld.png +0 -0
- package/docs/recipes/kickassembler/screenshots/fli-image.png +0 -0
- package/docs/recipes/kickassembler/screenshots/four-player-read-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/four-player-read.png +0 -0
- package/docs/recipes/kickassembler/screenshots/headless-verify-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/headless-verify.png +0 -0
- package/docs/recipes/kickassembler/screenshots/hello-world.png +0 -0
- package/docs/recipes/kickassembler/screenshots/high-score-insert-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/high-score-insert.png +0 -0
- package/docs/recipes/kickassembler/screenshots/hires-plot-line-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/hires-plot-line.png +0 -0
- package/docs/recipes/kickassembler/screenshots/irq-chain-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/irq-chain.png +0 -0
- package/docs/recipes/kickassembler/screenshots/irq-owns-port-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/irq-owns-port.png +0 -0
- package/docs/recipes/kickassembler/screenshots/isometric-room-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/isometric-room.png +0 -0
- package/docs/recipes/kickassembler/screenshots/light-pen-read-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/light-pen-read.png +0 -0
- package/docs/recipes/kickassembler/screenshots/logic-rate-decoupling-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/logic-rate-decoupling.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mci-interlace-average.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mci-interlace-frame-b.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mci-interlace-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mci-interlace.png +0 -0
- package/docs/recipes/kickassembler/screenshots/memory-layout-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/memory-layout.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mouse-1351-read-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/mouse-1351-read.png +0 -0
- package/docs/recipes/kickassembler/screenshots/nmi-timer-tick-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/nmi-timer-tick.png +0 -0
- package/docs/recipes/kickassembler/screenshots/own-keyscan-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/own-keyscan.png +0 -0
- package/docs/recipes/kickassembler/screenshots/paddle-read-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/paddle-read.png +0 -0
- package/docs/recipes/kickassembler/screenshots/palette-cells-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/palette-cells.png +0 -0
- package/docs/recipes/kickassembler/screenshots/pseudo-3d-road-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/pseudo-3d-road.png +0 -0
- package/docs/recipes/kickassembler/screenshots/pwm-digi-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/pwm-digi.png +0 -0
- package/docs/recipes/kickassembler/screenshots/raster-bars.png +0 -0
- package/docs/recipes/kickassembler/screenshots/reu-dma-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/reu-dma.png +0 -0
- package/docs/recipes/kickassembler/screenshots/runtime-relocation-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/runtime-relocation.png +0 -0
- package/docs/recipes/kickassembler/screenshots/screen-dissolve-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/screen-dissolve.png +0 -0
- package/docs/recipes/kickassembler/screenshots/screen-wipe-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/screen-wipe.png +0 -0
- package/docs/recipes/kickassembler/screenshots/scroll-panel-split-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/scroll-panel-split.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sfx-in-player-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sfx-in-player.png +0 -0
- package/docs/recipes/kickassembler/screenshots/shadebobs-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/shadebobs.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sideborder-open.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sine-scroller.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sine-table-runtime-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sine-table-runtime.png +0 -0
- package/docs/recipes/kickassembler/screenshots/software-sprite-preshifted-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/software-sprite-preshifted.png +0 -0
- package/docs/recipes/kickassembler/screenshots/speedcode-generator-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/speedcode-generator.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-border-scroller-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-border-scroller.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-cache-flip-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-cache-flip.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-multiplex-24.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-multiplex-game-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-multiplex-game.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-priority-classes-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-priority-classes.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-sine-chain-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-sine-chain.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-stretcher-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprite-stretcher.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprites-only-screen-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sprites-only-screen.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sqrt-atan2-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/sqrt-atan2.png +0 -0
- package/docs/recipes/kickassembler/screenshots/stable-raster-irq-control.png +0 -0
- package/docs/recipes/kickassembler/screenshots/stable-raster-irq.png +0 -0
- package/docs/recipes/kickassembler/screenshots/tech-tech-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/tech-tech.png +0 -0
- package/docs/recipes/kickassembler/screenshots/tod-alarm-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/tod-alarm.png +0 -0
- package/docs/recipes/kickassembler/screenshots/topbottom-border-open.png +0 -0
- package/docs/recipes/kickassembler/screenshots/twister-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/twister.png +0 -0
- package/docs/recipes/kickassembler/screenshots/unit-test-driver-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/unit-test-driver.png +0 -0
- package/docs/recipes/kickassembler/screenshots/vector-balls-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/vector-balls.png +0 -0
- package/docs/recipes/kickassembler/screenshots/wireframe-ships-ntsc.png +0 -0
- package/docs/recipes/kickassembler/screenshots/wireframe-ships.png +0 -0
- package/docs/recipes/kickassembler/scroll-panel-split.md +381 -0
- package/docs/recipes/kickassembler/sfx-in-player.md +907 -0
- package/docs/recipes/kickassembler/shadebobs.md +522 -0
- package/docs/recipes/kickassembler/sideborder-open.md +338 -0
- package/docs/recipes/kickassembler/sine-scroller.md +321 -0
- package/docs/recipes/kickassembler/sine-table-runtime.md +815 -0
- package/docs/recipes/kickassembler/software-sprite-preshifted.md +725 -0
- package/docs/recipes/kickassembler/speedcode-generator.md +606 -0
- package/docs/recipes/kickassembler/sprite-border-scroller.md +696 -0
- package/docs/recipes/kickassembler/sprite-cache-flip.md +1071 -0
- package/docs/recipes/kickassembler/sprite-multiplex-24.md +326 -0
- package/docs/recipes/kickassembler/sprite-multiplex-game.md +986 -0
- package/docs/recipes/kickassembler/sprite-priority-classes.md +463 -0
- package/docs/recipes/kickassembler/sprite-sine-chain.md +338 -0
- package/docs/recipes/kickassembler/sprite-stretcher.md +501 -0
- package/docs/recipes/kickassembler/sprites-only-screen.md +550 -0
- package/docs/recipes/kickassembler/sqrt-atan2.md +1014 -0
- package/docs/recipes/kickassembler/stable-raster-irq.md +360 -0
- package/docs/recipes/kickassembler/tape-turbo-loader.md +875 -0
- package/docs/recipes/kickassembler/tech-tech.md +679 -0
- package/docs/recipes/kickassembler/tod-alarm.md +754 -0
- package/docs/recipes/kickassembler/topbottom-border-open.md +407 -0
- package/docs/recipes/kickassembler/twister.md +518 -0
- package/docs/recipes/kickassembler/unit-test-driver.md +314 -0
- package/docs/recipes/kickassembler/vector-balls.md +545 -0
- package/docs/recipes/kickassembler/wireframe-ships.md +2324 -0
- package/docs/recipes/oscar64/actor-activation-window.md +807 -0
- package/docs/recipes/oscar64/adventure-engine.md +1094 -0
- package/docs/recipes/oscar64/attract-replay.md +465 -0
- package/docs/recipes/oscar64/beat-em-up-lanes.md +563 -0
- package/docs/recipes/oscar64/bfs-distance-map.md +723 -0
- package/docs/recipes/oscar64/bitmap-koala-viewer.md +355 -0
- package/docs/recipes/oscar64/car-contact.md +950 -0
- package/docs/recipes/oscar64/cave-scan.md +609 -0
- package/docs/recipes/oscar64/char-bullets.md +477 -0
- package/docs/recipes/oscar64/charset-animation.md +383 -0
- package/docs/recipes/oscar64/charset-parallax.md +401 -0
- package/docs/recipes/oscar64/destructible-terrain.md +1099 -0
- package/docs/recipes/oscar64/difficulty-tables.md +654 -0
- package/docs/recipes/oscar64/dig-and-guards.md +975 -0
- package/docs/recipes/oscar64/directory-reader.md +535 -0
- package/docs/recipes/oscar64/divide-check.md +581 -0
- package/docs/recipes/oscar64/double-buffer-nomirror.md +205 -0
- package/docs/recipes/oscar64/double-buffer.md +255 -0
- package/docs/recipes/oscar64/falling-blocks.md +874 -0
- package/docs/recipes/oscar64/fixed-point-jump-velocity.md +289 -0
- package/docs/recipes/oscar64/fixed-point-jump.md +210 -0
- package/docs/recipes/oscar64/flip-screen-rooms.md +793 -0
- package/docs/recipes/oscar64/frame-sync-loop-overrun.md +214 -0
- package/docs/recipes/oscar64/frame-sync-loop.md +254 -0
- package/docs/recipes/oscar64/game-tree-search.md +743 -0
- package/docs/recipes/oscar64/ghost-targeting.md +867 -0
- package/docs/recipes/oscar64/headless-verify.md +345 -0
- package/docs/recipes/oscar64/hello-world.md +83 -0
- package/docs/recipes/oscar64/high-score-persist.md +772 -0
- package/docs/recipes/oscar64/iffl-kernal-skip.md +421 -0
- package/docs/recipes/oscar64/joystick-input.md +358 -0
- package/docs/recipes/oscar64/lane-pursuit.md +1028 -0
- package/docs/recipes/oscar64/level-rle-decoder.md +620 -0
- package/docs/recipes/oscar64/lfsr-random-seed2.md +338 -0
- package/docs/recipes/oscar64/lfsr-random.md +403 -0
- package/docs/recipes/oscar64/load-asset-runtime.md +492 -0
- package/docs/recipes/oscar64/memory-layout.md +232 -0
- package/docs/recipes/oscar64/mixed-fighters.md +781 -0
- package/docs/recipes/oscar64/multi-sprite-object.md +533 -0
- package/docs/recipes/oscar64/nav-area-pathfinding.md +881 -0
- package/docs/recipes/oscar64/object-pool.md +572 -0
- package/docs/recipes/oscar64/pal-ntsc-detect.md +257 -0
- package/docs/recipes/oscar64/password-state.md +532 -0
- package/docs/recipes/oscar64/per-frame-hitbox.md +651 -0
- package/docs/recipes/oscar64/petscii-screen-codes.md +428 -0
- package/docs/recipes/oscar64/platformer-scaffold.md +1467 -0
- package/docs/recipes/oscar64/print-number.md +367 -0
- package/docs/recipes/oscar64/raster-bars.md +239 -0
- package/docs/recipes/oscar64/raster-profile-bars.md +404 -0
- package/docs/recipes/oscar64/rel-side-sectors.md +391 -0
- package/docs/recipes/oscar64/relative-file-records.md +378 -0
- package/docs/recipes/oscar64/save-load-seq-file.md +365 -0
- package/docs/recipes/oscar64/screenshots/README.md +99 -0
- package/docs/recipes/oscar64/screenshots/actor-activation-window-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/actor-activation-window.png +0 -0
- package/docs/recipes/oscar64/screenshots/adventure-engine-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/adventure-engine.png +0 -0
- package/docs/recipes/oscar64/screenshots/attract-replay-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/attract-replay.png +0 -0
- package/docs/recipes/oscar64/screenshots/beat-em-up-lanes-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/beat-em-up-lanes.png +0 -0
- package/docs/recipes/oscar64/screenshots/bfs-distance-map-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/bfs-distance-map.png +0 -0
- package/docs/recipes/oscar64/screenshots/bitmap-koala-viewer.png +0 -0
- package/docs/recipes/oscar64/screenshots/car-contact-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/car-contact.png +0 -0
- package/docs/recipes/oscar64/screenshots/cave-scan-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/cave-scan.png +0 -0
- package/docs/recipes/oscar64/screenshots/char-bullets-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/char-bullets.png +0 -0
- package/docs/recipes/oscar64/screenshots/charset-animation-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/charset-animation.png +0 -0
- package/docs/recipes/oscar64/screenshots/charset-parallax-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/charset-parallax.png +0 -0
- package/docs/recipes/oscar64/screenshots/destructible-terrain-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/destructible-terrain.png +0 -0
- package/docs/recipes/oscar64/screenshots/difficulty-tables-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/difficulty-tables.png +0 -0
- package/docs/recipes/oscar64/screenshots/dig-and-guards-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/dig-and-guards.png +0 -0
- package/docs/recipes/oscar64/screenshots/directory-reader-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/directory-reader.png +0 -0
- package/docs/recipes/oscar64/screenshots/divide-check-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/divide-check.png +0 -0
- package/docs/recipes/oscar64/screenshots/double-buffer-nomirror.png +0 -0
- package/docs/recipes/oscar64/screenshots/double-buffer-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/double-buffer.png +0 -0
- package/docs/recipes/oscar64/screenshots/falling-blocks-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/falling-blocks.png +0 -0
- package/docs/recipes/oscar64/screenshots/fixed-point-jump-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/fixed-point-jump-velocity-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/fixed-point-jump-velocity.png +0 -0
- package/docs/recipes/oscar64/screenshots/fixed-point-jump.png +0 -0
- package/docs/recipes/oscar64/screenshots/flip-screen-rooms-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/flip-screen-rooms.png +0 -0
- package/docs/recipes/oscar64/screenshots/frame-sync-loop-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/frame-sync-loop-overrun.png +0 -0
- package/docs/recipes/oscar64/screenshots/frame-sync-loop.png +0 -0
- package/docs/recipes/oscar64/screenshots/game-tree-search-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/game-tree-search.png +0 -0
- package/docs/recipes/oscar64/screenshots/ghost-targeting-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/ghost-targeting.png +0 -0
- package/docs/recipes/oscar64/screenshots/headless-verify-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/headless-verify.png +0 -0
- package/docs/recipes/oscar64/screenshots/hello-world.png +0 -0
- package/docs/recipes/oscar64/screenshots/high-score-persist-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/high-score-persist.png +0 -0
- package/docs/recipes/oscar64/screenshots/iffl-kernal-skip-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/iffl-kernal-skip.png +0 -0
- package/docs/recipes/oscar64/screenshots/joystick-input-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/joystick-input.png +0 -0
- package/docs/recipes/oscar64/screenshots/lane-pursuit-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/lane-pursuit.png +0 -0
- package/docs/recipes/oscar64/screenshots/level-rle-decoder-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/level-rle-decoder.png +0 -0
- package/docs/recipes/oscar64/screenshots/lfsr-random-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/lfsr-random-seed2.png +0 -0
- package/docs/recipes/oscar64/screenshots/lfsr-random.png +0 -0
- package/docs/recipes/oscar64/screenshots/load-asset-runtime-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/load-asset-runtime.png +0 -0
- package/docs/recipes/oscar64/screenshots/memory-layout-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/memory-layout.png +0 -0
- package/docs/recipes/oscar64/screenshots/mixed-fighters-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/mixed-fighters.png +0 -0
- package/docs/recipes/oscar64/screenshots/multi-sprite-object-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/multi-sprite-object.png +0 -0
- package/docs/recipes/oscar64/screenshots/nav-area-pathfinding-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/nav-area-pathfinding.png +0 -0
- package/docs/recipes/oscar64/screenshots/object-pool-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/object-pool.png +0 -0
- package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-oldntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/pal-ntsc-detect-pal.png +0 -0
- package/docs/recipes/oscar64/screenshots/password-state-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/password-state.png +0 -0
- package/docs/recipes/oscar64/screenshots/per-frame-hitbox-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/per-frame-hitbox.png +0 -0
- package/docs/recipes/oscar64/screenshots/petscii-screen-codes-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/petscii-screen-codes.png +0 -0
- package/docs/recipes/oscar64/screenshots/platformer-scaffold-d64-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/platformer-scaffold-d64.png +0 -0
- package/docs/recipes/oscar64/screenshots/platformer-scaffold-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/platformer-scaffold.png +0 -0
- package/docs/recipes/oscar64/screenshots/print-number-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/print-number.png +0 -0
- package/docs/recipes/oscar64/screenshots/raster-bars.png +0 -0
- package/docs/recipes/oscar64/screenshots/raster-profile-bars-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/raster-profile-bars.png +0 -0
- package/docs/recipes/oscar64/screenshots/rel-side-sectors-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/rel-side-sectors.png +0 -0
- package/docs/recipes/oscar64/screenshots/relative-file-records-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/relative-file-records.png +0 -0
- package/docs/recipes/oscar64/screenshots/save-load-seq-file-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/save-load-seq-file.png +0 -0
- package/docs/recipes/oscar64/screenshots/seeded-level-fill-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/seeded-level-fill.png +0 -0
- package/docs/recipes/oscar64/screenshots/sfx-engine-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/sfx-engine.png +0 -0
- package/docs/recipes/oscar64/screenshots/sid-music-player.png +0 -0
- package/docs/recipes/oscar64/screenshots/simple-shmup.png +0 -0
- package/docs/recipes/oscar64/screenshots/slope-collision-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/slope-collision.png +0 -0
- package/docs/recipes/oscar64/screenshots/soft-scroll-h.png +0 -0
- package/docs/recipes/oscar64/screenshots/sprite-animation-table-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/sprite-animation-table.png +0 -0
- package/docs/recipes/oscar64/screenshots/sprite-multiplex-8.png +0 -0
- package/docs/recipes/oscar64/screenshots/stable-raster-irq.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-input-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-input.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-overlay-playfield-gameover.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-overlay-playfield-notrail-control.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-overlay-playfield-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-overlay-playfield-ungated-control.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-overlay-playfield.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-window-menu-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/text-window-menu.png +0 -0
- package/docs/recipes/oscar64/screenshots/tile-grid-collision-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/tile-grid-collision.png +0 -0
- package/docs/recipes/oscar64/screenshots/tile-map-render-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/tile-map-render.png +0 -0
- package/docs/recipes/oscar64/screenshots/two-player-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/two-player.png +0 -0
- package/docs/recipes/oscar64/screenshots/two-word-parser-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/two-word-parser.png +0 -0
- package/docs/recipes/oscar64/screenshots/vehicle-control-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/vehicle-control.png +0 -0
- package/docs/recipes/oscar64/screenshots/wave-director-ntsc.png +0 -0
- package/docs/recipes/oscar64/screenshots/wave-director.png +0 -0
- package/docs/recipes/oscar64/seeded-level-fill.md +364 -0
- package/docs/recipes/oscar64/sfx-engine.md +514 -0
- package/docs/recipes/oscar64/sid-music-player.md +441 -0
- package/docs/recipes/oscar64/simple-shmup.md +1000 -0
- package/docs/recipes/oscar64/slope-collision.md +610 -0
- package/docs/recipes/oscar64/soft-scroll-h.md +261 -0
- package/docs/recipes/oscar64/sprite-animation-table.md +671 -0
- package/docs/recipes/oscar64/sprite-multiplex-8.md +314 -0
- package/docs/recipes/oscar64/stable-raster-irq.md +236 -0
- package/docs/recipes/oscar64/text-input.md +274 -0
- package/docs/recipes/oscar64/text-overlay-playfield.md +617 -0
- package/docs/recipes/oscar64/text-window-menu.md +551 -0
- package/docs/recipes/oscar64/tile-grid-collision.md +563 -0
- package/docs/recipes/oscar64/tile-map-render.md +395 -0
- package/docs/recipes/oscar64/two-player.md +528 -0
- package/docs/recipes/oscar64/two-word-parser.md +510 -0
- package/docs/recipes/oscar64/vehicle-control.md +771 -0
- package/docs/recipes/oscar64/wave-director.md +787 -0
- package/docs/recipes/runs.json +1606 -0
- package/docs/runtime/sim6502-reference.md +466 -0
- package/docs/runtime/vice-mcp-reference.md +351 -0
- package/docs/runtime/vice-reference.md +1393 -0
- package/docs/techniques/bitmap-modes.md +685 -0
- package/docs/techniques/cpu-cycle-tricks.md +1570 -0
- package/docs/techniques/effects-vector-3d.md +1431 -0
- package/docs/techniques/file-io.md +1427 -0
- package/docs/techniques/input.md +1072 -0
- package/docs/techniques/loaders-packers.md +768 -0
- package/docs/techniques/logic.md +3091 -0
- package/docs/techniques/maths.md +1524 -0
- package/docs/techniques/memory-banking.md +1686 -0
- package/docs/techniques/music-sid.md +1144 -0
- package/docs/techniques/raster.md +1543 -0
- package/docs/techniques/scroll.md +1292 -0
- package/docs/techniques/sprite.md +2566 -0
- package/docs/techniques/text-mode-render.md +717 -0
- package/docs/techniques/text.md +1095 -0
- package/docs/techniques/transitions.md +405 -0
- package/docs/toolchains/cartconv-reference.md +262 -0
- package/docs/toolchains/cc1541-reference.md +316 -0
- package/docs/toolchains/cc65-reference.md +667 -0
- package/docs/toolchains/error-sources/cc65/README.md +1 -0
- package/docs/toolchains/error-sources/cc65/missing-setcpu.s +4 -0
- package/docs/toolchains/error-sources/cc65/range-error.s +4 -0
- package/docs/toolchains/error-sources/cc65/segment-overflow.c +6 -0
- package/docs/toolchains/error-sources/cc65/unresolved-external.c +6 -0
- package/docs/toolchains/error-sources/kickassembler/README.md +1 -0
- package/docs/toolchains/error-sources/kickassembler/branch-out-of-range.asm +4 -0
- package/docs/toolchains/error-sources/kickassembler/failed-assert.asm +3 -0
- package/docs/toolchains/error-sources/kickassembler/flat-lib.asm +4 -0
- package/docs/toolchains/error-sources/kickassembler/import-source-no-namespace.asm +5 -0
- package/docs/toolchains/error-sources/kickassembler/macro-without-parentheses.asm +6 -0
- package/docs/toolchains/error-sources/kickassembler/memory-block-overlap.asm +4 -0
- package/docs/toolchains/error-sources/kickassembler/symbol-clash-lib.asm +5 -0
- package/docs/toolchains/error-sources/kickassembler/symbol-clash-main.asm +6 -0
- package/docs/toolchains/error-sources/kickassembler/unknown-symbol.asm +4 -0
- package/docs/toolchains/error-sources/oscar64/README.md +1 -0
- package/docs/toolchains/error-sources/oscar64/asm-addressing-mode.c +7 -0
- package/docs/toolchains/error-sources/oscar64/region-overflow.c +6 -0
- package/docs/toolchains/error-sources/oscar64/undefined-extern-var.c +5 -0
- package/docs/toolchains/error-sources/oscar64/undefined-extern.c +6 -0
- package/docs/toolchains/error-sources/oscar64/unknown-vic-field.c +6 -0
- package/docs/toolchains/error-sources/oscar64/void-main.c +3 -0
- package/docs/toolchains/kickassembler-reference.md +1020 -0
- package/docs/toolchains/memory-layout-planning.md +362 -0
- package/docs/toolchains/oscar64-headers-reference.md +583 -0
- package/docs/toolchains/oscar64-reference.md +892 -0
- package/docs/toolchains/petcat-reference.md +290 -0
- package/docs/toolchains/png2prg.md +188 -0
- package/docs/toolchains/release-disk.md +314 -0
- package/docs/toolchains/sidreloc.md +236 -0
- package/docs/toolchains/spindle-reference.md +487 -0
- package/docs/toolchains/tape-mastering.md +592 -0
- package/docs/toolchains/unit-testing-6502.md +281 -0
- package/docs/workflow/agent-harness.md +478 -0
- package/package.json +105 -0
- package/templates/_harness/CLAUDE.md.template +91 -0
- package/templates/_harness/PLAN.md.template +72 -0
- package/templates/_harness/check.py +442 -0
- package/templates/_harness/claude/settings.json +17 -0
- package/templates/_harness/gen-asm-header.py +96 -0
- package/templates/_harness/harness.mk +273 -0
- package/templates/_harness/hooks/plan-gate.py +185 -0
- package/templates/_harness/meter/frame_meter.asm +311 -0
- package/templates/_harness/meter/frame_meter.c +155 -0
- package/templates/_harness/meter/frame_meter.h +80 -0
- package/templates/_harness/zp-used.py +104 -0
- package/templates/action-puzzle/.claude/settings.json +17 -0
- package/templates/action-puzzle/.mcp.json +10 -0
- package/templates/action-puzzle/CLAUDE.md +120 -0
- package/templates/action-puzzle/Makefile +125 -0
- package/templates/action-puzzle/PLAN.md +388 -0
- package/templates/action-puzzle/README.md +240 -0
- package/templates/action-puzzle/expect.json +25 -0
- package/templates/action-puzzle/src/cave.c +280 -0
- package/templates/action-puzzle/src/cave.h +80 -0
- package/templates/action-puzzle/src/gen_autopilot.h +20 -0
- package/templates/action-puzzle/src/gen_caves.h +59 -0
- package/templates/action-puzzle/src/gen_notes.h +16 -0
- package/templates/action-puzzle/src/hiscore.c +152 -0
- package/templates/action-puzzle/src/hiscore.h +41 -0
- package/templates/action-puzzle/src/level.c +64 -0
- package/templates/action-puzzle/src/level.h +24 -0
- package/templates/action-puzzle/src/main.c +599 -0
- package/templates/action-puzzle/src/render.c +239 -0
- package/templates/action-puzzle/src/render.h +31 -0
- package/templates/action-puzzle/src/sound.c +158 -0
- package/templates/action-puzzle/src/sound.h +22 -0
- package/templates/action-puzzle/tools/disk_check.py +77 -0
- package/templates/action-puzzle/tools/drive.py +106 -0
- package/templates/action-puzzle/tools/gen.py +454 -0
- package/templates/action-puzzle/tools/model_check.py +49 -0
- package/templates/action-puzzle/tools/verdict_codes.py +24 -0
- package/templates/adventure/.claude/settings.json +17 -0
- package/templates/adventure/.mcp.json +10 -0
- package/templates/adventure/CLAUDE.md +118 -0
- package/templates/adventure/Makefile +113 -0
- package/templates/adventure/PLAN.md +396 -0
- package/templates/adventure/README.md +277 -0
- package/templates/adventure/expect.json +25 -0
- package/templates/adventure/src/engine.c +543 -0
- package/templates/adventure/src/engine.h +48 -0
- package/templates/adventure/src/gen_script.h +141 -0
- package/templates/adventure/src/gen_world.h +511 -0
- package/templates/adventure/src/main.c +439 -0
- package/templates/adventure/src/picture.c +83 -0
- package/templates/adventure/src/picture.h +16 -0
- package/templates/adventure/src/save.c +159 -0
- package/templates/adventure/src/save.h +25 -0
- package/templates/adventure/src/sound.c +71 -0
- package/templates/adventure/src/sound.h +16 -0
- package/templates/adventure/src/text.c +349 -0
- package/templates/adventure/src/text.h +40 -0
- package/templates/adventure/tools/disk_check.py +128 -0
- package/templates/adventure/tools/drive.py +119 -0
- package/templates/adventure/tools/gen.py +1033 -0
- package/templates/adventure/tools/world.py +356 -0
- package/templates/beat-em-up/.claude/settings.json +17 -0
- package/templates/beat-em-up/.mcp.json +10 -0
- package/templates/beat-em-up/CLAUDE.md +136 -0
- package/templates/beat-em-up/Makefile +96 -0
- package/templates/beat-em-up/PLAN.md +502 -0
- package/templates/beat-em-up/README.md +315 -0
- package/templates/beat-em-up/expect-gameover.json +8 -0
- package/templates/beat-em-up/expect.json +29 -0
- package/templates/beat-em-up/src/anim.c +70 -0
- package/templates/beat-em-up/src/art.c +344 -0
- package/templates/beat-em-up/src/autopilot.h +154 -0
- package/templates/beat-em-up/src/brute.c +273 -0
- package/templates/beat-em-up/src/enemy.c +281 -0
- package/templates/beat-em-up/src/engine.asm +512 -0
- package/templates/beat-em-up/src/fighter.c +384 -0
- package/templates/beat-em-up/src/game.h +269 -0
- package/templates/beat-em-up/src/hud.c +135 -0
- package/templates/beat-em-up/src/main.c +341 -0
- package/templates/beat-em-up/src/sound.c +44 -0
- package/templates/beat-em-up/src/street.c +93 -0
- package/templates/beat-em-up/src/verdict.h +523 -0
- package/templates/beat-em-up/src/view.c +362 -0
- package/templates/beat-em-up/tools/drive.py +105 -0
- package/templates/beat-em-up/tools/flickercheck.py +273 -0
- package/templates/demo/.claude/settings.json +17 -0
- package/templates/demo/.mcp.json +10 -0
- package/templates/demo/CLAUDE.md +129 -0
- package/templates/demo/Makefile +66 -0
- package/templates/demo/PLAN.md +401 -0
- package/templates/demo/README.md +322 -0
- package/templates/demo/expect.json +945 -0
- package/templates/demo/src/config.asm +80 -0
- package/templates/demo/src/framework.asm +390 -0
- package/templates/demo/src/main.asm +139 -0
- package/templates/demo/src/music.asm +197 -0
- package/templates/demo/src/part_main.asm +279 -0
- package/templates/demo/src/part_title.asm +79 -0
- package/templates/demo/src/parts.asm +43 -0
- package/templates/demo/src/tables.asm +73 -0
- package/templates/demo/src/verdict.asm +179 -0
- package/templates/demo/tools/audio.py +80 -0
- package/templates/demo/tools/gen_expect.py +167 -0
- package/templates/demo/tools/probe.py +95 -0
- package/templates/hello/.claude/settings.json +17 -0
- package/templates/hello/.mcp.json +10 -0
- package/templates/hello/CLAUDE.md +100 -0
- package/templates/hello/Makefile +38 -0
- package/templates/hello/PLAN.md +200 -0
- package/templates/hello/expect.json +20 -0
- package/templates/hello/src/main.c +161 -0
- package/templates/hello/src/sprite.asm +27 -0
- package/templates/hello-kick/.claude/settings.json +17 -0
- package/templates/hello-kick/.mcp.json +10 -0
- package/templates/hello-kick/CLAUDE.md +100 -0
- package/templates/hello-kick/Makefile +17 -0
- package/templates/hello-kick/PLAN.md +199 -0
- package/templates/hello-kick/expect.json +20 -0
- package/templates/hello-kick/src/main.asm +233 -0
- package/templates/platformer/.claude/settings.json +17 -0
- package/templates/platformer/.mcp.json +10 -0
- package/templates/platformer/CLAUDE.md +125 -0
- package/templates/platformer/Makefile +86 -0
- package/templates/platformer/PLAN.md +360 -0
- package/templates/platformer/README.md +277 -0
- package/templates/platformer/expect.json +32 -0
- package/templates/platformer/src/actors.c +289 -0
- package/templates/platformer/src/anim.c +51 -0
- package/templates/platformer/src/art.c +212 -0
- package/templates/platformer/src/autopilot.h +30 -0
- package/templates/platformer/src/engine.asm +253 -0
- package/templates/platformer/src/game.h +248 -0
- package/templates/platformer/src/hud.c +90 -0
- package/templates/platformer/src/level.c +184 -0
- package/templates/platformer/src/main.c +306 -0
- package/templates/platformer/src/player.c +268 -0
- package/templates/platformer/src/sound.c +42 -0
- package/templates/platformer/src/verdict.h +206 -0
- package/templates/platformer/src/view.c +331 -0
- package/templates/platformer/stage-expect.json +6 -0
- package/templates/platformer/tools/drive.py +112 -0
- package/templates/platformer/tools/tearcheck.py +229 -0
- package/templates/shmup-vertical/.claude/settings.json +17 -0
- package/templates/shmup-vertical/.mcp.json +10 -0
- package/templates/shmup-vertical/CLAUDE.md +137 -0
- package/templates/shmup-vertical/Makefile +131 -0
- package/templates/shmup-vertical/PLAN.md +569 -0
- package/templates/shmup-vertical/README.md +189 -0
- package/templates/shmup-vertical/expect.json +38 -0
- package/templates/shmup-vertical/src/bullets.c +158 -0
- package/templates/shmup-vertical/src/bullets.h +35 -0
- package/templates/shmup-vertical/src/display.c +239 -0
- package/templates/shmup-vertical/src/display.h +40 -0
- package/templates/shmup-vertical/src/game.h +85 -0
- package/templates/shmup-vertical/src/glyph.asm +358 -0
- package/templates/shmup-vertical/src/hiscore.c +141 -0
- package/templates/shmup-vertical/src/hiscore.h +16 -0
- package/templates/shmup-vertical/src/hit.asm +180 -0
- package/templates/shmup-vertical/src/hitbox.c +59 -0
- package/templates/shmup-vertical/src/hitbox.h +25 -0
- package/templates/shmup-vertical/src/kernel.asm +330 -0
- package/templates/shmup-vertical/src/level.c +174 -0
- package/templates/shmup-vertical/src/level.h +27 -0
- package/templates/shmup-vertical/src/main.c +824 -0
- package/templates/shmup-vertical/src/mux.asm +321 -0
- package/templates/shmup-vertical/src/sound.asm +255 -0
- package/templates/shmup-vertical/src/step.asm +169 -0
- package/templates/shmup-vertical/src/waves.c +195 -0
- package/templates/shmup-vertical/src/waves.h +33 -0
- package/templates/shmup-vertical/stage-expect.json +6 -0
- package/templates/shmup-vertical/tools/drive.py +152 -0
- package/templates/shmup-vertical/tools/joytest.py +159 -0
- package/templates/shmup-vertical/tools/meter.py +15 -0
- 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.
|