Greeps

Het startscenario voor deze oefening vind je in ScenariosStart.zip.

Hoofdstuk 1-3

Open het scenario greeps.
Greeps zijn dieren die verzot zijn op tomaten. Ze kunnen echter niet zwemmen !!
We maken nu kennis met deze dieren.
  1. Er zijn twee methodes beschikbaar om te zien of een greep aan de rand van de wereld is. Plaats de greep op positie (2,2), en ga na dat deze methodes een ander resultaat geven.
  2. Er zijn ook twee methodes met als naam move. Zoek uit wat het verschil is.
    Als je het scenario uitvoert, dan beweegt de greep. Welke van beide methodes wordt hierbij gebruikt? Roep beide methodes interactief aan. Welk verschil valt je op bij de aanroep?
    Vraag opnieuw beide methodes nadat de greep is vastgelopen op water. Wat stel je vast?

    Oplossing

  3. Welke instantievariabelen zijn typisch zijn voor een Greep. Wat betekenen ze ?
  4. Plaats een tomatenstapel in de wereld, welke instantievariabele heeft dit object? Je moet een parameter invullen, namelijk het aantal tomaten in de tomatenstapel. Dit wordt onthouden in de instantievariabele.
  5. Met welke methode, van een TomatoPile, neem je 'interactief' een tomaat weg (ook als er geen greep is). Hierdoor verandert de vorm van de tomatenstapel, maar veel belangrijker, ook de instantievariabele wijzigt. Controleer dit. Met deze methode kan iedereen dus "melden" aan de tomatenstapel dat er een tomaat wordt weggenomen.
  6. Plaats een greep op de tomatenstapel en vraag de methode checkFood(). Er gebeurt niets. Ga na dat dit wel lukt als er twee greeps bij dezelfde TomatoPile staan.
    Controleer dat de tomatenstapel ook nu correct onthoudt hoeveel tomaten er nog zijn. Hoe onthoudt de greep dat hij een tomaat draagt ?
  7. Wat doet de greep aan de rand van de wereld? Pas het 'gedrag' aan zodat hij draait over een hoek, willekeurig gekozen tussen 10 en 90 graden.
  8. Oplossing

  9. Voeg de methode draaiAanWater() toe, zodat de greep een beetje draait in de buurt van water - draai over een willekeurig gekozen kleine hoek. Zorg voor een verschillend gedrag voor greeps die een tomaat meehebben. Een greep met tomaat draait bijvoorbeeld in de andere richting.
    Wijzig ook het 'gedrag' van de greep. Het scenario ziet er nu al heel wat beter uit.

    Oplossing

  10. Voeg een nieuwe Actor Ship toe, met de nieuwe tekening . Klik met de rechtermuisknop op de afbeelding en bewaar deze afbeelding in de juiste map van je scenario.
    Als een greep in de buurt is van het ruimteschip kan ze haar tomaat "afladen" in het schip. Werk dit uit in de methode checkShip() en pas het gedrag van de greep aan.

    Oplossing

