#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))))