;Practicum Informatica 1 - Maanlander ;Alexander Thomas (1e kan Burg.Ir., KU Leuven) ;(c) 1998 ;Dit programma mag vrij gekopieerd worden, mits de naam van de auteur ;vermeld wordt en dit bericht intact blijft. ;Gebruiksaanwijzing: typ "(speel)". ;Opl. Oef. 1 ;----------- ;ADT maanlander: ;Constructor: ; maanlander$maak :: num * num * num -> maanlander ;Selectoren: ; maanlander$hoogte :: maanlander -> num ; maanlander$snelheid :: maanlander -> num ; maanlander$brandstof :: maanlander -> num ;Tests: ; maanlander$op-de-grond? :: maanlander -> boolean (define (maanlander$maak hoogte snelheid brandstof) (list hoogte snelheid brandstof)) (define (maanlander$hoogte maanlander) (car maanlander)) (define (maanlander$snelheid maanlander) (cadr maanlander)) (define (maanlander$brandstof maanlander) (caddr maanlander)) (define (maanlander$op-de-grond? maanlander) (<= (maanlander$hoogte maanlander) 0)) ;Opl. Oefening 2 ;--------------- (define (speel) ;sig: -> boodschap ;effect: Speel het spel maanlander, waarbij de maanlander in haar initiele ; toestand begint en eindigt met een boodschap, die zegt of je goed ; geland bent of integendeel gecrasht. (maanlanding initiele-maanlander)) (define (maanlanding maanlander) ;sig: maanlander -> boodschap ; de eerste parameter is van het type maanlander. ;effect: als de maanlander op de grond is, wordt gecontroleerd of het toestel ; zacht geland of gecrasht is, anders wordt de positie van de maanlander ; aangepast naargelang de hoeveelheid gas die de speler geeft en wordt ; de lus opnieuw uitgevoerd, nu met die nieuwe maanlander. (toon maanlander) (if (maanlander$op-de-grond? maanlander) (einde-spel maanlander) (maanlanding (pas-aan maanlander (hoeveel-gas))))) (define (pas-aan maanlander gas-ratio) ;sig: maanlander * Num -> maanlander ;vereist: De tweede parameter Num is een getal tussen 0 en 1 (grenzen inbegrepen). ; De eerste parameter en het resultaat zijn van het type maanlander ;resultaat: een nieuwe maanlander waarbij de positie is aangepast naargelang ; er gas gegeven is. We gaan in tijdsstappen van grootte dt (in seconden). ; De hoogte is in m en de snelheid dus in m/s. (list (+ (car maanlander) (* (cadr maanlander) dt)) (+ (cadr maanlander) (* (- (/ (* max-kracht gas-ratio) massa) valversnelling) dt)) (- (caddr maanlander) (* max-verbruik gas-ratio dt)))) (define (toon maanlander) ;sig: maanlander -> boodschap ;effect: toont de gegevens van de maanlander op een begrijpelijke manier ;print is vervangen door display voor een elegantere tekstweergave zonder "quotes" ;en compatibiliteit met MacGambit (display (list "Gegevens Maanlander: " 'hoogte (maanlander$hoogte maanlander) 'snelheid (maanlander$snelheid maanlander) 'brandstof (maanlander$brandstof maanlander)))(newline)) (define (einde-spel maanlander) ;sig: maanlander -> boodschap ;vereist: de maanlander bevindt zich op de grond (hoogte <= 0) ;effect: vertelt of de maanlander zacht geland is of gecrasht (te grote snelheid) (let ((eindsnelheid (maanlander$snelheid maanlander))) (display (list "Eindsnelheid : " eindsnelheid))(newline) (cond ((>= eindsnelheid veilige-snelheid) ; groter dan: snelheid is negatief! (display "Zachte landing, de maan is van ons!")(newline) 'game-over) ((< eindsnelheid (* veilige-snelheid 5)) (display "SMASH!!!! Als u dacht met die snelheid te kunnen landen, vergiste u zich want de smeulende brokjes van u en uw maanlander zijn over een straal van 50 m. verspreid! R.I.P.")(newline) 'game-over-insert-coin-to-play-again) (else (display "Spectaculaire crash! R.I.P.")(newline) 'game-over-insert-coin-to-play-again)))) (define (hoeveel-gas) ;sig: -> Num (getal tussen 0 en 1) ;effect: vraagt aan de gebruiker hoeveel gas hij wil geven. ;read is een procedure die een getal aan de gebruiker vraagt. ;de waarde van (read) is het getal dat de gebruiker ingetypt heeft ;opeenvolgende (read)'s kunnen dus verschillende waarden geven ;daarom is een let hier noodzakelijk (display "geef een getal tussen 0 en 1")(newline) (let ((invoer (read))) (if (and (number? invoer) (>= invoer 0) (<= invoer 1)) invoer (hoeveel-gas)))) ;als invoer niet voldoet, proberen we opnieuw ;Tot slot zijn er nog de definities van de globale variabelen die we gebruikt hebben. (define initiele-maanlander (maanlander$maak 50 0 20)) (define valversnelling .5) (define veilige-snelheid -2) (define dt 1) (define max-kracht 1) (define massa 1) (define max-verbruik 1) ;Opl. Oefening 3 ;--------------- (define (pas-aan maanlander gas-ratio) ;sig: maanlander * Num -> maanlander ;vereist: De tweede parameter Num is een getal tussen 0 en 1 (grenzen inbegrepen). ; De eerste parameter en het resultaat zijn van het type maanlander ;resultaat: een nieuwe maanlander waarbij de positie is aangepast naargelang ; er gas gegeven is. We gaan in tijdsstappen van grootte dt (in seconden). ; De hoogte is in m en de snelheid dus in m/s. (if (< (maanlander$brandstof maanlander) (* max-verbruik gas-ratio dt)) ;Zorgen dat het brandstofniveau nooit negatief kan worden: we kunnen niet meer verbruiken dan er nog beschikbaar is! (pas-aan maanlander (maanlander$brandstof maanlander)) (maanlander$maak (+ (maanlander$hoogte maanlander) (* (maanlander$snelheid maanlander) dt)) (+ (maanlander$snelheid maanlander) (* (- (/ (* max-kracht gas-ratio) massa) valversnelling) dt)) (- (maanlander$brandstof maanlander) (* max-verbruik gas-ratio dt))))) ;Opl. Oefening 4 ;--------------- ;ADT planeet: ;Constructor: ; planeet$maak :: [?] -> planeet ;Het resultaat is een lijst met als eerste element de naam van de planeet, dan ;de valversnelling en voor de rest lijsten van de vorm (maxhoogte wrijving) die ;de gordels voorstellen van laag naar hoog. ;Selectoren: ; planeet$naam :: planeet -> string ; planeet$valversnelling :: planeet -> Num ; planeet$gordels :: planeet -> [?] ; planeet$wrijving :: planeet * Num -> Num ;De tweede parameter voor deze procedure is de hoogte. (define (planeet$maak naam g gordels) (list naam g gordels)) (define (planeet$naam planeet) (car planeet)) (define (planeet$valversnelling planeet) (cadr planeet)) (define (planeet$gordels planeet) (caddr planeet)) (define (planeet$wrijving planeet hoogte) (define (zoek-wrijving gordels hoogte) (if (null? gordels) 0 ;We nemen aan dat de wrijving in de buitenste gordel (= ruimte) altijd 0 is. (if (< hoogte (caar gordels)) (cadar gordels) (zoek-wrijving (cdr gordels) hoogte)))) (zoek-wrijving (planeet$gordels planeet) hoogte)) ;Opl. Oefening 5 en 6 ;-------------------- (define (kwadraat x) (* x x)) (define (invsign x) ;sig: Num -> Num ;de omgekeerde signumfunctie (cond ((< x 0) 1) ((= x 0) 0) (else -1))) (define (speel) ;sig: -> boodschap ;effect: Speel het spel maanlander met een te kiezen planeet waarbij de maanlander ; in haar initiele toestand begint en eindigt met een boodschap, die zegt of ; je goed geland bent of integendeel gecrasht. (maanlanding initiele-maanlander (kies-planeet))) (define (maanlanding maanlander planeet) ;sig: maanlander * planeet -> boodschap ;effect: als de maanlander op de grond is, wordt gecontroleerd of het toestel ; zacht geland of gecrasht is, anders wordt de positie van de maanlander ; aangepast naargelang de hoeveelheid gas die de speler geeft en wordt ; de lus opnieuw uitgevoerd, nu met die nieuwe maanlander. (toon maanlander) (if (maanlander$op-de-grond? maanlander) (einde-spel maanlander planeet) (maanlanding (pas-aan maanlander (hoeveel-gas) planeet) planeet))) (define (pas-aan maanlander gas-ratio planeet) ;sig: maanlander * Num * planeet -> maanlander ;vereist: De tweede parameter Num is een getal tussen 0 en 1 (grenzen inbegrepen). ; De eerste parameter en het resultaat zijn van het type maanlander ;resultaat: een nieuwe maanlander waarbij de positie is aangepast naargelang ; er gas gegeven is. We gaan in tijdsstappen van grootte dt (in seconden). ; De hoogte is in m en de snelheid dus in m/s. ;De berekening die hier gebruikt wordt is eigenlijk zeer onnauwkeurig en levert op ;zijn minst merkwaardige situaties op bij een planeet met een hoge graviteit en wrijving. ;Zie onderaan voor een betere oplossing! (if (< (maanlander$brandstof maanlander) (* max-verbruik gas-ratio dt)) ;Zorgen dat het brandstofniveau nooit negatief kan worden: we kunnen niet meer ;verbruiken dan er nog beschikbaar is! (pas-aan maanlander (maanlander$brandstof maanlander) planeet) (maanlander$maak (+ (maanlander$hoogte maanlander) (* (maanlander$snelheid maanlander) dt)) (+ (maanlander$snelheid maanlander) (* (+ (- (/ (* max-kracht gas-ratio) massa) (planeet$valversnelling planeet)) (* (invsign (maanlander$snelheid maanlander)) ; wrijving is tegensteld aan de richting v.d. snelheid (/ (* (planeet$wrijving planeet (maanlander$hoogte maanlander)) (kwadraat (maanlander$snelheid maanlander))) massa))) dt)) (- (maanlander$brandstof maanlander) (* max-verbruik gas-ratio dt))))) (define (toon maanlander) ;sig: maanlander -> boodschap ;effect: toont de gegevens van de maanlander op een begrijpelijke manier ;print is vervangen door display voor een elegantere tekstweergave zonder "quotes" ;en compatibiliteit met MacGambit (newline)(display "Gegevens Maanlander: ")(newline) (display " hoogte: ") (display (maanlander$hoogte maanlander)) (newline) (display " snelheid: ") (display (maanlander$snelheid maanlander)) (newline) (display " brandstof: ") (display (maanlander$brandstof maanlander))(newline)) (define (einde-spel maanlander planeet) ;sig: maanlander * planeet -> boodschap ;vereist: de maanlander bevindt zich op de grond (hoogte <= 0) ;effect: vertelt of de maanlander zacht geland is, gecrasht (te grote snelheid) of ;ronduit uiteengespat (veel te grote snelheid) (let ((eindsnelheid (maanlander$snelheid maanlander))) (display (list "Eindsnelheid : " eindsnelheid))(newline) (cond ((>= eindsnelheid veilige-snelheid) ; groter dan: snelheid is negatief! (if (< eindsnelheid (* veilige-snelheid .95)) (if (string=? (planeet$naam planeet) "de aarde") (display "De UFO kwakt tegen de grond en komt tot stilstand na enkele treurwilgen ontworteld te hebben, maar gelukkig is niemand ongedeerd, ") (display "Waw, dat was nipt! Het landingsgestel kraakt en de maanlander schudt... maar gelukkig is dit ruimtetuig ontworpen door Burgies van de K.U.L., ")) (display "Zachte landing, ")) (display (planeet$naam planeet))(display " is van ons!") (newline) (if (string=? (planeet$naam planeet) "de aarde") (begin (display "Nu Red Michiel vinden om te kaarten! (die invasie kan wel even wachten)")(newline))) 'game-over) ((< eindsnelheid (* veilige-snelheid 5)) (display "SMASH!!!! Als u dacht met die snelheid te kunnen landen, vergiste u zich want de smeulende brokjes van u en uw maanlander zijn over een straal van 50 m. verspreid! R.I.P.") (newline) 'game-over-insert-coin-to-play-again) (else (display "Spectaculaire crash! R.I.P.")(newline) 'game-over-insert-coin-to-play-again)))) (define (hoeveel-gas) ;sig: -> Num (getal tussen 0 en 1) ;effect: vraagt aan de gebruiker hoeveel gas hij wil geven. ;read is een procedure die een getal aan de gebruiker vraagt. ;de waarde van (read) is het getal dat de gebruiker ingetypt heeft ;opeenvolgende (read)'s kunnen dus verschillende waarden geven ;daarom is een let hier noodzakelijk (display "geef een getal tussen 0 en 1")(newline) (let ((invoer (read))) (if (and (number? invoer) (>= invoer 0) (<= invoer 1)) invoer (hoeveel-gas)))) ;als invoer niet voldoet, proberen we opnieuw (define (kies-planeet) ;sig: -> planeet ;effect: laat de gebruiker kiezen uit een aantal planeten ;deze planeten zijn getest en speelbaar bevonden (met pas-aan2). (display "Kies een planeet uit de volgende lijst (geef een getal tussen 1 en 6)") (newline) (display "1. Maan: Goed voor beginners: valversnelling 0.5 en geen wrijving.") (newline) (display "2. Oefening 6: Valversnelling 1.8 en 3 wrijvingsgordels.") (newline) (display "3. Homogenix: Valversnelling 2.2 en een constante wrijving tot aan 100m.") (newline) (display "4. Aarde: Je bent de commandant van een UFO van de planeet Krypton met als missie: totale invasie van de aarde. Dat zal natuurlijk enkel lukken na een geslaagde landing.")(newline) (display "5. Krypton: Na de mislukte invasie op de aarde (de Kryptoniers waren na het kaarten zo zat dat ze met hun UFO tegen een boom gesmakt waren) is het nu aan u, aardbewoner, om deze planeet in te nemen met uw gloednieuwe maanlander, uitgerust met state-of-the-art kryptonstralers.") (newline) (display "6. Dinges: Over deze planeet weet men niet veel, behalve dat de bodem ervan bezaaid is met maanlander-schroot.") (newline) (let ((invoer (read))) (if (not (integer? invoer)) (kies-planeet) (cond ((= invoer 1) (planeet$maak "de maan" .5 '())) ((= invoer 2) (planeet$maak "Oefening 6" 1.8 '((10 .3) (30 .2) (100 .1)))) ((= invoer 3) (planeet$maak "Homogenix" 2.2 '((100 .3001)))) ((= invoer 4) (planeet$maak "de aarde" 9.81 '((5 2.22) (15 1.5) (20 1) (30 .8) (40 .5) (100 .25)))) ((= invoer 5) (planeet$maak "Krypton" 2 '((7 .27) (17 .16) (30 .08) (60 .05)))) ((= invoer 6) (planeet$maak "Dinges" .95 '((20 0) (22.5 .2) (40 .3) (60 .2) (80 .15) (110 .075)))) (else (kies-planeet)))))) ;Tot slot zijn er nog de definities van de globale variabelen die we gebruikt hebben. (define initiele-maanlander (maanlander$maak 50 0 20)) (define veilige-snelheid -2) (define dt 1) (define max-kracht 1) (define massa 1) (define max-verbruik 1) ;Om het probleem van de op-en-neer-dansende maanlanders op te lossen volgt ;hier een alternatieve pas-aan: ;dt wordt hier in stukjes verdeeld (verfijning) en de oude pas-aan wordt dus telkens ;opnieuw berekend maar dan met dt/verfijning. Dit levert een veel nauwkeuriger ;resultaat op maar het duurt dan ook iets langer eer het op het scherm komt. ;Dat dit systeem veel nauwkeuriger is kan men makkelijk inzien door een vrije val ;uit te voeren op de planeet "Homogenix". De "terminale snelheid" (-2,70756...) zal ;met deze nieuwe pas-aan veel sneller bereikt worden dan met de oude, waardoor ;het schokkend effect weggewerkt wordt! ;Natuurlijk is deze pas-aan2 identiek aan pas-aan voor een verfijning van 1. ;In principe zou de snelheidsverandering moeten ge•ntegreerd worden over dt, maar ;dat laat ik over aan de liefhebbers. (define (maanlanding maanlander planeet) ;sig: maanlander * planeet -> boodschap ;effect: als de maanlander op de grond is, wordt gecontroleerd of het toestel ; zacht geland of gecrasht is, anders wordt de positie van de maanlander ; aangepast naargelang de hoeveelheid gas die de speler geeft en wordt ; de lus opnieuw uitgevoerd, nu met die nieuwe maanlander. (toon maanlander) (if (maanlander$op-de-grond? maanlander) (einde-spel maanlander planeet) (maanlanding (pas-aan2 maanlander (hoeveel-gas) planeet 0) planeet))) (define verfijning 25) ;Dit is de hoeveelheid stukjes waarin dt wordt verdeeld. Hoe meer, hoe nauwkeuriger de ;berekeningen, maar ook hoe langer wachten tussen twee stappen. (define (pas-aan2 maanlander gas-ratio planeet iter) ;sig: maanlander * Num * planeet * Num -> maanlander ;iter moet beginnen vanaf 0 (if (= iter verfijning) maanlander (if (< (maanlander$brandstof maanlander) (* max-verbruik gas-ratio (/ dt verfijning))) ;Zorgen dat het brandstofniveau nooit negatief kan worden: we kunnen niet meer ;verbruiken dan er nog beschikbaar is! (pas-aan2 maanlander (maanlander$brandstof maanlander) planeet iter) (pas-aan2 (maanlander$maak (+ (maanlander$hoogte maanlander) (* (maanlander$snelheid maanlander) (/ dt verfijning))) (+ (maanlander$snelheid maanlander) (* (+ (- (/ (* max-kracht gas-ratio) massa) (planeet$valversnelling planeet)) (* (invsign (maanlander$snelheid maanlander)) ; wrijving is tegensteld aan de richting v.d. snelheid (/ (* (planeet$wrijving planeet (maanlander$hoogte maanlander)) (kwadraat (maanlander$snelheid maanlander))) massa))) (/ dt verfijning))) (- (maanlander$brandstof maanlander) (* max-verbruik gas-ratio (/ dt verfijning)))) gas-ratio planeet (+ iter 1)))))