Hoofdstuk 4

  1. Schrijf de methode initEarth() die Earth initialiseert: Zorg dat de greeps "onder" het schip zitten.
    Deze methode wordt automatisch uitgevoerd bij de start van het scenario.

    Oplossing

  2. Nu moet de gebruiker van het spel de kans krijgen om te bepalen hoeveel greeps er moeten zijn. De klasse Greenfoot kent de methode ask(...) die aan de gebruiker een vraag stelt, en het antwoord dan bewaart (om er iets mee te doen, allicht). Raadpleeg de Greenfoot-documentatie. Gebruik deze methode net vóór de for-loop in de methode initEarth(). Wat merk je nu bij het opstarten van het spel? Tel je het juiste aantal greeps? Wat gebeurt er als de gebruiker geen getal opgeeft? De methode ask vraagt een parameter: de vraag die aan de gebruiker gesteld moet worden. Wat de gebruiker ingeeft, wordt als tekst (String) teruggegeven. Omdat je hier een getal (int) nodig hebt om in de for-lus te gebruiken, moet je doen wat er als tip in de Greenfoot-documentatie staat: you can use methods like Integer.parseInt to turn the returned String into a number.

    Als de gebruiker geen getal intikt, dan crasht het programma. Je krijgt een venster te zien met grijze en rode tekst, dat zijn foutmeldingen. Scroll naar boven, daar zou je NumberFormatException moeten zien staan. Dat wijst je in de richting van de fout. (Je moet deze `fout' in het programma momenteel niet oplossen, de gebruiker moet maar netjes op de vraag antwoorden!)

    Oplossing

  3. Plaats op het scherm een tekst waarin het totaal aantal tomaten getoond wordt bij de start van het scenario. (zie boek p73). De positie van de tekst wordt uitgedrukt in "aantal cellen".

    Oplossing

  4. De greeps vertrekken allemaal in dezelfde richting, namelijk naar rechts (rotation = 0). Pas dit aan zodat elke greeps in een willekeurige richting vertrekt.
    Hoe kan je nu de greeps laten draaien vóór het scenario start? In elk geval moet dit ergens in een "constructor" worden uitgewerkt (enkel bij het maken van...). Het eenvoudigste is dit uit te werken in de klasse Greep. Voeg een default-constructor toe in die klasse, en stel een willekeurige hoek in voor elke (nieuwe) greep.

    Oplossing

  5. Pas de methode initEarth() aan zodat elke tomatenstapel een willekeurige grootte (minstens 10, maximum 30 tomaten) heeft. Werk twee mogelijkheden uit:
    1. De drie tomatenstapels zijn nog altijd allemaal even groot.
    2. De drie tomatenstapels hebben mogelijks een verschillend aantal tomaten.
    Ook de tekst moet aangepast worden.

    Oplossing

Hoofdstuk 5

  1. Doe de nodige aanpassingen zodat het scenario stopt als alle tomaten in het schip geladen zijn.

    Werk dit uit in drie stappen:

    Oplossing

Hoofdstuk 6

  1. Abstractie: In hoofdstuk 4 vertrokken er 20 greeps uit het ruimteschip bij de start van het scenario. De richting waarin een greep vertrekt wordt "willekeurig gekozen" in de constructor van Greep. Dit wordt nu aangepast zodat de greeps mooi gelijk verdeeld vertrekken in alle richtingen, en dus elke 18 graden één greep.
    Je moet hiervoor abstractie toevoegen. Dit gebeurt in 2 stappen:

    Oplossing

  2. In het huidige scenario is de positie van het schip en de tomatenstapels "hardgecodeerd".
    Schrijf de methode addShipAndGreeps() die één ruimteschip en 20 greeps toevoegt op een willekeurige positie, niet in het water. (De afbeelding van het ruimteschip mag wel gedeeltelijk in het water liggen, het middelpunt van het ruimteschip staat op de grond.)
    Zoek eerst met welke methode van Earth je kan bepalen of een bepaalde positie (x,y) grond of water is. Nu kan je volgende twee stappen uitwerken:
    • Kies een willekeurig waarde voor x.
    • Voor die x wordt een y bepaald zodat het punt (x,y) niet in het water ligt.
      Kies een willekeurige waarde voor y, dan is de kans groot dat het punt (x,y) in het water ligt.
      Voeg nu een while-lus toe met volgende pseudocode:
          zolang het punt (x,y) op water ligt
              vul y in met een nieuwe willekeurige waarde
      Als de lus stopt, dan ligt het punt (x,y) niet in het water.

    Merk op dat in deze oplossing enkel de waarde van y opnieuw berekend wordt.
    Voeg nu het ruimteschip en de 20 greeps toe op die positie.

    Pas de methode initEarth() aan zodat de nieuwe methode wordt gebruikt.

    Oplossing

  3. Een tweede mogelijkheid is het ruimteschip zo laag mogelijk te plaatsen in het scenario, maar niet in het water.
    Schrijf de methode addShipsAndGreeps(int aantal) die op twee manieren verschilt van de voorgaande: Zorg dat ook nu de greeps mooi verdeeld in alle richtingen vertrekken maar laat alle greeps 'naar boven' vertrekken (dus beperk de hoeken tot 180 graden). Om de "laagste" positie niet in water te bepalen, zoek je de grootste y-waarde zodat het punt (x,y) NIET in het water ligt.
    Je hebt terug een while-lus nodig. Schrijf eerst de pseudocode zoals hierboven. Initialiseer y op de grootst mogelijke waarde en verklein telkens met één tot je een positie hebt die niet in het water ligt.
    Om de looprichting van elke greep te berekenen gebruik je de parameter aantal.

    Oplossing

  4. De drie tomatenstapels hebben een willekeurige grootte, maar de posities zijn 'hard-gecodeerd' zodat de tomatenstapels niet op water liggen. Dit pas je aan zodat elke tomatenstapel op een willekeurige plaats komt, maar niet in het water.

    Oplossing

  5. In een volgende stap worden de tomatenstapels gelijker verdeeld over de ganse wereld. In plaats van een waarde te "kiezen" voor x, wordt de wereld verdeeld in 4 gelijke verticale stroken.
    Teken 3 (denkbeeldige) verticale lijnen op de achtergrond - op elk van die lijnen moet een tomatenstapel geplaatst worden. Hoe bereken je de afstand tussen de lijnen? Gebruik / voor de "gehele deling". Gebruik de afstand om de x-waarde te berekenen voor elke tomatenstapel.
    Pas de for-lus aan zodat je op elke verticale lijn een tomatenstapel plaatst.

    Oplossing

  6. Er werden altijd 3 tomatenstapels geplaatst. Dit wordt nu aangepast. Schrijf de nieuwe methode addTomatoPiles(int aantalPiles) die meerdere tomatenstapels in de wereld plaatst. De parameter aantalPiles stelt uiteraard het gewenste aantal tomatenstapels voor. De tomatenstapels moet ook nu gelijk verdeeld zijn over de breedte zoals hiervoor beschreven. Gebruik de parameter bij het berekenen van de afstand tussen de lijnen.
    Roep deze methode aan in de methode initEarth(), waarbij het aantal tomatenstapels willekeurig gekozen wordt tussen 4 en 10.

    Oplossing

  7. In de Images map staan drie achtergronden voor dit scenario. Pas de code aan zodat een willekeurige achtergrond wordt gekozen bij de start van dit scenario.

    Opmerking: het kan gebeuren dat je nu problemen ondervindt bij de initialisatie omdat er een achtergrond is met water over de volledige hoogte. Dit moet je niet oplossen. Je kan in dat geval de oneindige lus enkel stoppen met Ctrl-Alt-Del en Greenfoot killen. Zoek een methode van Earth waarmee je naar een andere map kan "springen" ("jump..."). Welke waarden kan je invullen voor de parameter?

    Oplossing

Hoofdstuk 7

Het einddoel is om de greeps slimmer te maken:

  1. Er zijn drie situaties waarbij de greep naar een "andere actor" moeten draaien. Schrijf hiervoor de methode turnTowards(Actor actor), waarbij het type van de parameter een actor is. Deze methode moet de greep in de richting van de opgegeven actor zetten.
    De Actor-klasse beschikt over de methode turnTowards(int x,int y) die een groot deel doet van wat je hier nodig hebt. Zoek interactief (=met klikken) de positie van een willekeurige actor en vul die waarden in om een andere greep te draaien naar die actor. De x,y-waarden voor de parameters moet je dus vragen aan de actor.

    Merk op: De methode turnTowards(int x,int y) wordt "overladen" door deze methode (andere parameters).
    Je kan de nieuwe methode ook interactief testen. Om een bepaalde actor als parameter in te vullen 'klik' je op die actor - bijvoorbeeld op het ruimteschip. Omdat de parameter van het type Actor is, kan je gelijk welke actor (greep, tomatenstapel) als parameter invullen (=aanklikken).

    Oplossing

  2. Schrijf de methode turnToShip() die een greep "richt" naar het ruimteschip.
    In deze methode moet je dus eerst het ruimteschip 'zoeken' - gebruik hiervoor een methode van World. Die methode resulteert in een List waarbij je zeker weet dat ze exact 1 object bevat. Gebruik de methodes van List uit de Java Documentation.
    Nadat het ruimteschip gevonden is kan je de greep ernaar richten.

    Oplossing

  3. Pas het gedrag van de greep aan zodat ze zoekt naar het ruimteschip indien ze een tomaat draagt.
    Merk op: Tomaten gaan nu vlotter naar het ruimteschip, maar greeps die aan water belanden zitten helemaal vast. We lossen dit later op.

    Oplossing

  4. Een greep zonder tomaat moet zich "richten" naar een tomatenstapel. Schrijf hiervoor de methode turnToTomatoPile(). In deze methode "zoekt" de greep naar een tomatenstapel in een straal van 40.
    Werk een eerste alternatief uit: de greep richt zich naar de eerste tomatenstapel. Wijzig het gedrag van de greep om dit uit te testen en merk op: (geen oplossing zoeken, enkel opmerken en begrijpen) Gebruik een methode van Actor, deze geeft een List terug. Deze lijst kan leeg zijn, maar ze kan ook meer dan één element bevatten.

    Oplossing

  5. Werk een tweede mogelijkheid uit: de greep "kiest" willekeurig een tomatenstapel.
    Merk op: (geen oplossing zoeken, enkel opmerken): Gebruik getRandomNumber - hoe bepaal je het aantal elementen in een lijst?

    Oplossing

  6. De greep die een tomaat draagt kan vastlopen aan het water - dit lossen we nu op. Het "draaien" van de greep in de methode draaiAanWater() is niet zinvol als de greep een tomaat draagt. Vervang dit en laat de greep 'kiezen' tussen mogelijkheden: Doordenkertje: Zorg dat het spel nog altijd stopt als alle tomaten weg zijn of als alle greeps verdronken zijn (eerst hoofdstuk 5 afwerken).

    Oplossing

  7. Als een greep alleen is aan een tomatenstapel dan heeft ze hulp nodig om een tomaat op te laden. Schrijf de methode roepGreeps() die alle greeps 'roept' in een straal van 80 rondom zich. Het is de bedoeling dat je alle greeps, die in de buurt zijn en nog geen tomaat hebben, zich "richten" naar de greep die hulp nodig heeft.
    Uiteraard wordt dit enkel gevraagd als de greep nog geen tomaat heeft, en als ze aan een tomatenstapel is.
    Pas ook het het gedrag van de greep aan.

    Oplossing

  8. We willen nu het ruimteschip traag in het scenario plaatsen. Het moet bovenaan het scenario starten, maar pas als je het scenario "runt" wordt het ruimteschip naar zijn juiste positie geplaatst. De greeps worden pas losgelaten als het schip is "geland".
    Pas je oplossing als volgt aan:

    Oplossing

  9. Extra: Kan je ervoor zorgen dat het spel niet direct stopt als alle tomaten zijn ingeladen. Eerst moeten alle greeps teruglopen naar het ruimteschip, en instappen.