Add ghost CPUs with original Pac-Man AI targeting
Implement four ghosts (Blinky, Pinky, Inky, Clyde) with authentic Pac-Man AI: Blinky chases directly, Pinky targets 2 tiles ahead (with original up-direction bug), Inky uses vector doubling from Blinky, Clyde switches to scatter within 8-tile radius. Includes chase/scatter mode cycling, ghost house exit with staggered delays, directional sprite rendering with animation, and ghost-pacman collision detection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
94
pacman-project/adt/ghost.rkt
Normal file
94
pacman-project/adt/ghost.rkt
Normal file
@@ -0,0 +1,94 @@
|
||||
#lang r7rs
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Ghost ADT ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; A ghost has a position, direction, movement mode, and type identity.
|
||||
;; The targeting AI is handled by the level — this ADT only stores state.
|
||||
;; Each ghost instance represents one of the four ghosts (Blinky, Inky,
|
||||
;; Pinky, Clyde). Contains NO graphics code.
|
||||
|
||||
(define-library (pacman-project adt ghost)
|
||||
(import (scheme base)
|
||||
(pacman-project adt position))
|
||||
(export make-ghost)
|
||||
|
||||
(begin
|
||||
|
||||
;; make-ghost :: symbol, number, number, number, number, number -> ghost
|
||||
;; Creates a ghost of the given type at start-row/col with a scatter
|
||||
;; corner target and a house exit delay (0 = starts outside).
|
||||
(define (make-ghost ghost-type start-row start-col
|
||||
scatter-row scatter-col exit-delay)
|
||||
(let ((position (make-position start-row start-col))
|
||||
(direction 'left)
|
||||
(mode (if (= exit-delay 0) 'scatter 'in-house))
|
||||
(scatter-target (make-position scatter-row scatter-col))
|
||||
(house-timer exit-delay)
|
||||
(movement-timer 0)
|
||||
(reverse-queued? #f))
|
||||
|
||||
;; direction! :: symbol -> /
|
||||
(define (direction! new-dir)
|
||||
(set! direction new-dir))
|
||||
|
||||
;; mode! :: symbol -> /
|
||||
;; Sets the ghost mode. When switching between chase/scatter,
|
||||
;; the ghost must reverse direction (queued for next move).
|
||||
(define (mode! new-mode)
|
||||
(when (and (not (eq? mode new-mode))
|
||||
(not (eq? mode 'in-house))
|
||||
(not (eq? new-mode 'in-house)))
|
||||
(set! reverse-queued? #t))
|
||||
(set! mode new-mode))
|
||||
|
||||
;; move! :: number, number -> /
|
||||
(define (move! delta-row delta-col)
|
||||
((position 'row!) (+ (position 'row) delta-row))
|
||||
((position 'col!) (+ (position 'col) delta-col)))
|
||||
|
||||
;; consume-reverse! :: -> boolean
|
||||
;; Returns #t and clears the flag if a reverse was queued.
|
||||
(define (consume-reverse!)
|
||||
(if reverse-queued?
|
||||
(begin (set! reverse-queued? #f) #t)
|
||||
#f))
|
||||
|
||||
;; update-house-timer! :: number -> /
|
||||
;; Decreases house timer. When it reaches 0, ghost exits.
|
||||
(define (update-house-timer! delta-time)
|
||||
(when (eq? mode 'in-house)
|
||||
(set! house-timer (- house-timer delta-time))
|
||||
(when (<= house-timer 0)
|
||||
(set! mode 'scatter))))
|
||||
|
||||
;; reset-movement-timer! :: -> /
|
||||
(define (reset-movement-timer!)
|
||||
(set! movement-timer 0))
|
||||
|
||||
;; advance-movement-timer! :: number -> number
|
||||
;; Returns updated movement timer value.
|
||||
(define (advance-movement-timer! delta-time)
|
||||
(set! movement-timer (+ movement-timer delta-time))
|
||||
movement-timer)
|
||||
|
||||
;; dispatch-ghost :: symbol -> any
|
||||
(define (dispatch-ghost msg)
|
||||
(cond ((eq? msg 'position) position)
|
||||
((eq? msg 'direction) direction)
|
||||
((eq? msg 'direction!) direction!)
|
||||
((eq? msg 'type) ghost-type)
|
||||
((eq? msg 'mode) mode)
|
||||
((eq? msg 'mode!) mode!)
|
||||
((eq? msg 'move!) move!)
|
||||
((eq? msg 'scatter-target) scatter-target)
|
||||
((eq? msg 'in-house?) (eq? mode 'in-house))
|
||||
((eq? msg 'consume-reverse!) consume-reverse!)
|
||||
((eq? msg 'update-house-timer!) update-house-timer!)
|
||||
((eq? msg 'movement-timer) movement-timer)
|
||||
((eq? msg 'advance-movement-timer!) advance-movement-timer!)
|
||||
((eq? msg 'reset-movement-timer!) reset-movement-timer!)
|
||||
(else (error "Ghost ADT -- Unknown message:" msg))))
|
||||
|
||||
dispatch-ghost))))
|
||||
Reference in New Issue
Block a user