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:
joren
2026-03-23 11:06:32 +01:00
parent c3c3c6e86c
commit cd70055bc7
45 changed files with 1936 additions and 1136 deletions

40
snake-wpo/adt-appel.rkt Normal file
View 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
View 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
View 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)))

View 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
View 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
View 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
View 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
View 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.
))

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B

BIN
snake-wpo/images/apple.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

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
View 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!))

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

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

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