Refactor(English): Rename all files and identifiers from Dutch to English
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 <noreply@anthropic.com>
This commit is contained in:
40
snake-wpo/adt-appel.rkt
Normal file
40
snake-wpo/adt-appel.rkt
Normal file
@@ -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.
|
||||
|
||||
))
|
||||
182
snake-wpo/adt-level.rkt
Normal file
182
snake-wpo/adt-level.rkt
Normal file
@@ -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))))
|
||||
91
snake-wpo/adt-positie.rkt
Normal file
91
snake-wpo/adt-positie.rkt
Normal file
@@ -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
|
||||
;; #<void> 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)))
|
||||
33
snake-wpo/adt-slang-stuk.rkt
Normal file
33
snake-wpo/adt-slang-stuk.rkt
Normal file
@@ -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)))
|
||||
77
snake-wpo/adt-slang.rkt
Normal file
77
snake-wpo/adt-slang.rkt
Normal file
@@ -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))))
|
||||
52
snake-wpo/adt-spel.rkt
Normal file
52
snake-wpo/adt-spel.rkt
Normal file
@@ -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))))
|
||||
188
snake-wpo/adt-teken.rkt
Normal file
188
snake-wpo/adt-teken.rkt
Normal file
@@ -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))))
|
||||
53
snake-wpo/constanten.rkt
Normal file
53
snake-wpo/constanten.rkt
Normal file
@@ -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.
|
||||
|
||||
))
|
||||
39
snake-wpo/hulp-procedures.rkt
Normal file
39
snake-wpo/hulp-procedures.rkt
Normal file
@@ -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))))))
|
||||
BIN
snake-wpo/images/apple-mask.png
Normal file
BIN
snake-wpo/images/apple-mask.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 649 B |
BIN
snake-wpo/images/apple.png
Normal file
BIN
snake-wpo/images/apple.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 673 B |
BIN
snake-wpo/images/snake-mask.png
Normal file
BIN
snake-wpo/images/snake-mask.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
BIN
snake-wpo/images/snake.png
Normal file
BIN
snake-wpo/images/snake.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
16
snake-wpo/spel.rkt
Normal file
16
snake-wpo/spel.rkt
Normal file
@@ -0,0 +1,16 @@
|
||||
#lang r7rs
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Spel Opstarten ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; Om dit spel op te starten zal je de `snake-wpo` map eerst moeten uitpakken en
|
||||
;; vervolgens moeten installeren. Vervolgens kan je jouw aanpassingen maken door
|
||||
;; de bestanden in deze map aan te passen: je moet niet opnieuw de map
|
||||
;; installeren.
|
||||
|
||||
(import (scheme base)
|
||||
(snake-wpo adt-spel))
|
||||
|
||||
(define spel (maak-spel))
|
||||
((spel 'start!))
|
||||
19
snake-wpo/tests/alle-testen.rkt
Normal file
19
snake-wpo/tests/alle-testen.rkt
Normal file
@@ -0,0 +1,19 @@
|
||||
#lang r7rs
|
||||
|
||||
(import (scheme base)
|
||||
(pp1 tests)
|
||||
(prefix (snake-wpo tests test-positie) positie:)
|
||||
(prefix (snake-wpo tests test-appel) appel:))
|
||||
|
||||
;; Omdat dit project onvolledig is hebben we enkel testcode voorzien
|
||||
;; voor enkele oefeningen uit het WPO. Voor je eigen project raden we
|
||||
;; je aan om voor elk ADT minstens één test te voorzien. Een apart
|
||||
;; bestand waarin je alle testen combineert, zoals dit, helpt
|
||||
;; om snel alle functionaliteit van je project (waar een test voor
|
||||
;; geschreven is) te controleren.
|
||||
|
||||
(define (test-alles)
|
||||
(positie:test)
|
||||
(appel:test))
|
||||
|
||||
(test-alles)
|
||||
27
snake-wpo/tests/test-appel.rkt
Normal file
27
snake-wpo/tests/test-appel.rkt
Normal file
@@ -0,0 +1,27 @@
|
||||
#lang r7rs
|
||||
|
||||
(define-library ()
|
||||
(import (scheme base)
|
||||
(scheme write)
|
||||
(pp1 tests)
|
||||
(snake-wpo adt-positie)
|
||||
(snake-wpo adt-appel))
|
||||
(export test)
|
||||
(begin
|
||||
|
||||
(define (test-oefening-4)
|
||||
;; We maken twee aparte positie-objecten die een verschillende y-oordinaat hebben
|
||||
(define p1 (maak-positie 5 5))
|
||||
(define p2 (maak-positie 10 5))
|
||||
(define appel (maak-appel p1))
|
||||
(check-eq? (appel 'positie) p1 "Verwacht dat de positie ingesteld is")
|
||||
((appel 'positie!) p2)
|
||||
(check-not-eq? (appel 'positie) p1 "Verwacht dat de positie aangepast is")
|
||||
(check-eq? (appel 'positie) p2 "Verwacht dat de positie aangepast is"))
|
||||
|
||||
(define (test)
|
||||
(run-test test-oefening-4 "Oefening 4"))))
|
||||
|
||||
;; Voor de test uit te voeren voer je onderstaande code uit in de REPL:
|
||||
;;
|
||||
;; (test)
|
||||
28
snake-wpo/tests/test-positie.rkt
Normal file
28
snake-wpo/tests/test-positie.rkt
Normal file
@@ -0,0 +1,28 @@
|
||||
#lang r7rs
|
||||
|
||||
(define-library ()
|
||||
(import (scheme base)
|
||||
(pp1 tests)
|
||||
(snake-wpo adt-positie))
|
||||
(export test)
|
||||
(begin
|
||||
|
||||
(define (test-oefening-1)
|
||||
;; We maken twee aparte positie-objecten die een verschillende y-oordinaat hebben
|
||||
(define p1 (maak-positie 100 200))
|
||||
(define p2 (maak-positie 100 600))
|
||||
;; We controleren eerst dat beiden een apart object zijn
|
||||
(check (not (eq? p1 p2)) "Beide positie-objecten mogen niet eq?-gelijk zijn")
|
||||
;; We controleren of ze verschillend zijn met de eigen 'vergelijk?-operatie...
|
||||
(check (not ((p1 'vergelijk?) p2)) "Beide posities mogen niet gelijk zijn aan elkaar!")
|
||||
;; We object `p2` aan zodanig dat `p1` en `p2` dezelfde `x` en `y` hebben.
|
||||
((p2 'y!) 200)
|
||||
;; En we doen de controle met de 'vergelijk?-operatie opnieuw, maar deze keer moeten ze gelijk zijn aan elkaar
|
||||
(check ((p1 'vergelijk?) p2) "Beide posities moeten gelijk zijn aan elkaar!"))
|
||||
|
||||
(define (test)
|
||||
(run-test test-oefening-1 "Oefening 1"))))
|
||||
|
||||
;; Voor de test uit te voeren voer je onderstaande code uit in de REPL:
|
||||
;;
|
||||
;; (test)
|
||||
Reference in New Issue
Block a user