From cd70055bc75977a63cd0749ce8f097a36d7fb34a Mon Sep 17 00:00:00 2001 From: joren Date: Mon, 23 Mar 2026 11:06:32 +0100 Subject: [PATCH] Refactor(English): Rename all files and identifiers from Dutch to English MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renamed files: constanten→constants, adt-positie→adt-position, adt-doolhof→adt-maze, adt-sleutel→adt-key, adt-tijdslimiet→adt-timer, adt-teken→adt-draw, adt-spel→adt-game. All message names, variables, comments, and tests converted to English. Also fixed counter location bug (time-label x/y were swapped). Co-Authored-By: Claude Opus 4.6 --- pacman-project/adt-doolhof.rkt | 118 ---------- pacman-project/adt-draw.rkt | 248 +++++++++++++++++++++ pacman-project/adt-game.rkt | 48 +++++ pacman-project/adt-key.rkt | 55 +++++ pacman-project/adt-level.rkt | 211 +++++++++--------- pacman-project/adt-maze.rkt | 118 ++++++++++ pacman-project/adt-pacman.rkt | 54 +++-- pacman-project/adt-positie.rkt | 52 ----- pacman-project/adt-position.rkt | 50 +++++ pacman-project/adt-score.rkt | 28 +-- pacman-project/adt-sleutel.rkt | 56 ----- pacman-project/adt-spel.rkt | 49 ----- pacman-project/adt-teken.rkt | 252 ---------------------- pacman-project/adt-tijdslimiet.rkt | 63 ------ pacman-project/adt-timer.rkt | 62 ++++++ pacman-project/constanten.rkt | 135 ------------ pacman-project/constants.rkt | 134 ++++++++++++ pacman-project/spel.rkt | 8 +- pacman-project/tests/all-tests.rkt | 24 +++ pacman-project/tests/alle-testen.rkt | 25 --- pacman-project/tests/test-doolhof.rkt | 59 ----- pacman-project/tests/test-maze.rkt | 58 +++++ pacman-project/tests/test-pacman.rkt | 66 +++--- pacman-project/tests/test-positie.rkt | 57 ----- pacman-project/tests/test-position.rkt | 57 +++++ pacman-project/tests/test-score.rkt | 30 --- pacman-project/tests/test-tijdslimiet.rkt | 55 ----- pacman-project/tests/test-timer.rkt | 55 +++++ snake-wpo/adt-appel.rkt | 40 ++++ snake-wpo/adt-level.rkt | 182 ++++++++++++++++ snake-wpo/adt-positie.rkt | 91 ++++++++ snake-wpo/adt-slang-stuk.rkt | 33 +++ snake-wpo/adt-slang.rkt | 77 +++++++ snake-wpo/adt-spel.rkt | 52 +++++ snake-wpo/adt-teken.rkt | 188 ++++++++++++++++ snake-wpo/constanten.rkt | 53 +++++ snake-wpo/hulp-procedures.rkt | 39 ++++ snake-wpo/images/apple-mask.png | Bin 0 -> 649 bytes snake-wpo/images/apple.png | Bin 0 -> 673 bytes snake-wpo/images/snake-mask.png | Bin 0 -> 5252 bytes snake-wpo/images/snake.png | Bin 0 -> 4644 bytes snake-wpo/spel.rkt | 16 ++ snake-wpo/tests/alle-testen.rkt | 19 ++ snake-wpo/tests/test-appel.rkt | 27 +++ snake-wpo/tests/test-positie.rkt | 28 +++ 45 files changed, 1936 insertions(+), 1136 deletions(-) delete mode 100644 pacman-project/adt-doolhof.rkt create mode 100644 pacman-project/adt-draw.rkt create mode 100644 pacman-project/adt-game.rkt create mode 100644 pacman-project/adt-key.rkt create mode 100644 pacman-project/adt-maze.rkt delete mode 100644 pacman-project/adt-positie.rkt create mode 100644 pacman-project/adt-position.rkt delete mode 100644 pacman-project/adt-sleutel.rkt delete mode 100644 pacman-project/adt-spel.rkt delete mode 100644 pacman-project/adt-teken.rkt delete mode 100644 pacman-project/adt-tijdslimiet.rkt create mode 100644 pacman-project/adt-timer.rkt delete mode 100644 pacman-project/constanten.rkt create mode 100644 pacman-project/constants.rkt create mode 100644 pacman-project/tests/all-tests.rkt delete mode 100644 pacman-project/tests/alle-testen.rkt delete mode 100644 pacman-project/tests/test-doolhof.rkt create mode 100644 pacman-project/tests/test-maze.rkt delete mode 100644 pacman-project/tests/test-positie.rkt create mode 100644 pacman-project/tests/test-position.rkt delete mode 100644 pacman-project/tests/test-score.rkt delete mode 100644 pacman-project/tests/test-tijdslimiet.rkt create mode 100644 pacman-project/tests/test-timer.rkt create mode 100644 snake-wpo/adt-appel.rkt create mode 100644 snake-wpo/adt-level.rkt create mode 100644 snake-wpo/adt-positie.rkt create mode 100644 snake-wpo/adt-slang-stuk.rkt create mode 100644 snake-wpo/adt-slang.rkt create mode 100644 snake-wpo/adt-spel.rkt create mode 100644 snake-wpo/adt-teken.rkt create mode 100644 snake-wpo/constanten.rkt create mode 100644 snake-wpo/hulp-procedures.rkt create mode 100644 snake-wpo/images/apple-mask.png create mode 100644 snake-wpo/images/apple.png create mode 100644 snake-wpo/images/snake-mask.png create mode 100644 snake-wpo/images/snake.png create mode 100644 snake-wpo/spel.rkt create mode 100644 snake-wpo/tests/alle-testen.rkt create mode 100644 snake-wpo/tests/test-appel.rkt create mode 100644 snake-wpo/tests/test-positie.rkt diff --git a/pacman-project/adt-doolhof.rkt b/pacman-project/adt-doolhof.rkt deleted file mode 100644 index 0420ee2..0000000 --- a/pacman-project/adt-doolhof.rkt +++ /dev/null @@ -1,118 +0,0 @@ -#lang r7rs - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Doolhof ADT ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Het doolhof bevat het logische grid met cellen. Elke cel heeft een type -;; (muur, muntje, leeg, sleutel, deur). Dit ADT bevat GEEN grafische code. - -(define-library (pacman-project adt-doolhof) - (import (scheme base) - (pacman-project constanten)) - (export maak-doolhof) - - (begin - - ;; maak-doolhof :: -> doolhof - ;; Maakt een nieuw doolhof-object aan met het volledige grid. - (define (maak-doolhof) - - ;; Het doolhof grid: 31 rijen x 28 kolommen. - ;; vector wordt gebruikt omdat het mutable is (i.t.t. #()). - (define grid - (vector (vector 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) - (vector 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1) - (vector 1 0 1 1 1 1 0 1 1 1 1 1 0 1 1 0 1 1 1 1 1 0 1 1 1 1 0 1) - (vector 1 0 1 0 0 1 0 1 1 1 1 1 0 1 1 0 1 1 1 1 1 0 1 1 1 1 0 1) - (vector 1 0 1 4 1 1 0 1 1 1 1 1 0 1 1 0 1 1 1 1 1 0 1 1 1 1 0 1) - (vector 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1) - (vector 1 0 1 1 1 1 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 0 1) - (vector 1 0 1 1 1 1 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 0 1) - (vector 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 1) - (vector 1 1 1 1 1 1 0 1 1 1 1 1 0 1 1 0 1 1 1 1 1 0 1 1 1 1 1 1) - (vector 2 2 2 2 2 1 0 1 1 1 1 1 0 1 1 0 1 1 1 1 1 0 1 2 2 2 2 2) - (vector 2 2 2 2 2 1 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 1 2 2 2 2 2) - (vector 2 2 2 2 2 1 0 1 1 0 1 1 1 4 4 1 1 1 0 1 1 0 1 2 2 2 2 2) - (vector 1 1 1 1 1 1 0 1 1 0 1 2 2 2 2 2 2 1 0 1 1 0 1 1 1 1 1 1) - (vector 2 0 0 0 0 0 0 0 0 0 1 2 2 2 2 2 2 1 0 0 0 0 0 0 0 0 0 2) - (vector 1 1 1 1 1 1 0 1 1 0 1 2 2 2 2 2 2 1 0 1 1 0 1 1 1 1 1 1) - (vector 2 2 2 2 2 1 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 2 2 2 2 2) - (vector 2 2 2 2 2 1 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 1 2 2 2 2 2) - (vector 2 2 2 2 2 1 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 2 2 2 2 2) - (vector 1 1 1 1 1 1 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 1 1) - (vector 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1) - (vector 1 0 1 1 1 1 0 1 1 1 1 1 0 1 1 0 1 1 1 1 1 0 1 1 1 1 0 1) - (vector 1 0 1 1 1 1 0 1 1 1 1 1 0 1 1 0 1 1 1 1 1 0 1 1 1 1 0 1) - (vector 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1) - (vector 1 1 1 0 1 1 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 1 0 1 1 1) - (vector 1 1 1 0 1 1 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 1 0 1 1 1) - (vector 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 4 1 0 0 0 0 0 0 1) - (vector 1 0 1 1 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 0 0 1 1 1 1 1 0 1) - (vector 1 0 1 1 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 1 1 1 1 1 1 0 1) - (vector 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1) - (vector 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1))) - - (define aantal-rijen (vector-length grid)) - (define aantal-kolommen (vector-length (vector-ref grid 0))) - - ;; cel-ref :: number, number -> number - ;; Geeft het celtype terug op de opgegeven positie. - (define (cel-ref rij kolom) - (vector-ref (vector-ref grid rij) kolom)) - - ;; cel-set! :: number, number, number -> / - ;; Past het celtype aan op de opgegeven positie. - (define (cel-set! rij kolom waarde) - (vector-set! (vector-ref grid rij) kolom waarde)) - - ;; muur? :: number, number -> boolean - (define (muur? rij kolom) - (= (cel-ref rij kolom) cel-type-muur)) - - ;; muntje? :: number, number -> boolean - (define (muntje? rij kolom) - (= (cel-ref rij kolom) cel-type-muntje)) - - ;; leeg? :: number, number -> boolean - (define (leeg? rij kolom) - (= (cel-ref rij kolom) cel-type-leeg)) - - ;; sleutel? :: number, number -> boolean - (define (sleutel? rij kolom) - (= (cel-ref rij kolom) cel-type-sleutel)) - - ;; deur? :: number, number -> boolean - (define (deur? rij kolom) - (= (cel-ref rij kolom) cel-type-deur)) - - ;; verwijder-deur! :: number, number -> / - ;; Verwijdert een deur uit het grid (maakt de cel leeg). - (define (verwijder-deur! rij kolom) - (cel-set! rij kolom cel-type-leeg)) - - ;; voor-elke-cel :: (number, number, number -> /) -> / - ;; Itereert over alle cellen en roept de callback op met rij, kolom en celtype. - (define (voor-elke-cel callback) - (do ((rij 0 (+ rij 1))) - ((= rij aantal-rijen)) - (do ((kolom 0 (+ kolom 1))) - ((= kolom aantal-kolommen)) - (callback rij kolom (cel-ref rij kolom))))) - - ;; dispatch-doolhof :: symbol -> any - (define (dispatch-doolhof msg) - (cond ((eq? msg 'rijen) aantal-rijen) - ((eq? msg 'kolommen) aantal-kolommen) - ((eq? msg 'cel-ref) cel-ref) - ((eq? msg 'cel-set!) cel-set!) - ((eq? msg 'muur?) muur?) - ((eq? msg 'muntje?) muntje?) - ((eq? msg 'leeg?) leeg?) - ((eq? msg 'sleutel?) sleutel?) - ((eq? msg 'deur?) deur?) - ((eq? msg 'verwijder-deur!) verwijder-deur!) - ((eq? msg 'voor-elke-cel) voor-elke-cel) - (else (error "Doolhof ADT -- Onbekend bericht:" msg)))) - - dispatch-doolhof))) diff --git a/pacman-project/adt-draw.rkt b/pacman-project/adt-draw.rkt new file mode 100644 index 0000000..e6eb8ee --- /dev/null +++ b/pacman-project/adt-draw.rkt @@ -0,0 +1,248 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Draw ADT ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; All graphics logic is isolated in this ADT. Game logic knows nothing about +;; pixels, windows, or sprites. Grid-to-pixel conversion happens exclusively +;; here. + +(define-library (pacman-project adt-draw) + (import (scheme base) + (pp1 graphics) + (pacman-project constants)) + (export make-draw) + + (begin + + ;; make-draw :: number, number -> draw + ;; Creates the draw object that handles all rendering. + (define (make-draw width height) + (let ((window (make-window width height "Pacman"))) + + ((window 'set-background!) "black") + + ;; + ;; Layers (order determines draw order) + ;; + + (define maze-layer ((window 'new-layer!))) + (define coins-layer ((window 'new-layer!))) + (define key-layer ((window 'new-layer!))) + (define pacman-layer ((window 'new-layer!))) + (define ui-layer ((window 'new-layer!))) + (define pause-layer ((window 'new-layer!))) + + ;; + ;; Maze tiles + ;; + + (define maze-tile (make-tile width height)) + ((maze-layer 'add-drawable!) maze-tile) + + ;; + ;; Coins tile + ;; + + (define coins-tile (make-tile width height)) + ((coins-layer 'add-drawable!) coins-tile) + + ;; + ;; Key sprite (in the maze) + ;; + + (define key-sprite (make-bitmap-tile "pacman-sprites/key.png")) + ((key-sprite 'set-scale!) sprite-scale-key) + ((key-layer 'add-drawable!) key-sprite) + + ;; Key UI indicator (next to the score) + (define key-ui-sprite (make-bitmap-tile "pacman-sprites/key.png")) + ((key-ui-sprite 'set-scale!) sprite-scale-key-ui) + ((key-ui-sprite 'set-x!) key-ui-x) + ((key-ui-sprite 'set-y!) key-ui-y) + + ;; + ;; Pac-Man sprite + ;; + + (define pacman-bitmap-tiles + (list (make-bitmap-tile "pacman-sprites/pacman-death-1.png") + (make-bitmap-tile "pacman-sprites/pacman-closed.png") + (make-bitmap-tile "pacman-sprites/pacman-open.png"))) + + (define pacman-sprite (make-tile-sequence pacman-bitmap-tiles)) + ((pacman-sprite 'set-scale!) sprite-scale-pacman) + ((pacman-layer 'add-drawable!) pacman-sprite) + + ;; Animation state + (define time-since-last-animation 0) + + ;; + ;; UI tiles + ;; + + (define ui-tile (make-tile width height)) + ((ui-layer 'add-drawable!) ui-tile) + + ;; + ;; Coordinate conversion + ;; + + ;; grid->pixel-x :: number -> number + (define (grid->pixel-x col) + (* cell-size-px col)) + + ;; grid->pixel-y :: number -> number + (define (grid->pixel-y row) + (+ (* row cell-size-px) maze-offset-y)) + + ;; + ;; Draw functions + ;; + + ;; draw-maze! :: maze -> / + ;; Draws all walls and doors. + (define (draw-maze! maze) + ((maze 'for-each-cell) + (lambda (row col cell-type) + (cond + ((= cell-type cell-type-wall) + ((maze-tile 'draw-rectangle!) + (grid->pixel-x col) + (grid->pixel-y row) + (- cell-size-px maze-wall-shrink) + (- cell-size-px maze-wall-shrink) + "blue")) + ((= cell-type cell-type-door) + ((maze-tile 'draw-rectangle!) + (grid->pixel-x col) + (grid->pixel-y row) + (- cell-size-px maze-wall-shrink) + (- cell-size-px maze-wall-shrink) + "pink")))))) + + ;; draw-coins! :: maze -> / + ;; Draws all coins in the maze. + (define (draw-coins! maze) + ((coins-tile 'clear!)) + ((maze 'for-each-cell) + (lambda (row col cell-type) + (when (= cell-type cell-type-coin) + ((coins-tile 'draw-rectangle!) + (+ (grid->pixel-x col) coin-inset) + (+ (grid->pixel-y row) coin-inset) + (- cell-size-px (* 2 coin-inset) 6) + (- cell-size-px (* 2 coin-inset) 6) + "yellow"))))) + + ;; draw-key! :: key -> / + ;; Draws the key at its position, or shows it in UI if taken. + (define (draw-key! key-obj) + (if (key-obj 'taken?) + (begin + ((key-layer 'remove-drawable!) key-sprite) + ((key-layer 'add-drawable!) key-ui-sprite)) + (let ((pos (key-obj 'position))) + ((key-sprite 'set-x!) (grid->pixel-x (pos 'col))) + ((key-sprite 'set-y!) (grid->pixel-y (pos 'row)))))) + + ;; draw-pacman! :: pacman, number -> / + ;; Draws Pac-Man at its current position with correct rotation. + (define (draw-pacman! pacman delta-time) + (let* ((pos (pacman 'position)) + (direction (pacman 'direction))) + ;; Set position + ((pacman-sprite 'set-x!) (grid->pixel-x (pos 'col))) + ((pacman-sprite 'set-y!) (grid->pixel-y (pos 'row))) + ;; Set rotation based on direction + (cond ((eq? direction 'right) ((pacman-sprite 'rotate!) rotation-right)) + ((eq? direction 'left) ((pacman-sprite 'rotate!) rotation-left)) + ((eq? direction 'up) ((pacman-sprite 'rotate!) rotation-up)) + ((eq? direction 'down) ((pacman-sprite 'rotate!) rotation-down))) + ;; Animation + (set! time-since-last-animation (+ time-since-last-animation delta-time)) + (when (>= time-since-last-animation animation-interval-ms) + ((pacman-sprite 'set-next!)) + (set! time-since-last-animation 0)))) + + ;; draw-ui! :: score, timer -> / + ;; Draws the score and time limit on screen. + (define (draw-ui! score timer) + ((ui-tile 'clear!)) + ;; Score + ((ui-tile 'draw-text!) + (number->string (score 'points)) + score-text-size score-text-x score-text-y "white") + ;; Separator line + ((ui-tile 'draw-rectangle!) + separator-x 0 separator-width height "white") + ;; Time limit + ((ui-tile 'draw-text!) + "Time remaining:" time-text-size time-label-x time-label-y "white") + ((ui-tile 'draw-text!) + ((timer 'format-time)) + score-text-size time-value-x time-value-y "white")) + + ;; draw-pause! :: boolean -> / + ;; Shows or hides the pause screen. + (define (draw-pause! paused?) + ((pause-layer 'empty!)) + (when paused? + (let ((pause-tile (make-tile width height))) + ((pause-layer 'add-drawable!) pause-tile) + ((pause-tile 'draw-rectangle!) 0 90 670 height "black") + ((pause-tile 'draw-text!) "Game Paused" 40 200 400 "red")))) + + ;; redraw-maze! :: maze -> / + ;; Redraws the maze (after door removal). + (define (redraw-maze! maze) + ((maze-tile 'clear!)) + (draw-maze! maze)) + + ;; + ;; Main draw function + ;; + + ;; draw-game! :: game -> / + ;; Draws the full game (registered as draw callback). + (define (draw-game! game) + (let ((level (game 'level))) + (draw-pacman! (level 'pacman) 0) + (draw-key! (level 'key)) + (draw-coins! (level 'maze)) + (draw-ui! (level 'score) (level 'timer)) + (draw-pause! (level 'paused?)))) + + ;; + ;; Callback registration + ;; + + ;; set-game-loop! :: (number -> /) -> / + (define (set-game-loop! fun) + ((window 'set-update-callback!) fun)) + + ;; set-key-callback! :: (symbol, any -> /) -> / + (define (set-key-callback! fun) + ((window 'set-key-callback!) fun)) + + ;; start-drawing! :: game -> / + ;; Starts drawing by setting the draw callback. + (define (start-drawing! game) + ;; Initial maze and coins draw (one-time) + (draw-maze! ((game 'level) 'maze)) + (draw-coins! ((game 'level) 'maze)) + ((window 'set-draw-callback!) + (lambda () (draw-game! game)))) + + ;; + ;; Dispatch + ;; + + (define (dispatch-draw msg) + (cond ((eq? msg 'set-game-loop!) set-game-loop!) + ((eq? msg 'set-key-callback!) set-key-callback!) + ((eq? msg 'start-drawing!) start-drawing!) + (else (error "Draw ADT -- Unknown message:" msg)))) + + dispatch-draw)))) diff --git a/pacman-project/adt-game.rkt b/pacman-project/adt-game.rkt new file mode 100644 index 0000000..17db6f5 --- /dev/null +++ b/pacman-project/adt-game.rkt @@ -0,0 +1,48 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Game ADT ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Top-level game object that connects the level (logic) with the draw ADT +;; (graphics). Registers callbacks for the game loop, keys, and drawing. + +(define-library (pacman-project adt-game) + (import (scheme base) + (pacman-project constants) + (pacman-project adt-level) + (pacman-project adt-draw)) + (export make-game) + + (begin + + ;; make-game :: -> game + (define (make-game) + (let ((level (make-level)) + (draw (make-draw window-width-px window-height-px))) + + ;; key-handler :: symbol, any -> / + ;; Processes key presses and forwards them to the level. + (define (key-handler status key) + (when (eq? status 'pressed) + ((level 'key-press!) key))) + + ;; game-loop :: number -> / + ;; Called each frame for game state updates. + (define (game-loop delta-time) + ((level 'update!) delta-time)) + + ;; start! :: -> / + ;; Starts the game by registering all callbacks. + (define (start!) + ((draw 'set-game-loop!) game-loop) + ((draw 'set-key-callback!) key-handler) + ((draw 'start-drawing!) dispatch-game)) + + ;; dispatch-game :: symbol -> any + (define (dispatch-game msg) + (cond ((eq? msg 'start!) start!) + ((eq? msg 'level) level) + (else (error "Game ADT -- Unknown message:" msg)))) + + dispatch-game)))) diff --git a/pacman-project/adt-key.rkt b/pacman-project/adt-key.rkt new file mode 100644 index 0000000..850b5b9 --- /dev/null +++ b/pacman-project/adt-key.rkt @@ -0,0 +1,55 @@ +#lang r7rs + +(#%require (only racket/base random)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Key ADT ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; The key is placed at a random coin position in the maze. When Pac-Man +;; picks it up, doors can be opened. Contains NO graphics code. + +(define-library (pacman-project adt-key) + (import (scheme base) + (pacman-project constants) + (pacman-project adt-position)) + (export make-key) + + (begin + + ;; make-key :: maze -> key + ;; Creates a key and places it at a random coin position. + (define (make-key maze) + (let ((position #f) + (taken? #f)) + + ;; place-random! :: -> / + ;; Places the key on a random cell that contains a coin. + (define (place-random!) + (let loop ((attempts 0)) + (if (>= attempts max-placement-attempts) + (error "No valid position found for key") + (let ((col (random 0 (maze 'cols))) + (row (random 0 (maze 'rows)))) + (if ((maze 'coin?) row col) + (begin + (set! position (make-position row col)) + ((maze 'cell-set!) row col cell-type-key)) + (loop (+ attempts 1))))))) + + ;; take! :: -> / + ;; Marks the key as taken. + (define (take!) + (set! taken? #t)) + + ;; Initialization: place the key immediately on creation. + (place-random!) + + ;; dispatch-key :: symbol -> any + (define (dispatch-key msg) + (cond ((eq? msg 'position) position) + ((eq? msg 'taken?) taken?) + ((eq? msg 'take!) take!) + (else (error "Key ADT -- Unknown message:" msg)))) + + dispatch-key)))) diff --git a/pacman-project/adt-level.rkt b/pacman-project/adt-level.rkt index 815da99..0e26772 100644 --- a/pacman-project/adt-level.rkt +++ b/pacman-project/adt-level.rkt @@ -4,172 +4,171 @@ ;; Level ADT ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Het level bevat alle spellogica: beweging van Pac-Man, botsingsdetectie, -;; muntjes eten, sleutel oppakken, deuren openen, teleportatie, pauze en -;; tijdsbeheer. Bevat GEEN grafische code. +;; Contains all game logic: Pac-Man movement, collision detection, coin/key +;; pickup, door opening, teleportation, pause, and time management. +;; Contains NO graphics code. (define-library (pacman-project adt-level) (import (scheme base) - (pacman-project constanten) - (pacman-project adt-positie) - (pacman-project adt-doolhof) + (pacman-project constants) + (pacman-project adt-position) + (pacman-project adt-maze) (pacman-project adt-pacman) - (pacman-project adt-sleutel) + (pacman-project adt-key) (pacman-project adt-score) - (pacman-project adt-tijdslimiet)) - (export maak-level) + (pacman-project adt-timer)) + (export make-level) (begin - ;; maak-level :: -> level - ;; Maakt een nieuw level aan met alle spelobjecten. - (define (maak-level) - (let ((doolhof (maak-doolhof)) - (pacman (maak-pacman 5 2)) - (sleutel #f) - (score (maak-score)) - (tijdslimiet (maak-tijdslimiet)) - (gepauzeerd? #f)) + ;; make-level :: -> level + ;; Creates a new level with all game objects. + (define (make-level) + (let ((maze (make-maze)) + (pacman (make-pacman 5 2)) + (key #f) + (score (make-score)) + (timer (make-timer)) + (paused? #f)) - ;; Initialiseer de sleutel nadat het doolhof is aangemaakt. - (set! sleutel (maak-sleutel doolhof)) + ;; Initialize key after maze is created. + (set! key (make-key maze)) ;; - ;; Richting helpers + ;; Direction helpers ;; - ;; richting->delta :: symbol -> (number . number) - ;; Converteert een richting naar een (delta-rij . delta-kolom) paar. - (define (richting->delta richting) - (cond ((eq? richting 'rechts) (cons 0 1)) - ((eq? richting 'links) (cons 0 -1)) - ((eq? richting 'omhoog) (cons -1 0)) - ((eq? richting 'omlaag) (cons 1 0)) + ;; direction->delta :: symbol -> (number . number) + ;; Converts a direction to a (delta-row . delta-col) pair. + (define (direction->delta direction) + (cond ((eq? direction 'right) (cons 0 1)) + ((eq? direction 'left) (cons 0 -1)) + ((eq? direction 'up) (cons -1 0)) + ((eq? direction 'down) (cons 1 0)) (else (cons 0 0)))) ;; - ;; Muntje logica + ;; Coin logic ;; - ;; eet-muntje! :: number, number -> / - ;; Verwijdert het muntje op de cel en past score/tijd aan. - (define (eet-muntje! rij kolom) - ((doolhof 'cel-set!) rij kolom cel-type-leeg) - ((score 'verhoog!)) - ((tijdslimiet 'verhoog!))) + ;; eat-coin! :: number, number -> / + ;; Removes the coin at the cell and updates score/time. + (define (eat-coin! row col) + ((maze 'cell-set!) row col cell-type-empty) + ((score 'increase!)) + ((timer 'increase!))) ;; - ;; Sleutel logica + ;; Key logic ;; - ;; pak-sleutel-op! :: number, number -> / - ;; Pakt de sleutel op en maakt de cel leeg. - (define (pak-sleutel-op! rij kolom) - ((doolhof 'cel-set!) rij kolom cel-type-leeg) - ((sleutel 'pak-op!))) + ;; pick-up-key! :: number, number -> / + ;; Picks up the key and clears the cell. + (define (pick-up-key! row col) + ((maze 'cell-set!) row col cell-type-empty) + ((key 'take!))) ;; - ;; Teleportatie logica + ;; Teleportation logic ;; - ;; teleporteer-horizontaal! :: number, number -> / - ;; Teleporteert Pac-Man naar de andere kant van het doolhof. - (define (teleporteer-horizontaal! rij kolom) - (let ((pac-pos (pacman 'positie))) - (cond ((< kolom 0) - ((pac-pos 'kolom!) (- (doolhof 'kolommen) 1)) - ((pac-pos 'rij!) rij)) - ((>= kolom (doolhof 'kolommen)) - ((pac-pos 'kolom!) 0) - ((pac-pos 'rij!) rij))))) + ;; teleport-horizontal! :: number, number -> / + ;; Teleports Pac-Man to the other side of the maze. + (define (teleport-horizontal! row col) + (let ((pac-pos (pacman 'position))) + (cond ((< col 0) + ((pac-pos 'col!) (- (maze 'cols) 1)) + ((pac-pos 'row!) row)) + ((>= col (maze 'cols)) + ((pac-pos 'col!) 0) + ((pac-pos 'row!) row))))) ;; - ;; Bewegingslogica + ;; Movement logic ;; - ;; beweeg-pacman! :: symbol -> / - ;; Beweegt Pac-Man in de opgegeven richting met alle spelregels. - (define (beweeg-pacman! richting) - (when (not ((tijdslimiet 'tijd-op?))) - (let* ((delta (richting->delta richting)) - (delta-rij (car delta)) - (delta-kolom (cdr delta)) - (huidige-pos (pacman 'positie)) - (volgende-rij (+ (huidige-pos 'rij) delta-rij)) - (volgende-kolom (+ (huidige-pos 'kolom) delta-kolom))) + ;; move-pacman! :: symbol -> / + ;; Moves Pac-Man in the given direction with all game rules. + (define (move-pacman! direction) + (when (not ((timer 'time-up?))) + (let* ((delta (direction->delta direction)) + (delta-row (car delta)) + (delta-col (cdr delta)) + (current-pos (pacman 'position)) + (next-row (+ (current-pos 'row) delta-row)) + (next-col (+ (current-pos 'col) delta-col))) - ;; Pas richting aan voor de teken-laag. - ((pacman 'richting!) richting) + ;; Update direction for the draw layer. + ((pacman 'direction!) direction) (cond - ;; Teleportatie: buiten het grid horizontaal. - ((or (< volgende-kolom 0) (>= volgende-kolom (doolhof 'kolommen))) - (teleporteer-horizontaal! volgende-rij volgende-kolom)) + ;; Teleportation: outside grid horizontally. + ((or (< next-col 0) (>= next-col (maze 'cols))) + (teleport-horizontal! next-row next-col)) - ;; Deur: open alleen als de sleutel opgepakt is. - (((doolhof 'deur?) volgende-rij volgende-kolom) - (when (sleutel 'opgepakt?) - ((doolhof 'verwijder-deur!) volgende-rij volgende-kolom))) + ;; Door: only open if key has been taken. + (((maze 'door?) next-row next-col) + (when (key 'taken?) + ((maze 'remove-door!) next-row next-col))) - ;; Normale beweging: alleen als het geen muur is. + ;; Normal movement: only if not a wall. (else - (when (not ((doolhof 'muur?) volgende-rij volgende-kolom)) - ((pacman 'beweeg!) delta-rij delta-kolom) - ;; Controleer wat er op de nieuwe positie staat. + (when (not ((maze 'wall?) next-row next-col)) + ((pacman 'move!) delta-row delta-col) + ;; Check what's at the new position. (cond - (((doolhof 'sleutel?) volgende-rij volgende-kolom) - (pak-sleutel-op! volgende-rij volgende-kolom)) - (((doolhof 'muntje?) volgende-rij volgende-kolom) - (eet-muntje! volgende-rij volgende-kolom))))))))) + (((maze 'key?) next-row next-col) + (pick-up-key! next-row next-col)) + (((maze 'coin?) next-row next-col) + (eat-coin! next-row next-col))))))))) ;; - ;; Pauze logica + ;; Pause logic ;; - ;; wissel-pauze! :: -> / - ;; Wisselt de pauzetoestand. - (define (wissel-pauze!) - (set! gepauzeerd? (not gepauzeerd?))) + ;; toggle-pause! :: -> / + (define (toggle-pause!) + (set! paused? (not paused?))) ;; - ;; Toets afhandeling + ;; Key handling ;; - ;; toets! :: symbol -> / - ;; Verwerkt een toetsaanslag. - (define (toets! toets) + ;; key-press! :: symbol -> / + ;; Processes a key press. + (define (key-press! pressed-key) (cond - ((eq? toets 'escape) (wissel-pauze!)) - ((not gepauzeerd?) + ((eq? pressed-key 'escape) (toggle-pause!)) + ((not paused?) (cond - ((eq? toets 'right) (beweeg-pacman! 'rechts)) - ((eq? toets 'left) (beweeg-pacman! 'links)) - ((eq? toets 'up) (beweeg-pacman! 'omhoog)) - ((eq? toets 'down) (beweeg-pacman! 'omlaag)))))) + ((eq? pressed-key 'right) (move-pacman! 'right)) + ((eq? pressed-key 'left) (move-pacman! 'left)) + ((eq? pressed-key 'up) (move-pacman! 'up)) + ((eq? pressed-key 'down) (move-pacman! 'down)))))) ;; - ;; Update (spellusfunctie) + ;; Update (game loop function) ;; ;; update! :: number -> / - ;; Wordt elk frame aangeroepen met het aantal verstreken milliseconden. - (define (update! delta-tijd) - (when (not gepauzeerd?) - ((tijdslimiet 'verlaag!) delta-tijd))) + ;; Called each frame with elapsed milliseconds. + (define (update! delta-time) + (when (not paused?) + ((timer 'decrease!) delta-time))) ;; ;; Dispatch ;; (define (dispatch-level msg) - (cond ((eq? msg 'doolhof) doolhof) + (cond ((eq? msg 'maze) maze) ((eq? msg 'pacman) pacman) - ((eq? msg 'sleutel) sleutel) + ((eq? msg 'key) key) ((eq? msg 'score) score) - ((eq? msg 'tijdslimiet) tijdslimiet) - ((eq? msg 'gepauzeerd?) gepauzeerd?) - ((eq? msg 'toets!) toets!) + ((eq? msg 'timer) timer) + ((eq? msg 'paused?) paused?) + ((eq? msg 'key-press!) key-press!) ((eq? msg 'update!) update!) - (else (error "Level ADT -- Onbekend bericht:" msg)))) + (else (error "Level ADT -- Unknown message:" msg)))) dispatch-level)))) diff --git a/pacman-project/adt-maze.rkt b/pacman-project/adt-maze.rkt new file mode 100644 index 0000000..076196b --- /dev/null +++ b/pacman-project/adt-maze.rkt @@ -0,0 +1,118 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Maze ADT ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; The maze contains the logical grid with cells. Each cell has a type +;; (wall, coin, empty, key, door). This ADT contains NO graphics code. + +(define-library (pacman-project adt-maze) + (import (scheme base) + (pacman-project constants)) + (export make-maze) + + (begin + + ;; make-maze :: -> maze + ;; Creates a new maze object with the full grid. + (define (make-maze) + + ;; The maze grid: 31 rows x 28 columns. + ;; vector is used because it is mutable (unlike #()). + (define grid + (vector (vector 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1) + (vector 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1) + (vector 1 0 1 1 1 1 0 1 1 1 1 1 0 1 1 0 1 1 1 1 1 0 1 1 1 1 0 1) + (vector 1 0 1 0 0 1 0 1 1 1 1 1 0 1 1 0 1 1 1 1 1 0 1 1 1 1 0 1) + (vector 1 0 1 4 1 1 0 1 1 1 1 1 0 1 1 0 1 1 1 1 1 0 1 1 1 1 0 1) + (vector 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1) + (vector 1 0 1 1 1 1 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 0 1) + (vector 1 0 1 1 1 1 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 0 1) + (vector 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 1) + (vector 1 1 1 1 1 1 0 1 1 1 1 1 0 1 1 0 1 1 1 1 1 0 1 1 1 1 1 1) + (vector 2 2 2 2 2 1 0 1 1 1 1 1 0 1 1 0 1 1 1 1 1 0 1 2 2 2 2 2) + (vector 2 2 2 2 2 1 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 1 2 2 2 2 2) + (vector 2 2 2 2 2 1 0 1 1 0 1 1 1 4 4 1 1 1 0 1 1 0 1 2 2 2 2 2) + (vector 1 1 1 1 1 1 0 1 1 0 1 2 2 2 2 2 2 1 0 1 1 0 1 1 1 1 1 1) + (vector 2 0 0 0 0 0 0 0 0 0 1 2 2 2 2 2 2 1 0 0 0 0 0 0 0 0 0 2) + (vector 1 1 1 1 1 1 0 1 1 0 1 2 2 2 2 2 2 1 0 1 1 0 1 1 1 1 1 1) + (vector 2 2 2 2 2 1 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 2 2 2 2 2) + (vector 2 2 2 2 2 1 0 1 1 0 0 0 0 0 0 0 0 0 0 1 1 0 1 2 2 2 2 2) + (vector 2 2 2 2 2 1 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 2 2 2 2 2) + (vector 1 1 1 1 1 1 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 1 1) + (vector 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1) + (vector 1 0 1 1 1 1 0 1 1 1 1 1 0 1 1 0 1 1 1 1 1 0 1 1 1 1 0 1) + (vector 1 0 1 1 1 1 0 1 1 1 1 1 0 1 1 0 1 1 1 1 1 0 1 1 1 1 0 1) + (vector 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1) + (vector 1 1 1 0 1 1 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 1 0 1 1 1) + (vector 1 1 1 0 1 1 0 1 1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 1 0 1 1 1) + (vector 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 4 1 0 0 0 0 0 0 1) + (vector 1 0 1 1 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 0 0 1 1 1 1 1 0 1) + (vector 1 0 1 1 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 1 1 1 1 1 1 0 1) + (vector 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1) + (vector 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1))) + + (define num-rows (vector-length grid)) + (define num-cols (vector-length (vector-ref grid 0))) + + ;; cell-ref :: number, number -> number + ;; Returns the cell type at the given position. + (define (cell-ref row col) + (vector-ref (vector-ref grid row) col)) + + ;; cell-set! :: number, number, number -> / + ;; Sets the cell type at the given position. + (define (cell-set! row col value) + (vector-set! (vector-ref grid row) col value)) + + ;; wall? :: number, number -> boolean + (define (wall? row col) + (= (cell-ref row col) cell-type-wall)) + + ;; coin? :: number, number -> boolean + (define (coin? row col) + (= (cell-ref row col) cell-type-coin)) + + ;; empty? :: number, number -> boolean + (define (empty? row col) + (= (cell-ref row col) cell-type-empty)) + + ;; key? :: number, number -> boolean + (define (key? row col) + (= (cell-ref row col) cell-type-key)) + + ;; door? :: number, number -> boolean + (define (door? row col) + (= (cell-ref row col) cell-type-door)) + + ;; remove-door! :: number, number -> / + ;; Removes a door from the grid (makes the cell empty). + (define (remove-door! row col) + (cell-set! row col cell-type-empty)) + + ;; for-each-cell :: (number, number, number -> /) -> / + ;; Iterates over all cells, calling callback with row, col, cell-type. + (define (for-each-cell callback) + (do ((row 0 (+ row 1))) + ((= row num-rows)) + (do ((col 0 (+ col 1))) + ((= col num-cols)) + (callback row col (cell-ref row col))))) + + ;; dispatch-maze :: symbol -> any + (define (dispatch-maze msg) + (cond ((eq? msg 'rows) num-rows) + ((eq? msg 'cols) num-cols) + ((eq? msg 'cell-ref) cell-ref) + ((eq? msg 'cell-set!) cell-set!) + ((eq? msg 'wall?) wall?) + ((eq? msg 'coin?) coin?) + ((eq? msg 'empty?) empty?) + ((eq? msg 'key?) key?) + ((eq? msg 'door?) door?) + ((eq? msg 'remove-door!) remove-door!) + ((eq? msg 'for-each-cell) for-each-cell) + (else (error "Maze ADT -- Unknown message:" msg)))) + + dispatch-maze))) diff --git a/pacman-project/adt-pacman.rkt b/pacman-project/adt-pacman.rkt index cc050b6..fb72188 100644 --- a/pacman-project/adt-pacman.rkt +++ b/pacman-project/adt-pacman.rkt @@ -4,45 +4,43 @@ ;; Pac-Man ADT ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Het Pac-Man ADT beheert de logische toestand van de speler: positie op het -;; grid en de huidige richting. Bevat GEEN grafische code. +;; Manages the logical state of the player: grid position and current +;; direction. Contains NO graphics code. (define-library (pacman-project adt-pacman) (import (scheme base) - (pacman-project adt-positie)) - (export maak-pacman) + (pacman-project adt-position)) + (export make-pacman) (begin - ;; maak-pacman :: number, number -> pacman - ;; Maakt een Pac-Man object aan op de opgegeven startpositie (rij, kolom). - (define (maak-pacman start-rij start-kolom) - (let ((positie (maak-positie start-rij start-kolom)) - (richting 'rechts)) + ;; make-pacman :: number, number -> pacman + ;; Creates a Pac-Man object at the given start position (row, col). + (define (make-pacman start-row start-col) + (let ((position (make-position start-row start-col)) + (direction 'right)) - ;; positie! :: positie -> / - ;; Vervangt de huidige positie. - (define (positie! nieuwe-positie) - (set! positie nieuwe-positie)) + ;; position! :: position -> / + (define (position! new-position) + (set! position new-position)) - ;; richting! :: symbol -> / - ;; Past de huidige richting aan. - (define (richting! nieuwe-richting) - (set! richting nieuwe-richting)) + ;; direction! :: symbol -> / + (define (direction! new-direction) + (set! direction new-direction)) - ;; beweeg! :: number, number -> / - ;; Verplaatst Pac-Man met een delta op het grid. - (define (beweeg! delta-rij delta-kolom) - ((positie 'rij!) (+ (positie 'rij) delta-rij)) - ((positie 'kolom!) (+ (positie 'kolom) delta-kolom))) + ;; move! :: number, number -> / + ;; Moves Pac-Man by a delta on the grid. + (define (move! delta-row delta-col) + ((position 'row!) (+ (position 'row) delta-row)) + ((position 'col!) (+ (position 'col) delta-col))) ;; dispatch-pacman :: symbol -> any (define (dispatch-pacman msg) - (cond ((eq? msg 'positie) positie) - ((eq? msg 'positie!) positie!) - ((eq? msg 'richting) richting) - ((eq? msg 'richting!) richting!) - ((eq? msg 'beweeg!) beweeg!) - (else (error "Pac-Man ADT -- Onbekend bericht:" msg)))) + (cond ((eq? msg 'position) position) + ((eq? msg 'position!) position!) + ((eq? msg 'direction) direction) + ((eq? msg 'direction!) direction!) + ((eq? msg 'move!) move!) + (else (error "Pac-Man ADT -- Unknown message:" msg)))) dispatch-pacman)))) diff --git a/pacman-project/adt-positie.rkt b/pacman-project/adt-positie.rkt deleted file mode 100644 index ef9af86..0000000 --- a/pacman-project/adt-positie.rkt +++ /dev/null @@ -1,52 +0,0 @@ -#lang r7rs - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Positie ADT ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Een positie stelt een locatie voor op het logische grid van het doolhof. -;; Coordinaten zijn in grid-eenheden (rij, kolom), NIET in pixels. - -(define-library (pacman-project adt-positie) - (import (scheme base)) - (export maak-positie) - - (begin - - ;; maak-positie :: number, number -> positie - ;; Maakt een nieuw positie-object aan met rij en kolom op het grid. - (define (maak-positie rij kolom) - - ;; rij! :: number -> / - ;; Past de rij-coordinaat aan. - (define (rij! nieuwe-rij) - (set! rij nieuwe-rij)) - - ;; kolom! :: number -> / - ;; Past de kolom-coordinaat aan. - (define (kolom! nieuwe-kolom) - (set! kolom nieuwe-kolom)) - - ;; vergelijk? :: positie -> boolean - ;; Controleert of twee posities dezelfde coordinaten hebben. - (define (vergelijk? andere-positie) - (and (= rij (andere-positie 'rij)) - (= kolom (andere-positie 'kolom)))) - - ;; beweeg :: number, number -> positie - ;; Geeft een nieuwe positie terug verschoven met delta-rij en delta-kolom. - (define (beweeg delta-rij delta-kolom) - (maak-positie (+ rij delta-rij) - (+ kolom delta-kolom))) - - ;; dispatch-positie :: symbol -> any - (define (dispatch-positie msg) - (cond ((eq? msg 'rij) rij) - ((eq? msg 'kolom) kolom) - ((eq? msg 'rij!) rij!) - ((eq? msg 'kolom!) kolom!) - ((eq? msg 'vergelijk?) vergelijk?) - ((eq? msg 'beweeg) beweeg) - (else (error "Positie ADT -- Onbekend bericht:" msg)))) - - dispatch-positie))) diff --git a/pacman-project/adt-position.rkt b/pacman-project/adt-position.rkt new file mode 100644 index 0000000..abe9724 --- /dev/null +++ b/pacman-project/adt-position.rkt @@ -0,0 +1,50 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Position ADT ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; A position represents a location on the logical maze grid. +;; Coordinates are in grid units (row, col), NOT pixels. + +(define-library (pacman-project adt-position) + (import (scheme base)) + (export make-position) + + (begin + + ;; make-position :: number, number -> position + ;; Creates a new position object with row and column on the grid. + (define (make-position row col) + + ;; row! :: number -> / + (define (row! new-row) + (set! row new-row)) + + ;; col! :: number -> / + (define (col! new-col) + (set! col new-col)) + + ;; equal? :: position -> boolean + ;; Checks whether two positions have the same coordinates. + (define (equal? other) + (and (= row (other 'row)) + (= col (other 'col)))) + + ;; move :: number, number -> position + ;; Returns a new position shifted by delta-row and delta-col. + (define (move delta-row delta-col) + (make-position (+ row delta-row) + (+ col delta-col))) + + ;; dispatch-position :: symbol -> any + (define (dispatch-position msg) + (cond ((eq? msg 'row) row) + ((eq? msg 'col) col) + ((eq? msg 'row!) row!) + ((eq? msg 'col!) col!) + ((eq? msg 'equal?) equal?) + ((eq? msg 'move) move) + (else (error "Position ADT -- Unknown message:" msg)))) + + dispatch-position))) diff --git a/pacman-project/adt-score.rkt b/pacman-project/adt-score.rkt index 15584e4..7757276 100644 --- a/pacman-project/adt-score.rkt +++ b/pacman-project/adt-score.rkt @@ -4,29 +4,29 @@ ;; Score ADT ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Houdt de score bij. Bevat GEEN grafische code. +;; Tracks the player's score. Contains NO graphics code. (define-library (pacman-project adt-score) (import (scheme base) - (pacman-project constanten)) - (export maak-score) + (pacman-project constants)) + (export make-score) (begin - ;; maak-score :: -> score - ;; Maakt een nieuw score-object aan, startend bij 0. - (define (maak-score) - (let ((punten 0)) + ;; make-score :: -> score + ;; Creates a new score object, starting at 0. + (define (make-score) + (let ((points 0)) - ;; verhoog! :: -> / - ;; Verhoogt de score met het aantal punten per muntje. - (define (verhoog!) - (set! punten (+ punten punten-per-muntje))) + ;; increase! :: -> / + ;; Increases the score by points-per-coin. + (define (increase!) + (set! points (+ points points-per-coin))) ;; dispatch-score :: symbol -> any (define (dispatch-score msg) - (cond ((eq? msg 'punten) punten) - ((eq? msg 'verhoog!) verhoog!) - (else (error "Score ADT -- Onbekend bericht:" msg)))) + (cond ((eq? msg 'points) points) + ((eq? msg 'increase!) increase!) + (else (error "Score ADT -- Unknown message:" msg)))) dispatch-score)))) diff --git a/pacman-project/adt-sleutel.rkt b/pacman-project/adt-sleutel.rkt deleted file mode 100644 index 61f73b8..0000000 --- a/pacman-project/adt-sleutel.rkt +++ /dev/null @@ -1,56 +0,0 @@ -#lang r7rs - -(#%require (only racket/base random)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Sleutel ADT ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; De sleutel is een object dat op een willekeurige positie in het doolhof -;; geplaatst wordt. Wanneer Pac-Man de sleutel oppakt, worden deuren geopend. -;; Bevat GEEN grafische code. - -(define-library (pacman-project adt-sleutel) - (import (scheme base) - (pacman-project constanten) - (pacman-project adt-positie)) - (export maak-sleutel) - - (begin - - ;; maak-sleutel :: doolhof -> sleutel - ;; Maakt een sleutel aan en plaatst deze op een willekeurige muntje-positie. - (define (maak-sleutel doolhof) - (let ((positie #f) - (opgepakt? #f)) - - ;; plaats-willekeurig! :: -> / - ;; Plaatst de sleutel op een willekeurige cel waar een muntje staat. - (define (plaats-willekeurig!) - (let loop ((pogingen 0)) - (if (>= pogingen max-plaatsing-pogingen) - (error "Geen geldige positie gevonden voor sleutel") - (let ((kolom (random 0 (doolhof 'kolommen))) - (rij (random 0 (doolhof 'rijen)))) - (if ((doolhof 'muntje?) rij kolom) - (begin - (set! positie (maak-positie rij kolom)) - ((doolhof 'cel-set!) rij kolom cel-type-sleutel)) - (loop (+ pogingen 1))))))) - - ;; pak-op! :: -> / - ;; Markeert de sleutel als opgepakt. - (define (pak-op!) - (set! opgepakt? #t)) - - ;; Initialisatie: plaats de sleutel direct bij aanmaak. - (plaats-willekeurig!) - - ;; dispatch-sleutel :: symbol -> any - (define (dispatch-sleutel msg) - (cond ((eq? msg 'positie) positie) - ((eq? msg 'opgepakt?) opgepakt?) - ((eq? msg 'pak-op!) pak-op!) - (else (error "Sleutel ADT -- Onbekend bericht:" msg)))) - - dispatch-sleutel)))) diff --git a/pacman-project/adt-spel.rkt b/pacman-project/adt-spel.rkt deleted file mode 100644 index 2b254e5..0000000 --- a/pacman-project/adt-spel.rkt +++ /dev/null @@ -1,49 +0,0 @@ -#lang r7rs - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Spel ADT ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Het top-level spel-object dat het level verbindt met het teken-ADT. -;; Registreert de callbacks voor de spellus, toetsen en tekenen. - -(define-library (pacman-project adt-spel) - (import (scheme base) - (pacman-project constanten) - (pacman-project adt-level) - (pacman-project adt-teken)) - (export maak-spel) - - (begin - - ;; maak-spel :: -> spel - ;; Maakt het spel-object aan. - (define (maak-spel) - (let ((level (maak-level)) - (teken (maak-teken venster-breedte-px venster-hoogte-px))) - - ;; toets-procedure :: symbol, any -> / - ;; Verwerkt toetsaanslagen en stuurt ze door naar het level. - (define (toets-procedure status toets) - (when (eq? status 'pressed) - ((level 'toets!) toets))) - - ;; spel-lus-procedure :: number -> / - ;; Wordt elk frame aangeroepen voor game-state updates. - (define (spel-lus-procedure delta-tijd) - ((level 'update!) delta-tijd)) - - ;; start! :: -> / - ;; Start het spel door alle callbacks te registreren. - (define (start!) - ((teken 'set-spel-lus-functie!) spel-lus-procedure) - ((teken 'set-toets-functie!) toets-procedure) - ((teken 'start-tekenen!) dispatch-spel)) - - ;; dispatch-spel :: symbol -> any - (define (dispatch-spel msg) - (cond ((eq? msg 'start!) start!) - ((eq? msg 'level) level) - (else (error "Spel ADT -- Onbekend bericht:" msg)))) - - dispatch-spel)))) diff --git a/pacman-project/adt-teken.rkt b/pacman-project/adt-teken.rkt deleted file mode 100644 index 7826a94..0000000 --- a/pacman-project/adt-teken.rkt +++ /dev/null @@ -1,252 +0,0 @@ -#lang r7rs - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Teken ADT ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Alle grafische logica is geisoleerd in dit ADT. De spellogica kent niets -;; over pixels, vensters of sprites. De omzetting van grid-coordinaten naar -;; schermcoordinaten gebeurt uitsluitend hier. - -(define-library (pacman-project adt-teken) - (import (scheme base) - (pp1 graphics) - (pacman-project constanten)) - (export maak-teken) - - (begin - - ;; maak-teken :: number, number -> teken - ;; Maakt het teken-object aan dat alle grafische weergave verzorgt. - (define (maak-teken breedte hoogte) - (let ((venster (make-window breedte hoogte "Pacman"))) - - ((venster 'set-background!) "black") - - ;; - ;; Lagen aanmaken (volgorde bepaalt tekenvolgorde) - ;; - - (define doolhof-laag ((venster 'new-layer!))) - (define muntjes-laag ((venster 'new-layer!))) - (define sleutel-laag ((venster 'new-layer!))) - (define pacman-laag ((venster 'new-layer!))) - (define ui-laag ((venster 'new-layer!))) - (define pauze-laag ((venster 'new-layer!))) - - ;; - ;; Doolhof tiles - ;; - - (define doolhof-tile (make-tile breedte hoogte)) - ((doolhof-laag 'add-drawable!) doolhof-tile) - - ;; - ;; Muntjes tile - ;; - - (define muntjes-tile (make-tile breedte hoogte)) - ((muntjes-laag 'add-drawable!) muntjes-tile) - - ;; - ;; Sleutel sprite (in het doolhof) - ;; - - (define sleutel-sprite (make-bitmap-tile "pacman-sprites/key.png")) - ((sleutel-sprite 'set-scale!) sprite-schaal-sleutel) - ((sleutel-laag 'add-drawable!) sleutel-sprite) - - ;; Sleutel UI indicator (naast de score) - (define sleutel-ui-sprite (make-bitmap-tile "pacman-sprites/key.png")) - ((sleutel-ui-sprite 'set-scale!) sprite-schaal-sleutel-ui) - ((sleutel-ui-sprite 'set-x!) sleutel-ui-x) - ((sleutel-ui-sprite 'set-y!) sleutel-ui-y) - - ;; - ;; Pac-Man sprite - ;; - - (define pacman-bitmap-tiles - (list (make-bitmap-tile "pacman-sprites/pacman-death-1.png") - (make-bitmap-tile "pacman-sprites/pacman-closed.png") - (make-bitmap-tile "pacman-sprites/pacman-open.png"))) - - (define pacman-sprite (make-tile-sequence pacman-bitmap-tiles)) - ((pacman-sprite 'set-scale!) sprite-schaal-pacman) - ((pacman-laag 'add-drawable!) pacman-sprite) - - ;; Animatie state - (define tijd-sinds-laatste-animatie 0) - - ;; - ;; UI tiles - ;; - - (define ui-tile (make-tile breedte hoogte)) - ((ui-laag 'add-drawable!) ui-tile) - - ;; - ;; Coordinaat conversie - ;; - - ;; grid->pixel-x :: number -> number - ;; Converteert een grid kolom naar een pixel x-coordinaat. - (define (grid->pixel-x kolom) - (* cel-grootte-px kolom)) - - ;; grid->pixel-y :: number -> number - ;; Converteert een grid rij naar een pixel y-coordinaat. - (define (grid->pixel-y rij) - (+ (* rij cel-grootte-px) doolhof-offset-y)) - - ;; - ;; Teken functies - ;; - - ;; teken-doolhof! :: doolhof -> / - ;; Tekent alle muren en deuren van het doolhof. - (define (teken-doolhof! doolhof) - ((doolhof 'voor-elke-cel) - (lambda (rij kolom celtype) - (cond - ((= celtype cel-type-muur) - ((doolhof-tile 'draw-rectangle!) - (grid->pixel-x kolom) - (grid->pixel-y rij) - (- cel-grootte-px doolhof-muur-krimp) - (- cel-grootte-px doolhof-muur-krimp) - "blue")) - ((= celtype cel-type-deur) - ((doolhof-tile 'draw-rectangle!) - (grid->pixel-x kolom) - (grid->pixel-y rij) - (- cel-grootte-px doolhof-muur-krimp) - (- cel-grootte-px doolhof-muur-krimp) - "pink")))))) - - ;; teken-muntjes! :: doolhof -> / - ;; Tekent alle muntjes in het doolhof. - (define (teken-muntjes! doolhof) - ((muntjes-tile 'clear!)) - ((doolhof 'voor-elke-cel) - (lambda (rij kolom celtype) - (when (= celtype cel-type-muntje) - ((muntjes-tile 'draw-rectangle!) - (+ (grid->pixel-x kolom) muntje-inset) - (+ (grid->pixel-y rij) muntje-inset) - (- cel-grootte-px (* 2 muntje-inset) 6) - (- cel-grootte-px (* 2 muntje-inset) 6) - "yellow"))))) - - ;; teken-sleutel! :: sleutel -> / - ;; Tekent de sleutel op haar positie, of verbergt ze als opgepakt. - (define (teken-sleutel! sleutel) - (if (sleutel 'opgepakt?) - (begin - ((sleutel-laag 'remove-drawable!) sleutel-sprite) - ((sleutel-laag 'add-drawable!) sleutel-ui-sprite)) - (let ((pos (sleutel 'positie))) - ((sleutel-sprite 'set-x!) (grid->pixel-x (pos 'kolom))) - ((sleutel-sprite 'set-y!) (grid->pixel-y (pos 'rij)))))) - - ;; teken-pacman! :: pacman, number -> / - ;; Tekent Pac-Man op zijn huidige positie met de juiste rotatie. - (define (teken-pacman! pacman delta-tijd) - (let* ((pos (pacman 'positie)) - (richting (pacman 'richting))) - ;; Positie instellen - ((pacman-sprite 'set-x!) (grid->pixel-x (pos 'kolom))) - ((pacman-sprite 'set-y!) (grid->pixel-y (pos 'rij))) - ;; Rotatie instellen op basis van richting - (cond ((eq? richting 'rechts) ((pacman-sprite 'rotate!) rotatie-rechts)) - ((eq? richting 'links) ((pacman-sprite 'rotate!) rotatie-links)) - ((eq? richting 'omhoog) ((pacman-sprite 'rotate!) rotatie-omhoog)) - ((eq? richting 'omlaag) ((pacman-sprite 'rotate!) rotatie-omlaag))) - ;; Animatie - (set! tijd-sinds-laatste-animatie (+ tijd-sinds-laatste-animatie delta-tijd)) - (when (>= tijd-sinds-laatste-animatie animatie-interval-ms) - ((pacman-sprite 'set-next!)) - (set! tijd-sinds-laatste-animatie 0)))) - - ;; teken-ui! :: score, tijdslimiet -> / - ;; Tekent de score en de tijdslimiet op het scherm. - (define (teken-ui! score tijdslimiet) - ((ui-tile 'clear!)) - ;; Score - ((ui-tile 'draw-text!) - (number->string (score 'punten)) - score-tekst-grootte score-tekst-x score-tekst-y "white") - ;; Scheidingslijn - ((ui-tile 'draw-rectangle!) - scheidingslijn-x 0 scheidingslijn-breedte hoogte "white") - ;; Tijdslimiet - ((ui-tile 'draw-text!) - "Time remaining:" tijd-tekst-grootte tijd-label-x tijd-label-y "white") - ((ui-tile 'draw-text!) - ((tijdslimiet 'formatteer-tijd)) - score-tekst-grootte tijd-waarde-x tijd-waarde-y "white")) - - ;; teken-pauze! :: boolean -> / - ;; Toont of verbergt het pauzescherm. - (define (teken-pauze! gepauzeerd?) - ((pauze-laag 'empty!)) - (when gepauzeerd? - (let ((pauze-tile (make-tile breedte hoogte))) - ((pauze-laag 'add-drawable!) pauze-tile) - ((pauze-tile 'draw-rectangle!) 0 90 670 hoogte "black") - ((pauze-tile 'draw-text!) "Game Paused" 40 200 400 "red")))) - - ;; herteken-doolhof! :: doolhof -> / - ;; Hertekent het doolhof (na deur verwijdering). - (define (herteken-doolhof! doolhof) - ((doolhof-tile 'clear!)) - (teken-doolhof! doolhof)) - - ;; - ;; Hoofdtekenfunctie - ;; - - ;; teken-spel! :: spel -> / - ;; Tekent het volledige spel (wordt als draw-callback geregistreerd). - (define (teken-spel! spel) - (let ((level (spel 'level))) - (teken-pacman! (level 'pacman) 0) - (teken-sleutel! (level 'sleutel)) - (teken-muntjes! (level 'doolhof)) - (teken-ui! (level 'score) (level 'tijdslimiet)) - (teken-pauze! (level 'gepauzeerd?)))) - - ;; - ;; Callbacks instellen - ;; - - ;; set-spel-lus-functie! :: (number -> /) -> / - ;; Registreert de update-callback. - (define (set-spel-lus-functie! fun) - ((venster 'set-update-callback!) fun)) - - ;; set-toets-functie! :: (symbol, any -> /) -> / - ;; Registreert de keyboard-callback. - (define (set-toets-functie! fun) - ((venster 'set-key-callback!) fun)) - - ;; start-tekenen! :: spel -> / - ;; Start het tekenen door de draw-callback in te stellen. - (define (start-tekenen! spel) - ;; Initieel doolhof en muntjes tekenen (eenmalig) - (teken-doolhof! ((spel 'level) 'doolhof)) - (teken-muntjes! ((spel 'level) 'doolhof)) - ((venster 'set-draw-callback!) - (lambda () (teken-spel! spel)))) - - ;; - ;; Dispatch - ;; - - (define (dispatch-teken msg) - (cond ((eq? msg 'set-spel-lus-functie!) set-spel-lus-functie!) - ((eq? msg 'set-toets-functie!) set-toets-functie!) - ((eq? msg 'start-tekenen!) start-tekenen!) - (else (error "Teken ADT -- Onbekend bericht:" msg)))) - - dispatch-teken)))) diff --git a/pacman-project/adt-tijdslimiet.rkt b/pacman-project/adt-tijdslimiet.rkt deleted file mode 100644 index 8a553ce..0000000 --- a/pacman-project/adt-tijdslimiet.rkt +++ /dev/null @@ -1,63 +0,0 @@ -#lang r7rs - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Tijdslimiet ADT ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Beheert de aftellende tijdslimiet van het spel. Bevat GEEN grafische code. - -(define-library (pacman-project adt-tijdslimiet) - (import (scheme base) - (pacman-project constanten)) - (export maak-tijdslimiet) - - (begin - - ;; maak-tijdslimiet :: -> tijdslimiet - ;; Maakt een nieuw tijdslimiet-object aan. - (define (maak-tijdslimiet) - (let ((resterende-tijd start-tijd-seconden) - (tijd-sinds-laatste-tick 0)) - - ;; verlaag! :: number -> / - ;; Verlaagt de tijd op basis van het aantal verstreken milliseconden. - (define (verlaag! ms) - (set! tijd-sinds-laatste-tick (+ tijd-sinds-laatste-tick ms)) - (when (>= tijd-sinds-laatste-tick ms-per-seconde) - (set! tijd-sinds-laatste-tick 0) - (when (> resterende-tijd 0) - (set! resterende-tijd (- resterende-tijd 1))))) - - ;; verhoog! :: -> / - ;; Verhoogt de resterende tijd met een bonus (bij het eten van een muntje). - (define (verhoog!) - (set! resterende-tijd (+ resterende-tijd tijd-bonus-per-muntje))) - - ;; tijd-op? :: -> boolean - ;; Controleert of de tijd verstreken is. - (define (tijd-op?) - (= resterende-tijd 0)) - - ;; formatteer-tijd :: -> string - ;; Geeft de resterende tijd terug als "m:ss" string. - (define (formatteer-tijd) - (let* ((minuten (quotient resterende-tijd 60)) - (seconden (remainder resterende-tijd 60)) - (min-str (number->string minuten)) - (sec-str (number->string seconden))) - (string-append min-str - ":" - (if (< seconden 10) - (string-append "0" sec-str) - sec-str)))) - - ;; dispatch-tijdslimiet :: symbol -> any - (define (dispatch-tijdslimiet msg) - (cond ((eq? msg 'resterende-tijd) resterende-tijd) - ((eq? msg 'verlaag!) verlaag!) - ((eq? msg 'verhoog!) verhoog!) - ((eq? msg 'tijd-op?) tijd-op?) - ((eq? msg 'formatteer-tijd) formatteer-tijd) - (else (error "Tijdslimiet ADT -- Onbekend bericht:" msg)))) - - dispatch-tijdslimiet)))) diff --git a/pacman-project/adt-timer.rkt b/pacman-project/adt-timer.rkt new file mode 100644 index 0000000..796ce67 --- /dev/null +++ b/pacman-project/adt-timer.rkt @@ -0,0 +1,62 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Timer ADT ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Manages the countdown time limit. Contains NO graphics code. + +(define-library (pacman-project adt-timer) + (import (scheme base) + (pacman-project constants)) + (export make-timer) + + (begin + + ;; make-timer :: -> timer + ;; Creates a new timer object. + (define (make-timer) + (let ((remaining-time start-time-seconds) + (time-since-last-tick 0)) + + ;; decrease! :: number -> / + ;; Decreases time based on elapsed milliseconds. + (define (decrease! ms) + (set! time-since-last-tick (+ time-since-last-tick ms)) + (when (>= time-since-last-tick ms-per-second) + (set! time-since-last-tick 0) + (when (> remaining-time 0) + (set! remaining-time (- remaining-time 1))))) + + ;; increase! :: -> / + ;; Adds a time bonus (when eating a coin). + (define (increase!) + (set! remaining-time (+ remaining-time time-bonus-per-coin))) + + ;; time-up? :: -> boolean + (define (time-up?) + (= remaining-time 0)) + + ;; format-time :: -> string + ;; Returns remaining time as "m:ss" string. + (define (format-time) + (let* ((minutes (quotient remaining-time 60)) + (seconds (remainder remaining-time 60)) + (min-str (number->string minutes)) + (sec-str (number->string seconds))) + (string-append min-str + ":" + (if (< seconds 10) + (string-append "0" sec-str) + sec-str)))) + + ;; dispatch-timer :: symbol -> any + (define (dispatch-timer msg) + (cond ((eq? msg 'remaining-time) remaining-time) + ((eq? msg 'decrease!) decrease!) + ((eq? msg 'increase!) increase!) + ((eq? msg 'time-up?) time-up?) + ((eq? msg 'format-time) format-time) + (else (error "Timer ADT -- Unknown message:" msg)))) + + dispatch-timer)))) diff --git a/pacman-project/constanten.rkt b/pacman-project/constanten.rkt deleted file mode 100644 index 79ba0f4..0000000 --- a/pacman-project/constanten.rkt +++ /dev/null @@ -1,135 +0,0 @@ -#lang r7rs - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Constanten ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Alle configuratieconstanten voor het Pac-Man spel worden hier gedefinieerd. -;; Dit voorkomt "magic constants" doorheen de code en maakt het spel flexibel -;; aanpasbaar. - -(define-library (pacman-project constanten) - (import (scheme base)) - (export ;; Venster - venster-breedte-px - venster-hoogte-px - - ;; Doolhof grid - cel-grootte-px - doolhof-offset-y - doolhof-muur-krimp - - ;; Cel types - cel-type-muntje - cel-type-muur - cel-type-leeg - cel-type-sleutel - cel-type-deur - - ;; Muntje weergave - muntje-inset - - ;; Sprites - sprite-schaal-pacman - sprite-schaal-sleutel - sprite-schaal-sleutel-ui - - ;; Animatie - animatie-interval-ms - - ;; Score - punten-per-muntje - - ;; Tijdslimiet - start-tijd-seconden - ms-per-seconde - tijd-bonus-per-muntje - - ;; Sleutel plaatsing - max-plaatsing-pogingen - - ;; Rotatie hoeken - rotatie-rechts - rotatie-links - rotatie-omhoog - rotatie-omlaag - - ;; UI posities - score-tekst-grootte - score-tekst-x - score-tekst-y - tijd-tekst-grootte - tijd-label-x - tijd-label-y - tijd-waarde-x - tijd-waarde-y - scheidingslijn-x - scheidingslijn-breedte - sleutel-ui-x - sleutel-ui-y) - - (begin - - ;; Venster dimensies - (define venster-breedte-px 1000) - (define venster-hoogte-px 830) - - ;; Doolhof grid configuratie - (define cel-grootte-px 24) - (define doolhof-offset-y 97) - (define doolhof-muur-krimp 6) - - ;; Cel type encodering voor het doolhof grid - (define cel-type-muntje 0) - (define cel-type-muur 1) - (define cel-type-leeg 2) - (define cel-type-sleutel 3) - (define cel-type-deur 4) - - ;; Muntje weergave: inset in pixels ten opzichte van cel rand - (define muntje-inset 7) - - ;; Sprite schaalfactoren - (define sprite-schaal-pacman 1.5) - (define sprite-schaal-sleutel 1.5) - (define sprite-schaal-sleutel-ui 3) - - ;; Animatie timing - (define animatie-interval-ms 100) - - ;; Score configuratie - (define punten-per-muntje 10) - - ;; Tijdslimiet configuratie - (define start-tijd-seconden 60) - (define ms-per-seconde 1000) - (define tijd-bonus-per-muntje 1) - - ;; Sleutel plaatsing - (define max-plaatsing-pogingen 1000) - - ;; Rotatie hoeken (graden) - (define rotatie-rechts 0) - (define rotatie-links 180) - (define rotatie-omhoog 90) - (define rotatie-omlaag -90) - - ;; UI posities voor score weergave - (define score-tekst-grootte 40) - (define score-tekst-x 560) - (define score-tekst-y 20) - - ;; UI posities voor tijd weergave - (define tijd-tekst-grootte 35) - (define tijd-label-x 300) - (define tijd-label-y 710) - (define tijd-waarde-x 400) - (define tijd-waarde-y 800) - - ;; Scheidingslijn tussen speelveld en UI - (define scheidingslijn-x 670) - (define scheidingslijn-breedte 24) - - ;; Sleutel UI positie (naast score) - (define sleutel-ui-x 20) - (define sleutel-ui-y 35))) diff --git a/pacman-project/constants.rkt b/pacman-project/constants.rkt new file mode 100644 index 0000000..552a169 --- /dev/null +++ b/pacman-project/constants.rkt @@ -0,0 +1,134 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Constants ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; All configuration constants for the Pac-Man game. Prevents magic numbers +;; throughout the codebase and makes the game easily configurable. + +(define-library (pacman-project constants) + (import (scheme base)) + (export ;; Window + window-width-px + window-height-px + + ;; Maze grid + cell-size-px + maze-offset-y + maze-wall-shrink + + ;; Cell types + cell-type-coin + cell-type-wall + cell-type-empty + cell-type-key + cell-type-door + + ;; Coin rendering + coin-inset + + ;; Sprites + sprite-scale-pacman + sprite-scale-key + sprite-scale-key-ui + + ;; Animation + animation-interval-ms + + ;; Score + points-per-coin + + ;; Time limit + start-time-seconds + ms-per-second + time-bonus-per-coin + + ;; Key placement + max-placement-attempts + + ;; Rotation angles + rotation-right + rotation-left + rotation-up + rotation-down + + ;; UI positions + score-text-size + score-text-x + score-text-y + time-text-size + time-label-x + time-label-y + time-value-x + time-value-y + separator-x + separator-width + key-ui-x + key-ui-y) + + (begin + + ;; Window dimensions + (define window-width-px 1000) + (define window-height-px 830) + + ;; Maze grid configuration + (define cell-size-px 24) + (define maze-offset-y 97) + (define maze-wall-shrink 6) + + ;; Cell type encoding for the maze grid + (define cell-type-coin 0) + (define cell-type-wall 1) + (define cell-type-empty 2) + (define cell-type-key 3) + (define cell-type-door 4) + + ;; Coin rendering: inset in pixels from cell edge + (define coin-inset 7) + + ;; Sprite scale factors + (define sprite-scale-pacman 1.5) + (define sprite-scale-key 1.5) + (define sprite-scale-key-ui 3) + + ;; Animation timing + (define animation-interval-ms 100) + + ;; Score configuration + (define points-per-coin 10) + + ;; Time limit configuration + (define start-time-seconds 60) + (define ms-per-second 1000) + (define time-bonus-per-coin 1) + + ;; Key placement + (define max-placement-attempts 1000) + + ;; Rotation angles (degrees) + (define rotation-right 0) + (define rotation-left 180) + (define rotation-up 90) + (define rotation-down -90) + + ;; UI positions for score display + (define score-text-size 40) + (define score-text-x 560) + (define score-text-y 20) + + ;; UI positions for time display (right side of separator) + (define time-text-size 35) + (define time-label-x 710) + (define time-label-y 300) + (define time-value-x 800) + (define time-value-y 400) + + ;; Separator line between play field and UI + (define separator-x 670) + (define separator-width 24) + + ;; Key UI position (next to score) + (define key-ui-x 20) + (define key-ui-y 35))) diff --git a/pacman-project/spel.rkt b/pacman-project/spel.rkt index 784b466..e3dc26a 100644 --- a/pacman-project/spel.rkt +++ b/pacman-project/spel.rkt @@ -1,11 +1,11 @@ #lang r7rs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Spel Opstarten ;; +;; Start Game ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (import (scheme base) - (pacman-project adt-spel)) + (pacman-project adt-game)) -(define spel (maak-spel)) -((spel 'start!)) +(define game (make-game)) +((game 'start!)) diff --git a/pacman-project/tests/all-tests.rkt b/pacman-project/tests/all-tests.rkt new file mode 100644 index 0000000..2efb134 --- /dev/null +++ b/pacman-project/tests/all-tests.rkt @@ -0,0 +1,24 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; All Tests ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Runs all ADT tests. Open this file and evaluate to run everything. + +(import (scheme base) + (pp1 tests) + (prefix (pacman-project tests test-position) position:) + (prefix (pacman-project tests test-maze) maze:) + (prefix (pacman-project tests test-pacman) pacman:) + (prefix (pacman-project tests test-score) score:) + (prefix (pacman-project tests test-timer) timer:)) + +(define (test-all) + (position:test) + (maze:test) + (pacman:test) + (score:test) + (timer:test)) + +(test-all) diff --git a/pacman-project/tests/alle-testen.rkt b/pacman-project/tests/alle-testen.rkt deleted file mode 100644 index 7e631ad..0000000 --- a/pacman-project/tests/alle-testen.rkt +++ /dev/null @@ -1,25 +0,0 @@ -#lang r7rs - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Alle Testen ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Voert alle ADT-testen uit. Open dit bestand en evalueer om alle testen -;; tegelijkertijd te draaien. - -(import (scheme base) - (pp1 tests) - (prefix (pacman-project tests test-positie) positie:) - (prefix (pacman-project tests test-doolhof) doolhof:) - (prefix (pacman-project tests test-pacman) pacman:) - (prefix (pacman-project tests test-score) score:) - (prefix (pacman-project tests test-tijdslimiet) tijdslimiet:)) - -(define (test-alles) - (positie:test) - (doolhof:test) - (pacman:test) - (score:test) - (tijdslimiet:test)) - -(test-alles) diff --git a/pacman-project/tests/test-doolhof.rkt b/pacman-project/tests/test-doolhof.rkt deleted file mode 100644 index 5b9b671..0000000 --- a/pacman-project/tests/test-doolhof.rkt +++ /dev/null @@ -1,59 +0,0 @@ -#lang r7rs - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Tests: Doolhof ADT ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(define-library (pacman-project tests test-doolhof) - (import (scheme base) - (pp1 tests) - (pacman-project constanten) - (pacman-project adt-doolhof)) - (export test) - - (begin - - ;; Test dimensies - (define (test-dimensies) - (define d (maak-doolhof)) - (check-eq? (d 'rijen) 31 "Doolhof moet 31 rijen hebben") - (check-eq? (d 'kolommen) 28 "Doolhof moet 28 kolommen hebben")) - - ;; Test muur detectie (rij 0 is volledig muur) - (define (test-muur) - (define d (maak-doolhof)) - (check ((d 'muur?) 0 0) "Cel (0,0) moet een muur zijn") - (check ((d 'muur?) 0 14) "Cel (0,14) moet een muur zijn")) - - ;; Test muntje detectie - (define (test-muntje) - (define d (maak-doolhof)) - (check ((d 'muntje?) 1 1) "Cel (1,1) moet een muntje zijn") - (check (not ((d 'muntje?) 0 0)) "Cel (0,0) mag geen muntje zijn")) - - ;; Test deur detectie - (define (test-deur) - (define d (maak-doolhof)) - (check ((d 'deur?) 4 3) "Cel (4,3) moet een deur zijn") - (check (not ((d 'deur?) 1 1)) "Cel (1,1) mag geen deur zijn")) - - ;; Test cel-set! en verwijder-deur! - (define (test-mutatie) - (define d (maak-doolhof)) - ((d 'verwijder-deur!) 4 3) - (check ((d 'leeg?) 4 3) "Cel (4,3) moet leeg zijn na verwijder-deur!") - (check (not ((d 'deur?) 4 3)) "Cel (4,3) mag geen deur meer zijn")) - - ;; Test dat Pac-Man niet door muur kan (muur blokkeert) - (define (test-muur-blokkade) - (define d (maak-doolhof)) - ;; Pac-Man staat op (1,1) en wil naar links (1,0) -> dat is een muur - (check ((d 'muur?) 1 0) "Cel (1,0) is een muur, Pac-Man kan niet door")) - - (define (test) - (run-test test-dimensies "Doolhof: dimensies") - (run-test test-muur "Doolhof: muur detectie") - (run-test test-muntje "Doolhof: muntje detectie") - (run-test test-deur "Doolhof: deur detectie") - (run-test test-mutatie "Doolhof: cel mutatie") - (run-test test-muur-blokkade "Doolhof: muur blokkeert beweging")))) diff --git a/pacman-project/tests/test-maze.rkt b/pacman-project/tests/test-maze.rkt new file mode 100644 index 0000000..b743404 --- /dev/null +++ b/pacman-project/tests/test-maze.rkt @@ -0,0 +1,58 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Tests: Maze ADT ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-library (pacman-project tests test-maze) + (import (scheme base) + (pp1 tests) + (pacman-project constants) + (pacman-project adt-maze)) + (export test) + + (begin + + ;; Test dimensions + (define (test-dimensions) + (define m (make-maze)) + (check-eq? (m 'rows) 31 "Maze should have 31 rows") + (check-eq? (m 'cols) 28 "Maze should have 28 cols")) + + ;; Test wall detection (row 0 is all walls) + (define (test-wall) + (define m (make-maze)) + (check ((m 'wall?) 0 0) "Cell (0,0) should be a wall") + (check ((m 'wall?) 0 14) "Cell (0,14) should be a wall")) + + ;; Test coin detection + (define (test-coin) + (define m (make-maze)) + (check ((m 'coin?) 1 1) "Cell (1,1) should be a coin") + (check (not ((m 'coin?) 0 0)) "Cell (0,0) should not be a coin")) + + ;; Test door detection + (define (test-door) + (define m (make-maze)) + (check ((m 'door?) 4 3) "Cell (4,3) should be a door") + (check (not ((m 'door?) 1 1)) "Cell (1,1) should not be a door")) + + ;; Test cell-set! and remove-door! + (define (test-mutation) + (define m (make-maze)) + ((m 'remove-door!) 4 3) + (check ((m 'empty?) 4 3) "Cell (4,3) should be empty after remove-door!") + (check (not ((m 'door?) 4 3)) "Cell (4,3) should no longer be a door")) + + ;; Test that wall blocks movement + (define (test-wall-blocks) + (define m (make-maze)) + (check ((m 'wall?) 1 0) "Cell (1,0) is a wall, Pac-Man cannot pass")) + + (define (test) + (run-test test-dimensions "Maze: dimensions") + (run-test test-wall "Maze: wall detection") + (run-test test-coin "Maze: coin detection") + (run-test test-door "Maze: door detection") + (run-test test-mutation "Maze: cell mutation") + (run-test test-wall-blocks "Maze: wall blocks movement")))) diff --git a/pacman-project/tests/test-pacman.rkt b/pacman-project/tests/test-pacman.rkt index c097bda..4e6befc 100644 --- a/pacman-project/tests/test-pacman.rkt +++ b/pacman-project/tests/test-pacman.rkt @@ -1,51 +1,51 @@ #lang r7rs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Tests: Pac-Man ADT ;; +;; Tests: Pac-Man ADT ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define-library (pacman-project tests test-pacman) (import (scheme base) (pp1 tests) - (pacman-project adt-positie) + (pacman-project adt-position) (pacman-project adt-pacman)) (export test) (begin - ;; Test aanmaak en startpositie - (define (test-aanmaak) - (define pac (maak-pacman 5 2)) - (define pos (pac 'positie)) - (check-eq? (pos 'rij) 5 "Start rij moet 5 zijn") - (check-eq? (pos 'kolom) 2 "Start kolom moet 2 zijn")) + ;; Test creation and start position + (define (test-creation) + (define pac (make-pacman 5 2)) + (define pos (pac 'position)) + (check-eq? (pos 'row) 5 "Start row should be 5") + (check-eq? (pos 'col) 2 "Start col should be 2")) - ;; Test richting - (define (test-richting) - (define pac (maak-pacman 5 2)) - (check-eq? (pac 'richting) 'rechts "Startrichting moet rechts zijn") - ((pac 'richting!) 'links) - (check-eq? (pac 'richting) 'links "Richting moet links zijn na richting!")) + ;; Test direction + (define (test-direction) + (define pac (make-pacman 5 2)) + (check-eq? (pac 'direction) 'right "Start direction should be right") + ((pac 'direction!) 'left) + (check-eq? (pac 'direction) 'left "Direction should be left after direction!")) - ;; Test beweeg! - (define (test-beweeg) - (define pac (maak-pacman 5 2)) - ((pac 'beweeg!) 0 1) - (define pos (pac 'positie)) - (check-eq? (pos 'kolom) 3 "Kolom moet 3 zijn na 1 stap rechts") - (check-eq? (pos 'rij) 5 "Rij ongewijzigd na horizontale beweging")) + ;; Test move! + (define (test-move) + (define pac (make-pacman 5 2)) + ((pac 'move!) 0 1) + (define pos (pac 'position)) + (check-eq? (pos 'col) 3 "Col should be 3 after 1 step right") + (check-eq? (pos 'row) 5 "Row unchanged after horizontal move")) - ;; Test meerdere bewegingen - (define (test-meerdere-bewegingen) - (define pac (maak-pacman 5 5)) - ((pac 'beweeg!) -1 0) - ((pac 'beweeg!) 0 1) - (define pos (pac 'positie)) - (check-eq? (pos 'rij) 4 "Rij moet 4 zijn na omhoog") - (check-eq? (pos 'kolom) 6 "Kolom moet 6 zijn na rechts")) + ;; Test multiple moves + (define (test-multiple-moves) + (define pac (make-pacman 5 5)) + ((pac 'move!) -1 0) + ((pac 'move!) 0 1) + (define pos (pac 'position)) + (check-eq? (pos 'row) 4 "Row should be 4 after up") + (check-eq? (pos 'col) 6 "Col should be 6 after right")) (define (test) - (run-test test-aanmaak "Pac-Man: aanmaak en startpositie") - (run-test test-richting "Pac-Man: richting") - (run-test test-beweeg "Pac-Man: beweeg!") - (run-test test-meerdere-bewegingen "Pac-Man: meerdere bewegingen")))) + (run-test test-creation "Pac-Man: creation and start position") + (run-test test-direction "Pac-Man: direction") + (run-test test-move "Pac-Man: move!") + (run-test test-multiple-moves "Pac-Man: multiple moves")))) diff --git a/pacman-project/tests/test-positie.rkt b/pacman-project/tests/test-positie.rkt deleted file mode 100644 index 592c16f..0000000 --- a/pacman-project/tests/test-positie.rkt +++ /dev/null @@ -1,57 +0,0 @@ -#lang r7rs - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Tests: Positie ADT ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(define-library (pacman-project tests test-positie) - (import (scheme base) - (pp1 tests) - (pacman-project adt-positie)) - (export test) - - (begin - - ;; Test aanmaak en getters - (define (test-aanmaak) - (define pos (maak-positie 5 10)) - (check-eq? (pos 'rij) 5 "Rij moet 5 zijn") - (check-eq? (pos 'kolom) 10 "Kolom moet 10 zijn")) - - ;; Test mutators - (define (test-mutators) - (define pos (maak-positie 0 0)) - ((pos 'rij!) 3) - ((pos 'kolom!) 7) - (check-eq? (pos 'rij) 3 "Rij moet 3 zijn na rij!") - (check-eq? (pos 'kolom) 7 "Kolom moet 7 zijn na kolom!")) - - ;; Test vergelijk? - (define (test-vergelijk) - (define p1 (maak-positie 5 10)) - (define p2 (maak-positie 5 10)) - (define p3 (maak-positie 5 11)) - (check ((p1 'vergelijk?) p2) "Gelijke posities moeten gelijk zijn") - (check (not ((p1 'vergelijk?) p3)) "Verschillende posities mogen niet gelijk zijn")) - - ;; Test beweeg - (define (test-beweeg) - (define pos (maak-positie 5 10)) - (define nieuwe-pos ((pos 'beweeg) -1 0)) - (check-eq? (nieuwe-pos 'rij) 4 "Rij moet 4 zijn na beweeg omhoog") - (check-eq? (nieuwe-pos 'kolom) 10 "Kolom ongewijzigd na beweeg omhoog") - ;; Originele positie mag niet gewijzigd zijn - (check-eq? (pos 'rij) 5 "Originele rij mag niet wijzigen")) - - ;; Test dat twee objecten niet eq? zijn - (define (test-identiteit) - (define p1 (maak-positie 1 1)) - (define p2 (maak-positie 1 1)) - (check (not (eq? p1 p2)) "Twee positie-objecten mogen niet eq? zijn")) - - (define (test) - (run-test test-aanmaak "Positie: aanmaak en getters") - (run-test test-mutators "Positie: mutators") - (run-test test-vergelijk "Positie: vergelijk?") - (run-test test-beweeg "Positie: beweeg") - (run-test test-identiteit "Positie: identiteit")))) diff --git a/pacman-project/tests/test-position.rkt b/pacman-project/tests/test-position.rkt new file mode 100644 index 0000000..df41b10 --- /dev/null +++ b/pacman-project/tests/test-position.rkt @@ -0,0 +1,57 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Tests: Position ADT ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-library (pacman-project tests test-position) + (import (scheme base) + (pp1 tests) + (pacman-project adt-position)) + (export test) + + (begin + + ;; Test creation and getters + (define (test-creation) + (define pos (make-position 5 10)) + (check-eq? (pos 'row) 5 "Row should be 5") + (check-eq? (pos 'col) 10 "Col should be 10")) + + ;; Test mutators + (define (test-mutators) + (define pos (make-position 0 0)) + ((pos 'row!) 3) + ((pos 'col!) 7) + (check-eq? (pos 'row) 3 "Row should be 3 after row!") + (check-eq? (pos 'col) 7 "Col should be 7 after col!")) + + ;; Test equal? + (define (test-equal) + (define p1 (make-position 5 10)) + (define p2 (make-position 5 10)) + (define p3 (make-position 5 11)) + (check ((p1 'equal?) p2) "Equal positions should match") + (check (not ((p1 'equal?) p3)) "Different positions should not match")) + + ;; Test move + (define (test-move) + (define pos (make-position 5 10)) + (define new-pos ((pos 'move) -1 0)) + (check-eq? (new-pos 'row) 4 "Row should be 4 after move up") + (check-eq? (new-pos 'col) 10 "Col unchanged after move up") + ;; Original position must not change + (check-eq? (pos 'row) 5 "Original row must not change")) + + ;; Test that two objects are not eq? + (define (test-identity) + (define p1 (make-position 1 1)) + (define p2 (make-position 1 1)) + (check (not (eq? p1 p2)) "Two position objects must not be eq?")) + + (define (test) + (run-test test-creation "Position: creation and getters") + (run-test test-mutators "Position: mutators") + (run-test test-equal "Position: equal?") + (run-test test-move "Position: move") + (run-test test-identity "Position: identity")))) diff --git a/pacman-project/tests/test-score.rkt b/pacman-project/tests/test-score.rkt deleted file mode 100644 index 4b01ce1..0000000 --- a/pacman-project/tests/test-score.rkt +++ /dev/null @@ -1,30 +0,0 @@ -#lang r7rs - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Tests: Score ADT ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(define-library (pacman-project tests test-score) - (import (scheme base) - (pp1 tests) - (pacman-project adt-score)) - (export test) - - (begin - - ;; Test startscore - (define (test-startscore) - (define s (maak-score)) - (check-eq? (s 'punten) 0 "Startscore moet 0 zijn")) - - ;; Test score verhoging - (define (test-verhoog) - (define s (maak-score)) - ((s 'verhoog!)) - (check-eq? (s 'punten) 10 "Score moet 10 zijn na 1 muntje") - ((s 'verhoog!)) - (check-eq? (s 'punten) 20 "Score moet 20 zijn na 2 muntjes")) - - (define (test) - (run-test test-startscore "Score: startscore") - (run-test test-verhoog "Score: verhoging")))) diff --git a/pacman-project/tests/test-tijdslimiet.rkt b/pacman-project/tests/test-tijdslimiet.rkt deleted file mode 100644 index 9ee9d42..0000000 --- a/pacman-project/tests/test-tijdslimiet.rkt +++ /dev/null @@ -1,55 +0,0 @@ -#lang r7rs - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Tests: Tijdslimiet ADT ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(define-library (pacman-project tests test-tijdslimiet) - (import (scheme base) - (pp1 tests) - (pacman-project adt-tijdslimiet)) - (export test) - - (begin - - ;; Test starttijd - (define (test-starttijd) - (define t (maak-tijdslimiet)) - (check-eq? (t 'resterende-tijd) 60 "Starttijd moet 60 seconden zijn") - (check (not ((t 'tijd-op?))) "Tijd mag niet op zijn bij start")) - - ;; Test verlaag na 1 seconde - (define (test-verlaag) - (define t (maak-tijdslimiet)) - ((t 'verlaag!) 1000) - (check-eq? (t 'resterende-tijd) 59 "Tijd moet 59 zijn na 1 seconde")) - - ;; Test verhoog (muntje bonus) - (define (test-verhoog) - (define t (maak-tijdslimiet)) - ((t 'verlaag!) 1000) - ((t 'verhoog!)) - (check-eq? (t 'resterende-tijd) 60 "Tijd moet 60 zijn na verlaag + verhoog")) - - ;; Test formatteer-tijd - (define (test-formatteer) - (define t (maak-tijdslimiet)) - (check-eq? ((t 'formatteer-tijd)) "1:00" "60 seconden = 1:00") - ((t 'verlaag!) 1000) - (check-eq? ((t 'formatteer-tijd)) "0:59" "59 seconden = 0:59")) - - ;; Test tijd-op? - (define (test-tijd-op) - (define t (maak-tijdslimiet)) - ;; 60 keer verlagen met 1 seconde - (do ((i 0 (+ i 1))) - ((= i 60)) - ((t 'verlaag!) 1000)) - (check ((t 'tijd-op?)) "Tijd moet op zijn na 60 seconden")) - - (define (test) - (run-test test-starttijd "Tijdslimiet: starttijd") - (run-test test-verlaag "Tijdslimiet: verlaag") - (run-test test-verhoog "Tijdslimiet: verhoog") - (run-test test-formatteer "Tijdslimiet: formatteer-tijd") - (run-test test-tijd-op "Tijdslimiet: tijd-op?")))) diff --git a/pacman-project/tests/test-timer.rkt b/pacman-project/tests/test-timer.rkt new file mode 100644 index 0000000..326988c --- /dev/null +++ b/pacman-project/tests/test-timer.rkt @@ -0,0 +1,55 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Tests: Timer ADT ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-library (pacman-project tests test-timer) + (import (scheme base) + (pp1 tests) + (pacman-project adt-timer)) + (export test) + + (begin + + ;; Test initial time + (define (test-initial) + (define t (make-timer)) + (check-eq? (t 'remaining-time) 60 "Initial time should be 60 seconds") + (check (not ((t 'time-up?))) "Time should not be up at start")) + + ;; Test decrease after 1 second + (define (test-decrease) + (define t (make-timer)) + ((t 'decrease!) 1000) + (check-eq? (t 'remaining-time) 59 "Time should be 59 after 1 second")) + + ;; Test increase (coin bonus) + (define (test-increase) + (define t (make-timer)) + ((t 'decrease!) 1000) + ((t 'increase!)) + (check-eq? (t 'remaining-time) 60 "Time should be 60 after decrease + increase")) + + ;; Test format-time + (define (test-format) + (define t (make-timer)) + (check-eq? ((t 'format-time)) "1:00" "60 seconds = 1:00") + ((t 'decrease!) 1000) + (check-eq? ((t 'format-time)) "0:59" "59 seconds = 0:59")) + + ;; Test time-up? + (define (test-time-up) + (define t (make-timer)) + ;; Decrease 60 times by 1 second + (do ((i 0 (+ i 1))) + ((= i 60)) + ((t 'decrease!) 1000)) + (check ((t 'time-up?)) "Time should be up after 60 seconds")) + + (define (test) + (run-test test-initial "Timer: initial time") + (run-test test-decrease "Timer: decrease") + (run-test test-increase "Timer: increase") + (run-test test-format "Timer: format-time") + (run-test test-time-up "Timer: time-up?")))) diff --git a/snake-wpo/adt-appel.rkt b/snake-wpo/adt-appel.rkt new file mode 100644 index 0000000..7f6c0cd --- /dev/null +++ b/snake-wpo/adt-appel.rkt @@ -0,0 +1,40 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Appel ADT ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +(define-library () + (import (scheme base)) + (export maak-appel) + + (begin + + + + ;; ADT Appel + ;; maak-appel :: positie -> appel + ;; positie :: appel -> positie + ;; positie! :: appel, positie -> / + + ;; maak-appel :: positie -> appel + (define (maak-appel positie) + ;; positie! :: positie -> / + (define (positie! nieuwe-positie) + (set! positie nieuwe-positie)) + + (define (dispatch-appel msg) + (cond ((eq? msg 'positie) positie) + ((eq? msg 'positie!) positie!) + (else (error "Appel ADT -- Onbekend bericht:" msg)))) + dispatch-appel) + + ;; Merk op dat de `dispatch-appel` procedure niet in de beschrijving van het ADT + ;; staat! Deze procedure wordt gebruikt om het ADT in een object-gebaseerde + ;; stijl te implementeren. De beschrijving van het ADT bevat alleen de operaties + ;; die beschikbaar zijn in de dispatch-procedure. Dat betekent dat een procedure + ;; die niet beschikbaar gesteld wordt in de dispatch-procedure, geen deel + ;; uitmaakt van de beschrijving van het ADT. + + )) diff --git a/snake-wpo/adt-level.rkt b/snake-wpo/adt-level.rkt new file mode 100644 index 0000000..5bd9338 --- /dev/null +++ b/snake-wpo/adt-level.rkt @@ -0,0 +1,182 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Level ADT ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-library () + (import (scheme base) + + (snake-wpo adt-appel) + + (snake-wpo adt-positie) + (snake-wpo adt-slang) + (snake-wpo hulp-procedures) + (snake-wpo constanten)) + (export maak-level) + + (begin + + ;; Dit voorbeeldspel bestaat uit slechts 1 level. We zouden de appel en slang + ;; rechtstreeks in het Spel ADT kunnen geïmplementeerd hebben. Maar, als we + ;; later echter zouden beslissen om een nieuw soort level toe te voegen dan moet + ;; niet heel het Spel ADT aangepast worden. Door deze opsplitsing te maken moet + ;; alleen de implementatie van het Level ADT aangepast worden. + + ;; maak-level :: number, number -> level + (define (maak-level aantal-cellen-breedte aantal-cellen-hoogte) + (let* ((slang-start-positie + (maak-positie (quotient aantal-cellen-breedte 2) + (quotient aantal-cellen-hoogte 2))) + (slang-object (maak-slang slang-start-positie)) + (appel-object #f) + (appel-tijd 0) + (slang-tijd 0)) + + ;; + ;; Hulpprocedures + ;; + + ;; Deze procedures genereren een random positie in de spelwereld. + ;; In dit eenvoudig spel is er geen check om te controleren of er al een + ;; object op de gegenereerde locatie is. + + ;; random-x-waarde :: / -> number + (define (random-x-waarde) + (random aantal-cellen-breedte)) + + ;; random-y-waarde :: / -> number + (define (random-y-waarde) + (random aantal-cellen-hoogte)) + + + ;; + ;; Logica Appel + ;; + + ;; Deze is hier geïmplementeerd omdat voor een appel op een nieuwe positie + ;; te zetten, de afmetingen van het spelbord geweten moeten worden. + ;; Als dit geïmplementeerd zou zijn in het Appel ADT, dan zou het Appel ADT + ;; ook afhankelijk zijn van het Level ADT. Om deze afhankelijkheid te + ;; vermijden is deze logica hier geïmplementeerd. + + ;; random-positie :: / -> positie + (define (random-positie) + (let ((x (random-x-waarde)) + (y (random-y-waarde))) + (maak-positie x y))) + + ;; randomise-appel! :: / -> / + (define (randomise-appel!) + (if appel-object + (let ((nieuwe-positie (random-positie)) + (appel-positie (appel-object 'positie))) + ;; Verplaats de appel naar een nieuwe positie! + ((appel-positie 'x!) (nieuwe-positie 'x)) + ((appel-positie 'y!) (nieuwe-positie 'y)) + ;; Reset de timer + (set! appel-tijd 0)))) + + ;; nieuwe-appel! :: / -> / + (define (nieuwe-appel!) + + + (set! appel-object (maak-appel (random-positie))) + ;; Vergeet ook niet om de `adt-appel`-library te importeren (zie bovenaan). + ;; Alternatieve oplossing: + ;; Je kan ook in de `let*` bovenaan (lijn 17) de `appel-object` variabele + ;; initialiseren met `(maak-appel (maak-positie 2 2))`. Merk op dat + ;; de `(random-positie)` procedure hier nog niet gedefinieërd is en je deze + ;; dus elders moet implementeren. + + (set! appel-tijd 0)) + + ;; beweeg-appel! :: / -> / + (define (beweeg-appel! delta-tijd) + (set! appel-tijd (+ appel-tijd delta-tijd)) + (if (> appel-tijd appel-refresh-rate) + (randomise-appel!))) + + + ;; + ;; Logica Slang + ;; + + ;; We hebben ervoor gekozen om alle logica dat te maken heeft met het + ;; bewegen van de slang in het Level ADT zelf te implementeren. Dit omdat + ;; een deel van de logica voor het bewegen afhankelijk is van de positie + ;; van de appel. We zouden er ook voor gekozen kunnen hebben om deze in het + ;; Slang ADT zelf te implementeren, maar dan moet het Slang ADT toegang + ;; krijgen tot informatie dat bij het level hoort. + ;; Het voordeel van dit hier te implementeren is dat we geen complexiteit + ;; toevoegen om die data te delen. Het nadeel is dat een deel van de logica + ;; die conceptueel bij het Slang ADT zou moeten horen, niet in het Slang ADT + ;; geïmplementeerd is. + ;; Bepaal in je eigen project wanneer je welke methode toepast! Je gemaakte + ;; keuzes moeten zorgen tot een goede codekwaliteit. + + ;; beweeg-slang! :: / -> / + (define (beweeg-slang!) + (if (> slang-tijd slang-snelheid) + (begin + ;; Laat de slang 1 eenheid "vooruit" bewegen + (slang-object 'beweeg!) + + ;; Kijk of de slang botst met de appel. + (if appel-object + (let* ((appel-positie (appel-object 'positie)) + (overlappingen ((slang-object 'voor-alle-stukken) + (lambda (stuk) + ((appel-positie 'vergelijk?) (stuk 'positie)))))) + (if (member #t overlappingen) + (begin (slang-object 'verleng!) + (nieuwe-appel!))))) + ;; Reset de timer. + (set! slang-tijd 0)))) + + ;; draai-slang! :: symbol -> / + (define (draai-slang! toets) + (cond + ((eq? toets 'right) + ((slang-object 'richting!) 'rechts)) + ((eq? toets 'left) + ((slang-object 'richting!) 'links)) + ((eq? toets 'up) + ((slang-object 'richting!) 'omhoog)) + ((eq? toets 'down) + ((slang-object 'richting!) 'omlaag)))) + + + ;; + ;; Algemene Logica + ;; + + ;; update! :: number -> / + (define (update! delta-tijd) + (set! slang-tijd (+ slang-tijd delta-tijd)) + (beweeg-appel! delta-tijd) + (beweeg-slang!)) + + ;; toets :: any -> / + (define (toets! toets) + (draai-slang! toets)) + + + ;; + ;; Initialisatie + ;; + + (nieuwe-appel!) + + + ;; + ;; Dispatch + ;; + + (define (dispatch-level msg) + (cond ((eq? msg 'update!) update!) + ((eq? msg 'toets!) toets!) + ((eq? msg 'appel) appel-object) + ((eq? msg 'slang) slang-object) + (else (error "Level ADT -- Onbekend bericht:" msg)))) + dispatch-level)))) diff --git a/snake-wpo/adt-positie.rkt b/snake-wpo/adt-positie.rkt new file mode 100644 index 0000000..245a9cb --- /dev/null +++ b/snake-wpo/adt-positie.rkt @@ -0,0 +1,91 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Positie ADT ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-library () + (import (scheme base)) + (export maak-positie) + + (begin + + ;; maak-positie :: number, number -> position + (define (maak-positie x y) + + ;; We voorzien twee soorten "beweeg" procedures: een functionele (zonder + ;; assignments, die dus een nieuw objectje teruggeeft), en destructieve (met + ;; assignments, die de interne variabelen van het objectje aanpast). + ;; In deze oplossing worden beiden manieren gebruikt omdat beiden op + ;; verschillende plaatsen in het spel gebruikt worden. + + + ;; x! :: number -> / + (define (x! nieuwe-x) + (set! x nieuwe-x)) + + ;; y! :: number -> / + (define (y! nieuwe-y) + (set! y nieuwe-y)) + + + + ;; vergelijk? :: positie -> boolean + (define (vergelijk? andere-positie) + (and (= x (andere-positie 'x)) + (= y (andere-positie 'y)))) + + ;; Merk volgende eigenaardigheid op bij onderstaande code... + ;; + ;; (define pos1 (maak-positie 10 20)) + ;; (define pos2 (maak-positie 10 20)) + ;; (eq? pos1 pos2) ; -> #f + ;; ((pos1 'vergelijk?) pos2) ; -> #t + ;; + ;; De ingebouwde `eq?` van Scheme vergelijkt of twee waardes identiek + ;; hetzelfde zijn. `pos1` en `pos2` wijzen naar verschillende procedures (want + ;; elk hebben een andere omgeving). Daarom geeft `eq?` #f terug in deze + ;; situatie. + ;; + ;; Merk ook op dat deze implementatie gebruik maakt van `=` want zowel van de + ;; variabele `x` als de variabele `y` wordt verwacht dat deze numbers zijn. + ;; Als je zeker bent dat twee values number zijn, maak dan altijd gebruik van + ;; `=` en niet van `eq?`. Kijk naar de documentatie van R7RS voor meer + ;; informatie over het verschil tussen `=`, `eq?` maar ook tussen `equal?` en + ;; `eqv?`. + + + (define (beweeg richting) + (cond ((eq? richting 'omhoog) (maak-positie x (- y 1))) + ((eq? richting 'omlaag) (maak-positie x (+ y 1))) + ((eq? richting 'links) (maak-positie (- x 1) y)) + ((eq? richting 'rechts) (maak-positie (+ x 1) y)))) + + + ;; Er zijn meerdere manieren om een positie te veranderen. Ofwel pas je met + ;; behulp van `x!` en `y!` de waarde van een positie-object aan, ofwel + ;; genereer je een nieuw positie-object (zoals `beweeg`) die je dan vervolgens + ;; aan een ander object kan toewijzen (bijvoorbeeld met `positie!`) + ;; Beiden zijn goede methodes voor je spellogica (of zelfs tekenlogica) te + ;; implementeren. Documenteer goed welke keuzes je maakt in jouw spel. + ;; In dit spel worden beiden gebruikt: de logica voor een slang te laten + ;; bewegen maakt gebruik van `beweeg`, maar de logica om een appel op een + ;; nieuwe positie te plaatsen maakt gebruik van `x!` en `y!`. + ;; Merk op dat we bij de conditional hierboven een `else` tak geplaatst + ;; hebben. Mocht deze procedure opgeroepen worden met een foutief symbool (of + ;; andere type van waarde) zal er onmiddellijk een foutmelding gegenereerd + ;; worden. Hierdoor kan je sneller bepaalde bugs mee oplossen en bespaar je + ;; onnodig debugging-werk. Indien je geen else-tak voorziet zal Scheme een + ;; # teruggeven als resultaat. + + + (define (dispatch-positie msg) + (cond ((eq? msg 'x) x) + ((eq? msg 'y) y) + ((eq? msg 'x!) x!) + ((eq? msg 'y!) y!) + ((eq? msg 'beweeg) beweeg) + ((eq? msg 'vergelijk?) vergelijk?) + (else (error "Positie ADT -- Onbekend bericht:" msg)))) + + dispatch-positie))) diff --git a/snake-wpo/adt-slang-stuk.rkt b/snake-wpo/adt-slang-stuk.rkt new file mode 100644 index 0000000..f975cd0 --- /dev/null +++ b/snake-wpo/adt-slang-stuk.rkt @@ -0,0 +1,33 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Slang Stuk ADT ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-library () + (import (scheme base)) + (export maak-slang-stuk) + + (begin + + + ;; We willen een slang voorstellen. Dit doen we natuurlijk door meerdere + ;; lichaamsdelen te tekenen. We hebben een hoofd en de rest van de staart die + ;; bestaat uit verschillende blokjes. Om het gemakkelijk te maken stellen we het + ;; hoofd hetzelfde voor als het lichaam. Dit wil zeggen dat de slang + ;; uiteindelijk zal bestaan uit een lijst van objectjes van het Slang Stuk ADT. + + ;; maak-slang-stuk :: positie -> slang-stuk + (define (maak-slang-stuk positie) + + ;; positie! :: positie -> / + (define (positie! nieuwe-positie) + (set! positie nieuwe-positie)) + + ;; Dispatch functie + (define (dispatch-slang-stuk msg) + (cond ((eq? msg 'positie) positie) + ((eq? msg 'positie!) positie!) + (else (error "Slang Stuk ADT -- Onbekend bericht:" msg)))) + + dispatch-slang-stuk))) diff --git a/snake-wpo/adt-slang.rkt b/snake-wpo/adt-slang.rkt new file mode 100644 index 0000000..7b293a0 --- /dev/null +++ b/snake-wpo/adt-slang.rkt @@ -0,0 +1,77 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Slang ADT ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-library () + (import (scheme base) + (snake-wpo adt-slang-stuk)) + (export maak-slang) + + (begin + + (define (maak-slang start-positie) + (let ((stukken (list (maak-slang-stuk start-positie))) + (richting 'omhoog)) + + ;; Deze procedure voegt een stuk toe aan de slang. Namelijk aan de + ;; voorkant van de slang. Dit doen we om twee redenen. Enerzijds omdat + ;; `cons` de snelste manier is om dit te doen, maar anderzijds ook omdat + ;; dit het updaten van de slang gemakkelijker maakt. Alle andere stukken + ;; mogen blijven staan. Dit zal duidelijk worden bij het updaten van de + ;; positie. + + ;; maak-langer! :: / -> / + (define (maak-langer!) + (let* ((hoofd (car stukken)) + (nieuwe-positie (((hoofd 'positie) 'beweeg) richting)) + (nieuw-stuk (maak-slang-stuk nieuwe-positie))) + (set! stukken (cons nieuw-stuk stukken)))) + + ;; Deze procedure laat de slang bewegen. Dit doet hij door de positie van + ;; het hoofd-object te nemen, en aan de hand van de huidige richting een + ;; nieuwe positie te berekenen. Daarna wordt de positie van elk deeltje één + ;; plaats opgeschoven zodanig dat het eerste deeltje op de nieuwe positie + ;; staat, en het tweede deeltje op de oude positie van het eerste deeltje + ;; en zo verder... + + ;; beweeg! :: / -> / + (define (beweeg!) + (define (iter lst new-pos) + (let* ((first (car lst)) + (rest (cdr lst)) + (old-pos (first 'positie))) + ((first 'positie!) new-pos) + (if (not (null? rest)) + (iter rest old-pos)))) + (let* ((hoofd (car stukken)) + (volgende-positie (((hoofd 'positie) 'beweeg) richting))) + (iter stukken volgende-positie))) + + ;; set-richting! :: symbol -> / + (define (set-richting! r) + (set! richting r)) + + ;; voor-alle-stukken :: (slang-stuk -> any) -> list + (define (voor-alle-stukken f) + (map f stukken)) + + + ;; Merk op dat het signatuur van de `voor-alle-stukken` procedure een + ;; genest signatuur bevat: dat is namelijk het signatuur van de procedure + ;; die aan de `voor-alle-stukken` procedure zelf meegegeven wordt. + ;; Het signatuur zegt dus dat de `voor-alle-stukken` procedure zelf een + ;; procedure binnenneemt en een lijst teruggeeft, en dat die procedure een + ;; procedure moet zijn die van een slang-stuk objectje naar eender welke + ;; andere waarde gaat. + + + (define (dispatch-slang msg) + (cond ((eq? msg 'verleng!) (maak-langer!)) + ((eq? msg 'richting!) set-richting!) + ((eq? msg 'beweeg!) (beweeg!)) + ((eq? msg 'voor-alle-stukken) voor-alle-stukken) + (else (error "Slang ADT -- Onbekend bericht:" msg)))) + + dispatch-slang)))) \ No newline at end of file diff --git a/snake-wpo/adt-spel.rkt b/snake-wpo/adt-spel.rkt new file mode 100644 index 0000000..165e36c --- /dev/null +++ b/snake-wpo/adt-spel.rkt @@ -0,0 +1,52 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Spel ADT ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-library () + (import (scheme base) + (snake-wpo adt-level) + (snake-wpo constanten) + (snake-wpo adt-teken)) + (export maak-spel) + + (begin + + ;; maak-spel :: / -> spel + (define (maak-spel) + ;; Dit is het eigenlijk spel object. + (let ((level (maak-level spel-breedte spel-hoogte)) + (teken (maak-teken venster-breedte-px venster-hoogte-px))) + + ;; toets-procedure :: symbol, any -> / + (define (toets-procedure status toets) + ;; status is ofwel gelijk aan ... + ;; - 'pressed: wanneer de toets ingedrukt wordt + ;; - 'released: wanneer de toets losgelaten wordt + ;; Wanneer de toets voor lange tijd ingedrukt wordt, dan wordt deze + ;; procedure meermaals aangeroepen waarbij status gelijk is aan 'pressed + ;; voor dezelfde toets! + (if (eq? status 'pressed) + ((level 'toets!) toets))) + + ;; spel-lus-procedure :: number -> / + (define (spel-lus-procedure delta-tijd) + ((level 'update!) delta-tijd)) + + (define (teken-procedure) + ((teken 'teken-spel!) dispatch-spel)) + + (define (start!) + ;; Stel de callbacks in voor het teken ADT: implementatie van de spellus. + ((teken 'set-spel-lus-functie!) spel-lus-procedure) + ((teken 'set-toets-functie!) toets-procedure) + ((teken 'start-tekenen!) dispatch-spel)) + + ;; Dispatch functie + (define (dispatch-spel msg) + (cond ((eq? msg 'start!) start!) + ((eq? msg 'level) level) + (else (error "Spel ADT -- Onbekend bericht:" msg)))) + + dispatch-spel)))) \ No newline at end of file diff --git a/snake-wpo/adt-teken.rkt b/snake-wpo/adt-teken.rkt new file mode 100644 index 0000000..a365286 --- /dev/null +++ b/snake-wpo/adt-teken.rkt @@ -0,0 +1,188 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Teken ADT ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define-library () + (import (scheme base) + (pp1 graphics) + (snake-wpo constanten)) + (export maak-teken) + + (begin + + ;; maak-teken :: number, number -> teken + (define (maak-teken pixels-horizontaal pixels-verticaal) + (let ((venster (make-window pixels-horizontaal pixels-verticaal "Snake"))) + + ;; + ;; Configureren van het spelvenster + ;; + + ((venster 'set-background!) "black") + + + ;; + ;; Slang stukken tiles beheren + ;; Voor een slang te tekenen moeten we een dynamisch aantal stukken + ;; tekenen. Het is dus onmogelijk om op voorhand deze 'tiles' te + ;; definieren. Daarom gaan we dus een associatie-lijst bijhouden van tiles. + ;; Elk element in die lijst zal dus een cons-cell zijn waarbij de car gelijk + ;; is aan het object, en de cdr die tile is die gebruikt wordt om dat object + ;; op het scherm te tekenen. + ;; + + (define slang-laag ((venster 'new-layer!))) + (define slang-tiles '()) + + ;; voeg-slang-stuk-toe! :: slang-stuk -> tile + (define (voeg-slang-stuk-toe! slang-stuk) + (let ((nieuwe-tile + (make-bitmap-tile "images/snake.png" "images/snake-mask.png"))) + (set! slang-tiles (cons (cons slang-stuk nieuwe-tile) slang-tiles)) + ((slang-laag 'add-drawable!) nieuwe-tile) + nieuwe-tile)) + + ;; neem-slang-stuk :: slang-stuk -> tile + (define (neem-slang-stuk slang-stuk) + (let ((result (assoc slang-stuk slang-tiles))) + (if result + (cdr result) + (voeg-slang-stuk-toe! slang-stuk)))) + + + ;; OPGELET: Merk op dat er geen code aanwezig is om tiles te verwijderen. + ;; In dit voorbeeldspel was dit niet nodig. Maar in een complex spel zal je + ;; ook tiles moeten verwijderen als ze niet meer nodig zijn. Hou hier + ;; rekening mee wanneer je jouw spel implementeert! + + + ;; + ;; Appel tiles beheren + ;; In dit voorbeeldspel is er slechts 1 appel, dus 1 vaste tile in het Teken + ;; ADT is voldoende. + ;; + + (define appel-laag ((venster 'new-layer!))) + (define appel-tile + (make-bitmap-tile "images/apple.png" "images/apple-mask.png")) + ((appel-laag 'add-drawable!) appel-tile) + + + ;; OPGELET: Omdat er in dit spel slechts één appel zichtbaar is hebben wij + ;; de implementatie van het tekenen van appels eenvoudig gemaakt door + ;; slechts één tile aan te maken die steeds hergebruikt wordt. Denk na wat + ;; er gebeurd als er meerdere appels tegelijkertijd moeten verschijnen als + ;; er maar één tile is... Hoe is dit probleem opgelost bij + + ;; OPGELET: Merk op dat de volgorde waarin lagen aangemaakt worden een + ;; invloed hebben op hoe je spel getekend wordt. Lagen worden op het scherm + ;; getekend van oud-naar-nieuw. Dat betekent dat de tiles op de appel-laag + ;; getekend zullen worden bovenop de tiles op de slang-laag. + + + + ;; + ;; Teken Functies + ;; + + ;; Generieke teken procedure + + + ;; teken-object! :: any tile -> / + (define (teken-object! obj tile) + (let* ((obj-x ((obj 'positie) 'x)) + (obj-y ((obj 'positie) 'y)) + (screen-x (* cel-breedte-px obj-x)) + (screen-y (* cel-hoogte-px obj-y))) + ((tile 'set-x!) screen-x) + ((tile 'set-y!) screen-y))) + + ;; Schrijf je spellogica nooit in pixels. Dit zorgt ervoor dat je spel kan + ;; werken ongeacht de specifieke resolutie of hardware (denk aan een matrix + ;; van LED-lichtjes t.o.v. pixels op een computerscherm). De omzetting van + ;; spellogica naar tekenlogica gebeurt hier door de coördinaten in de + ;; spellogica te vermenigvuldigen met de afmetingen (in pixels) van een cel. + ;; + ;; Informatie over werken met tiles en lagen kan teruggevonden worden in de + ;; documentatie van de grafische bibliotheek en wordt dus niet hier in + ;; detail besproken. + + + ;; Appel + ;; teken-appel! :: appel -> / + (define (teken-appel! appel) + (if appel + (teken-object! appel appel-tile))) + + ;; Slang + ;; teken-slang-stuk! :: slang-stuk -> / + (define (teken-slang-stuk! slang-stuk) + (let ((tile (neem-slang-stuk slang-stuk))) + (teken-object! slang-stuk tile))) + + ;; Spel + ;; teken-spel! :: spel -> / + (define (teken-spel! spel) + (teken-level! (spel 'level))) + + ;; Level + ;; teken-level! :: level -> / + (define (teken-level! level) + (teken-appel! (level 'appel)) + (teken-slang! (level 'slang))) + + ;; Slang + ;; teken-slang! :: slang -> / + + + (define (teken-slang! slang) + ((slang 'voor-alle-stukken) teken-slang-stuk!)) + + + + ;; + ;; Callbacks instellen + ;; + + ;; set-spel-lus-functie! :: (number -> /) -> / + (define (set-spel-lus-functie! fun) + ((venster 'set-update-callback!) fun)) + + ;; set-toets-functie! :: (symbol, any -> /) -> / + (define (set-toets-functie! fun) + ((venster 'set-key-callback!) fun)) + + ;; set-klik-functie! :: (symbol, symbol, number, number -> /) -> / + (define (set-klik-functie! fun) + (define (aangepaste-functie btn evt x y) + (let ((grid-x (quotient x cel-breedte-px)) + (grid-y (quotient y cel-hoogte-px))) + (fun btn evt grid-x grid-y))) + ((venster 'set-mouse-click-callback!) aangepaste-functie)) + + + ;; Merk op dat deze procedure, net zoals de `teken-object!` procedure + ;; hierboven een omzetting doet van het coördinatensysteem. Dit gaat van + ;; teken-coördinaat naar spel-coördinaat, dus in plaats van te + ;; vermenigvuldigen moeten we hier delen (`quotient` is deling zonder rest). + + + ;; start-tekenen! :: / -> / + (define (start-tekenen! spel) + ((venster 'set-draw-callback!) (lambda () + (teken-spel! spel)))) + + ;; + ;; Dispatch + ;; + + (define (dispatch-teken msg) + (cond ((eq? msg 'set-toets-functie!) set-toets-functie!) + ((eq? msg 'set-spel-lus-functie!) set-spel-lus-functie!) + ((eq? msg 'set-klik-functie!) set-klik-functie!) + ((eq? msg 'start-tekenen!) start-tekenen!) + (else (error "Teken ADT -- Onbekend bericht:" msg)))) + + dispatch-teken)))) diff --git a/snake-wpo/constanten.rkt b/snake-wpo/constanten.rkt new file mode 100644 index 0000000..31fa199 --- /dev/null +++ b/snake-wpo/constanten.rkt @@ -0,0 +1,53 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Constanten ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; We maken eerst een lijstje met variabelen die ons spel zullen +;; configureren. Uiteraard willen we niet dat deze "magic constants" doorheen +;; het hele programma verspreid staan. Dit heeft als gevolg dat wanneer je +;; bijvoorbeeld het formaat van het spel wil aanpassen je dan doorheen je hele +;; code moet graven en zoeken om dit te doen. Het abstraheren van deze waardes +;; in variabelen zorgt voor heel flexibele code. + +(define-library () + (import (scheme base)) + (export cel-breedte-px + cel-hoogte-px + spel-breedte + spel-hoogte + venster-breedte-px + venster-hoogte-px + appel-refresh-rate + slang-snelheid) + + (begin + + (define cel-breedte-px 20) + (define cel-hoogte-px 20) + + (define spel-breedte 20) + (define spel-hoogte 20) + + (define venster-breedte-px (* cel-breedte-px spel-breedte)) + (define venster-hoogte-px (* cel-hoogte-px spel-hoogte)) + + ;; Hoe lang een appel op dezelfde plaats blijft staan... + (define appel-refresh-rate 20000) ;; 20000 milliseconden = 20 seconden + + ;; Aan welke snelheid de slang ongeveer beweegt... + (define slang-snelheid 200) ;; 200 milliseconden = 0.2 seconden + + ;; De code in dit bestand moet abstractie maken van hoe of wat de elementen + ;; (de appel en de slang) getekend worden. Deze taken zijn namelijk uitbesteed + ;; aan het teken-ADT (en zo aan de Graphics.rkt library). Vermits de code hier + ;; niet weet of het teken-ADT gaat tekenen naar een computerscherm, of elke + ;; frame naar een kleurenprinter gaat sturen, of gaat uitbeelden in kiezeltjes + ;; op de grond, of... mag de code in dit bestand niet geschreven worden in + ;; functie van pixels (of punten/kiezels/etc.) In plaats daarvan werken we met + ;; een abstract grid dat het speelvenster voorstelt. De volgende berekeningen + ;; zijn nodig voor het Teken ADT. Het is jouw taak om de spellogica zodanig te + ;; schrijven dat deze onafhankelijk is van de tekenlogica. + + )) diff --git a/snake-wpo/hulp-procedures.rkt b/snake-wpo/hulp-procedures.rkt new file mode 100644 index 0000000..2072644 --- /dev/null +++ b/snake-wpo/hulp-procedures.rkt @@ -0,0 +1,39 @@ +#lang r7rs + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Hulp Procedures ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; Het schrijven van algemene hulpprocedures die niet specifiek bij een ADT +;; horen kan vaak nuttig zijn. Dit kan gaan van procedures voor het eenvoudiger +;; om iets te debuggen, of om een bepaald patroon op standaard datatypes van +;; Scheme te voorzien dat niet standaard aanwezig is. E.g., als je in je +;; implementatie regelmatig, en op verschillende plaatsen, de som van een lijst +;; getallen nodig hebt kan je in de globale omgeving daar een procedure voor +;; voorzien. De juiste hulpprocedures ondersteunen hierbij de rest van je +;; implementatie. + +(define-library () + (import (scheme base) + (scheme write)) + (export debug random) + + (begin + + ;; We bieden de `random` van Racket simpel opnieuw aan. + ;; De `#%require`-syntax is speciaal aan de Racket-implementatie van R7RS en + ;; is gelijkaardig aan de `import` van R7RS (maar mag ook elders gebruikt + ;; worden). + ;; Voor je eigen project is het gebruiken van `random` op deze manier + ;; uiteraard ook toegestaan. Het importeren van andere procedures uit + ;; Racket, die niet beschikbaar zijn in R7RS, is ook toegelaten, maar + ;; sommige procedures kunnen niet toegepast worden op enkele datastructuren + ;; die R7RS gebruikt (bv. lijsten). Gebruik `#%require` dus op eigen risico. + (#%require (only racket random)) + + ;; Een hulpprocedure om eenvoudig mee te debuggen (door de boolean op `#f` te zetten wordt er niets meer geprint). + (define debugging? #t) + (define (debug . msg) + (if debugging? + (begin (display msg) + (newline)))))) diff --git a/snake-wpo/images/apple-mask.png b/snake-wpo/images/apple-mask.png new file mode 100644 index 0000000000000000000000000000000000000000..eef4bc5b1edeace796a6f6c5ae78a1c00e4eec13 GIT binary patch literal 649 zcmV;40(Sk0P)EX>4Tx04R}tkv&MmKpe$iKcu1+2P=ql$WWauh>AFB6^c+H)C#RSm|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jfa&%I3krMxx6k5c1aNLh~_a1le0Dq&xR5LgZsG4P@ zlL;Z4TNOgD2qKDJ48kWdQ=b#X6ge3JS*_Gq>z@3D;k>r8%ypVkB(aDkh!7y7hB7L!5T{im#YCF+6CVCy$1jpgCRZ7Z z91EyIh2;3b|KNAGW?^!|O$sM~t{2<>7y&}NK(lV!-^aGyJOKjFz?IhaR~x|0C+YRJ z7CQp^w}Ff6wkGcZmpj1FlP(#OBL!&s3kBf)jJ_!c4BP@eYi@6?eVjf38R{x^0~{Oz zqeaSI_jq@AXK(+WY4!I54El0@r919*58;6Mo0000PbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht7XSbN zb4f%&R5;7!lu-_VFbG2j_rLS=M-6ElxVgj_`GR$nGBOOH?Pg|&+ki7xKQr$Ek$l7q zDLG1{ioLo}DiQ&%;7qGA$Dh8L=KBh=TT`A@xs-6Z!XFX>Uuk*t|7J_VXP3e|Q|_q* j(hee4?!y>gVS2U!KAb)f$WA-X00000NkvXXu0mjf82Ahe literal 0 HcmV?d00001 diff --git a/snake-wpo/images/apple.png b/snake-wpo/images/apple.png new file mode 100644 index 0000000000000000000000000000000000000000..4894456b31024e5488534cf1169d8cd56edf8909 GIT binary patch literal 673 zcmV;S0$%-zP)EX>4Tx04R}tkv&MmKpe$iKcu1+2P=ql$WWauh>AFB6^c+H)C#RSm|Xe=O&XFE z7e~Rh;NZt%)xpJCR|i)?5c~jfa&%I3krMxx6k5c1aNLh~_a1le0Dq&xR5LgZsG4P@ zlL;Z4TNOgD2qKDJ48kWdQ=b#X6ge3JS*_Gq>z@3D;k>r8%ypVkB(aDkh!7y7hB7L!5T{im#YCF+6CVCy$1jpgCRZ7Z z91EyIh2;3b|KNAGW?^!|O$sM~t{2<>7y&}NK(lV!-^aGyJOKjFz?IhaR~x|0C+YRJ z7CQp^w}Ff6wkGcZmpj1FlP(#OBL!&s3kBf)jJ_!c4BP@eYi@6?eVjf38R{x^0~{Oz zqeaSI_jq@AXK(+WY4!I54El0@r919*LJzlx00000PbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht7XSbN zi%CR5R5;7kltB)_AP58(({Ff?|G$YId1qvU}1HH?x8g0L86}NBOny*MX8- zsaO&cgyeL9OYS!P8QMKz4-ksg-Wj8WL-x7kTF-C^;@X2ong zhr1XO0F_+{`4@MXtOvdtw;|-MBabL~kbuJvw)-r2q6PpLgLopD+Vp%j00000NkvXX Hu0mjf@E8_2 literal 0 HcmV?d00001 diff --git a/snake-wpo/images/snake-mask.png b/snake-wpo/images/snake-mask.png new file mode 100644 index 0000000000000000000000000000000000000000..96c3d58f444f71446a71dc6fc54944dbb882f1d8 GIT binary patch literal 5252 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 KMNUMnLSTYWxFOO2 literal 0 HcmV?d00001 diff --git a/snake-wpo/images/snake.png b/snake-wpo/images/snake.png new file mode 100644 index 0000000000000000000000000000000000000000..e077cf2fe104d5380c0a9dbb74bbf6bc7f42cf25 GIT binary patch literal 4644 zcmV+<65H*GP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O?T$k{dY=g#Ystdjx_c_&5%q5qpC@{(fMoWxL%o z_RP+#qges z+;Q6-Z%Sq4C0rVWn+ad}!-xB`5C6);oii;&;_EwB%qtkKnTBP~p1F#I$a&>WZ-74@ zuk`!x@B>uIpx!W77TEoGZZTB8VJqG|N3K(jPYH=D>vaP{#MsJYB$5H2C8}icC4?eE z94iUx%$#}%gA9mjSWGgc7;|tXP%@0ocZ%F=gN7XJG~7fYH0D^yO^cmmrKI4eqzZ<* zMop@kwPvG6HhvN@+qgDcKSPOudM#`_zPK+SJuMKlwDWeS>voL<>MA&bfT6sG8U4M@nRVO z(7|%%tE3bxbCxsT0!5*LNvh?>EwPM|!n7smk$3LCW$w4~=CuA+-omdk=PY&qMCKf+ z`#Eo4WNn$w&6}~O3N5BSQhj{BYj>e`6Se;NI9ArK=}s-QoQ!%3H!7I@$s3+Sxn?BNN_s%3S8!%cHGocEq)uOlh5IY)^`Elj`j z$qA%?wqRHl4qoRfNFsnN<3$o&v`$mtb(7=2Y8Ny-SR$OPU# z;{^i0P+{5!V1>$#R&_Dsd6Srjm?@EoGqFmKqi?eHb&1_r9NX;Pb2z|{&<>3f^v9R~`ClwBR7e00nw zH33&?$32=q+vPkc&lLddsLb}@91#bbe%K4|1qFdG4CP~L&kGr+dqC zMr!da5yQu2(W=zvU`=^>EgqW0{0R($dkr0I8p0=tC6Eq;hBmeu8h{`r5XL;zeO=}l zNG8Pg8@Y3TE7>@iHEEtS%^8D*q_~!`FIF89?L2Z3JC6fiDD(~`in+r$%KF~hO&V%~wyyXMyzP&XbAP8CzB+z&qX}$TVBFZZHN^^9 zL#HxlHA1U`QEh90WYfSUFY=G3QK`?Ogtb|=i>)3u!5;Mm+2n+>ZTxUAU}~F3ix*M*~LxjoGLm_te zM%KVzrux;<{qBW-c%=F9X$6<5LK={W1CXlt`7vfOHW;4RLlVC^v;TcY?;Fuw_GCMw zE97Ido48PH@e|2zv_|7c1i{HLW=;5^-T*npNpt$jzpMXH{@pJxG{1({S9P@;La zI$^I`Cc=-6qgM*F>j7MVa3F2C0;`30r<+j35&IS)q5>x=9aEes*P?&WNBna%B7!3;F=71u zp_Ob#=y!_*^}|%+5Vx(}WK%l2XtL=`v$caSY{tRy%;J#faLj?huj5bTh$D0#a7M`- zV8mF`y_#La^5F+u94|wFDq#n%y5I&n@~vfDLjgxY24*Ofu2rUW(q>XnNE(scWYKgx zf*S2L3BZ$;>s`v-ksy`V>dcV1;_gV$NEu*ZWcb z7!JAKuP!q_8>dXorzlhl8K#}jwpn0`*t(;2tu(5X<6q>~Pe=Fl2Z{EHVKcQ?GpV30 z#VZ(NWodVLW?w@wcTer&c-%q^31jw{Uu{>!Xy-aTWH&G0$VE*xU(*a2u4$gvsnK{= zjFENG-P-j+CxIL8G}^UWMC>B-<_wH8FBbI{$spa{dYpY$x+Zh>9@F8imp{~+zvkYk zG~Ff`&ulD8(%Y*^unh>z1Krsz(FwfP?x0nqpmBvtJg>VVVCT8x4%a;eVqq__c>*6bR8;P)WAW zI@Pv-nSaSvg?n_{l4_2kh%E8j?3p`-pkbwcdt${E7eE|u?_@l<%thzL69hG_;SFKh zCd-qHi;a5QDzXt?**KPrwkPY}7Um!`Cz3(iXvv_jrXxR6#F3G<*Y+D4woVokbJw*X z-*4nKhEL=nh2AM`Yz{qFHy;PLfIzb~VrDbrap+vu-LDYazZh zh_?6B{JPs~%o}IgGR<)egso8b_7hRwd(pZE>@?%u{S|EQ&bGfo{m_q>EOdVbVn26N zX5HRImbY){JcMP zx2Ld`P(0?Jz(Cb;W>vARmhut#`0Tk^wBI}8u(lTAJQAlPHJz_hxF5){{l+K=2$F}uVTW!w8hZQ(yP3KN{zzdUsfivI zY9$7FAG(D3ASPoUZimV2??!998?7c?Mt8=^)=vT&CoT49e2GY`M;?R5vL5+)_G|fO ze#mu{UT7})=FVFhi@iUXvQ!NF+je}vx&N`XkC^Yfrd@5v#S|9EuVjr0Tm|s9iCh!+ z8VTG{lp!Ul)wZ_rCruDm3Sp&iH#@d9Z}Mv1urRq+>e*c71WlwLBt_dQ&Nr1>K?Bu? zy|OK$gr$VZcWxalv}I78fIAA_jgweWTzy0PnO}lpu^jhTE2iA7QlM)LsTjzW02wGC z9UoW+nS@@j+IQd*;`Jb?2!df$hO4@Iw>yQ_1=#2DW7$84CX zy!HL}XOf=p&m>J>zmizri+o0Hk5~9Dn_Cq^uLvTFUJSw~F;kxt#S}cp*FAiEy^HfK z?{j~SJ|%B5z$XyTGTpF$WEE0hc?#(3371k|PCZ`U?f%{fxdT z2MpW-J!@`nt$mz602%5kbpsq60;5IBUiWx+cV}<^o@w>>0}T3dep~!6FaQ7m32;bR za{vGf6951U69E94oEQKA00(qQO+^Rf1RM((Cxr@s000017D+@wR5;7E)lG`RKoExE zHMgjJcZuGS-5f+2?Wxl)ub~ENoH_{`H4N>u4(##qKaw{XnBa2 z2r8X1UMT!{geV^XYZ{v3#`R<{`h>QGW`&tHS`5)iEu zCd-Jwms83_pxz?TGKegtZ9&@xH$ux;)YyL#u<1$ z8N}>!tgfFgSJ#+*j