Cursus Algoritmiek - - najaar 2005 Practicumopdracht 10 : Verkeerslichten-simulator N.B. Voordat je deze opdracht maakt, moet je eerst het elektronische cursusevaluatieformulier, dat je op de cursus-web-pagina (http://www.cs.ru.nl/~sjakie/pi1) onder het kopje Laatste nieuws kunt vinden, invullen en opsturen. Het na versturen getoonde nummer moet je noteren en in de body van je emailbericht met je programma-uitwerking mee opsturen!! Dit is de laatste practicumopdracht van deze cursus die nog ingeleverd moet worden. Ook deze opdracht is voor 2 weken: 1 e week (= de week vóór Kerst) t/m deelopdracht 10.4; maak in de 2 e week (= de week vanaf 9 januari 2006) de rest. Inleveren vóór vrijdag 13 januari 2006 om 14:00u! 1. Achtergrond Tijdens de hoorcolleges van vorige week en deze week heb je kennis gemaakt met een nieuwe manier van probleem analyseren: de aanpak via objectgericht werken. Met deze nieuwe aanpak heb je in de vorige practicumopdracht al geoefend voor een zeer eenvoudige situatie. In deze laatste practicumopdracht ga je voor een wat complexer systeem een objectgerichte analyse maken en (in een grafische omgeving) een oplossing ontwerpen en implementeren. 2. Leerdoelen Na afloop van deze opdracht ben je in staat om: de basisprincipes van objectgericht werken toe te passen in wat minder triviale situaties; bestaande class -sjablonen uit te breiden tot volledig functionele objecttypen. 3. Instructie Bestudeer allereerst het op het hoorcollege besproken onderdelen uit het Algoritmiek-dictaat (uit hoofdstuk 11) en de sheets zoals die gebruikt zijn op het college (over OO-werken) en uiteraard je op het hoorcollege gemaakte aanvullende eigen aantekeningen. Hiernaast is in een figuur getoond hoe je uiteindelijke programma er moet uitzien. Naast de vier toegangswegen tot het getekende kruispunt staan verkeerslichten opgesteld, die op de gebruikelijke wijze verspringen. We zien (op papier misschien niet helemaal duidelijk, maar toch...) dat de lichten rechtsboven en linksonder op rood staan en dat de verkeerslichten linksboven en rechtsonder blijkbaar net van groen op oranje (of geel ) zijn gesprongen en binnenkort op rood zullen springen. Even later zullen de nu op rood staande verkeerslichten op groen springen, een poosje zo blijven en dan op hun beurt verspringen naar oranje/geel etc. Via de Algoritmiek -Webpagina staat tot je beschikking het bestand verkeerssimulatie.zip, waarin [naast het ondertussen wel bekende gui_kernel.cpp en dito.h -bestand] ook het bestand kruispunt.cpp zit. Als je deze bestanden opneemt in een nieuw project, compileert en linkt, krijg je het hiernaast getoonde, ietwat uitgeklede kruispunt te zien (zie 2 e figuur). Aan jullie de taak om, uitgaande van de 2 e situatie de programmacode stap voor stap aan te passen, totdat je de eerste situatie hebt gekregen. 1
In het bestand kruispunt.cpp zitten o.a. definities (+gedeeltelijke implementaties) van de volgende constanten, objectklassen en (hulp) functies: const GSIZE ScreenSize (450, 450); const GPOINT MidScreen (ScreenSize.cx / 2, ScreenSize.cy / 2); class Kruispunt; // vóóraankondiging via een proto-type class KruispuntSimulatie : public GUI public: KruispuntSimulatie (Kruispunt&); virtual void Window (const RECT& area); virtual void Timer (const int dt); private: Kruispunt& kruispunt; ; enum Kleur Rood, Geel, Groen, Zwart, Wit, AantalKleuren ; const RGBCOLOUR& kleurnaarrgb (Kleur k) ; inline Kleur& operator++ (Kleur& k) ; void tekencirkel (Canvas& canvas, GPOINT p, int straal, Kleur k, int dim_factor); void tekenrechthoek (Canvas& canvas, GPOINT p, GSIZE s, Kleur k, int dim_factor) ; void tekenkast (Canvas &canvas, GPOINT pos, GSIZE afm); void tekenomgeving (Canvas& canvas); void tekenwegen (Canvas& canvas);..... N.B. Let er op, dat alle procedures die direct of indirect gebruik maken van de teken -procedures, de canvas -parameter moeten meekrijgen! De al aangekondigde klasse Kruispunt is vrij kaal gegeven en vooral dit onderdeel moet door jullie uitgewerkt worden teneinde de applicatie de gewenste functionaliteit te geven: class Kruispunt public: Kruispunt (); void tekenkruispunt (Canvas& canvas); void volgendetoestand (Canvas& canvas); private: ; Kruispunt :: Kruispunt () void Kruispunt :: volgendetoestand (Canvas& canvas) void Kruispunt :: tekenkruispunt (Canvas &canvas) tekenomgeving (canvas); tekenwegen (canvas); const GSIZE afm_vlicht (40,100); tekenkast (canvas, GPOINT (100,100), afm_vlicht); Je ziet hierboven dat het aangeleverde kale kruispunt blijkbaar getekend wordt door aanroep van de tekenkruispunt -methode, die behalve de omgeving en de wegen slechts één niet-van-lichtenvoorziene-kast tekent. Zie daar het begin van de jullie toebedachte opdracht om dit simulatieprogramma als een echte set verkeerslichten te laten functioneren. 2
Tot slot vind je in de aangeleverde programmacode, dat de verkeerslichtensimulator wordt gestart via de constructor-aanroep: KruispuntSimulatie :: KruispuntSimulatie (Kruispunt& k) : GUI (ScreenSize, "Kruispunt"), kruispunt (k) Deelopdracht 10.0 Voorbereiding: maak workspace met aangeleverde bestanden Maak binnen het Visual C++-systeem een nieuwe workspace (/project/een Windows application ) en plaats daarin de bestanden uit het beschikbare bestand verkeerssimulatie.zip. Zie ook het begin van dit instructiedeel. Geef het make -commando en de.h-bestanden zullen [waarschijnlijk..] automatisch opgenomen worden in het project. Overtuig je ervan, dat na linken en opstarten ervan, je Windows applicatie als uitvoer het in figuur 2 getoonde scherm geeft. Probleemschets (1) bij deze opdracht We zullen er eerst mee aan de slag gaan om binnen die ene (in de 2 e figuur) getoonde kast van dat ene verkeerslicht lampen (of zo je wilt: lichten ) te kunnen plaatsen. Elk verkeerslicht heeft drie lampen, die op hun kleur en positie na volkomen identiek aan elkaar zijn. Om je het leven (althans: bij deze opdracht) te vergemakkelijken, is in het kruispunt.cpp-bestand een functie tekencirkel opgenomen. Van deze functie gaven we reeds als header: void tekencirkel (Canvas& canvas, GPOINT p, int straal, Kleur k, int dim_factor); Een GPOINT is hierin een vóórgedefinieerde Struct met een x - en een y -veld. Als je bij aanroep van deze functie aan de dim_factor bijvoorbeeld de waarde 3 mee geeft, dan zal de getekende cirkel nog zwak de kleur van die lamp hebben (net zoals je bij een echt verkeerslicht als het groene licht brandt, toch kunnen zien dat er rood glas vóór de bovenste lamp zit). Deelopdracht 10.1 Ontwerp en implementeer een Lamp -objectklasse Analyseer de benodigde opbouw en functionaliteit van een Lamp -object en ontwerp en implementeer een Lamp -objectklasse. Gebruik, net zoals dat ook gebeurd is voor reeds (gedeeltelijk) gedefinieerde methodes of gewone functies zoals tekenkast en tekenwegen, een methode met de naam tekenlamp (waarbij als eerste parameter het canvas mee gegeven wordt en als tweede parameter een boolean waarde is_aan ), om een lamp-object op het scherm af te beelden. Als een lamp aan moet worden afgebeeld, gebruik je een andere dimfactor dan wanneer hij uit is (b.v. dimfactor 1 versus 3). De lichten van de 1 e figuur zijn gemaakt met een LampGrootte 12 (kortom: neem diezelfde waarde voor de straal van je lampen). De plaats waarop die lamp getoond moet worden, kan bepaald worden uit de GPOINT-waarde die eerder bij initialisatie van het object is meegegeven. Test deze Lamp-klasse uit, door zolang even vanuit de meegegeven methode void Kruispunt :: tekenkruispunt (Canvas &canvas); bijvoorbeeld midden op het scherm (neem hiervoor de vóórgedefinieerde constante MidScreen ) een rode lamp te tonen (en als dat correct verloopt, haal die losse lamp daar dan weer weg!). Deelopdracht 10.2 Ontwerp en implementeer een Verkeerslicht -objectklasse Analyseer de benodigde opbouw en functionaliteit van een Verkeerslicht -object en ontwerp en implementeer zo n objectklasse. Uiteraard bevat een verkeerslicht (-variabele) drie lampen (rood, geel/oranje, groen) die op onderling gelijke afstanden van elkaar staan en die passen binnen een (getekende) kast. In de voorbeelden van beide figuren zijn de kastdimensies 40 bij 100 genomen. Als je een verkeerslicht-object zodanig implementeert, dat de GPOINT-coördinaten van het verkeerslicht overeenkomen met het midden van de kast (de omtrek) van dat verkeerslicht, dan is daarna de plaats van de gele/oranje lamp gemakkelijk te bepalen. Hou er verder rekening mee dat het x -veld horizontaal gerekend is en het y -veld vertikaal, 3
dat de oorsprong (0,0) van het coördinatenstelsel in de linkerbovenhoek zit en dat de y -coördinaat groter (positiever) wordt naar beneden toe. Test de werking van deze objectklasse uit door bijvoorbeeld weer midden op de kruising een testverkeerslicht te plaatsen en dat op rood te zetten. Het kan nodig zijn, dat je bij nader inzien ontwerp en/of implementatie van je Lamp-klasse moet aanpassen. Deelopdracht 10.3 Kruispunt -klasse: implementeer constructor en teken-methode In het gegeven bestand kruispunt.cpp zit een zwaar onvolledige versie van een Kruispunt - objectklasse. Analyseer de benodigde klasse-velden, voeg die toe aan de Kruispunt -objectklasse en implementeer en/of verbeter de constructor- en de tekenkruispunt-methodes. Suggestie: plaats de 4 benodigde verkeerslichten in een rij van verkeerslichten; hierdoor kun je ze eenvoudiger aansturen (dan vaak via een herhaling van 0 t/m 3...). Zet voor het uittesten even 2 verkeerslichten op groen en 2 op rood en test uit of het gehele kruispunt met zijn verkeerslichten goed wordt weergegeven. Het kan nodig zijn, dat je bij nader inzien je Verkeerslicht-klasse moet aanpassen. Probleemschets (2) bij deze opdracht Misschien is het je tijdens het bekijken van de gegeven code opgevallen, dat gelijk in het begin bij de eerste [primitieve] definitie van de: class KruispuntSimulatie : public GUI de optie public GUI staat en dat een van de methoden van deze klasse is: virtual void Timer (const int dt); en dat via menu-keuzes de methoden starttimer en stoptimer kunnen worden geactiveerd. We willen de verkeerslichten op een regelmatige wijze aansturen, waarbij om de zoveel (milli-) seconden de toestand van de verkeerslichten verandert. Het echte aansturen van de verkeerslichten bij dit kruispunt gebeurt met behulp van die meegeleverde Kruispuntsimulatie -klasse, waar in de Timer -methode steeds (na elk bij starttimer aangegeven tijdsinterval) de Kruispunt-methode volgendetoestand wordt aangeroepen. Als voorbeeld geven we, dat na de aanroep starttimer(500); vervolgens elke 500 milliseconden de Timer-methode wordt geactiveerd en de daarin geplaatste code uitgevoerd. We beperken ons in deze opdracht tot sets van telkens twee verkeerslichten (bijvoorbeeld linksboven+rechtsonder versus rechtsboven+linksonder) waarbij beide verkeerslichten van zo n set in dezelfde toestand verkeren. We gaan nu eerst de buiten-werking -fase (waarbij -zoals vaak s nachts gebeurt- alleen de oranje lampen van de beide verkeerslichtensets staan te knipperen) implementeren en daarna de in-werking - fase waarbij de groen-oranje-rood-volgorde correct wordt afgewerkt. Deelopdracht 10.4 a) Het verkeersregelsysteem in de buiten-werking -fase Analyseer de voor een buiten werking -fase benodigde functionaliteit van de Kruispunt - en eventueel ook KruispuntSimulatie -objectklasse en ontwerp en implementeer die. Je mag zelf bepalen of je het knipperen van de geel/oranje-lichten van beide sets afwisselend of gelijktijdig laat gebeuren. Test de werking uit en pas zonodig je code aan. N.B. 1) Je kunt [uiteraard afhankelijk van de rest van je code] eventueel een licht op uit zetten, door het de kleur zwart te geven. 2) Als je ontdekt dat bij het weer uitschakelen van die knippertoestand de geel/oranje-lampen blijven branden, laat dat probleempje dan voorlopig rusten en denk nog eens rustig na over hoe je dat kunt verhelpen (en doe dat later dan inderdaad een keer). 4
Probleemschets (3) bij deze opdracht We gaan nu de uiteindelijke versie ontwikkelen, waarbij de verkeerslichten in een wel-actief -toestand de groen-geel-rood-fasen doorlopen en daarmee het verkeer regelen. Uiteraard moet bij een kruispunt de verkeersveiligheid gewaarborgd zijn en daarom is een klein tijdsverschil ingebouwd tussen het op rood springen van de ene set en het vervolgens op groen springen van de andere set. Schematisch hebben we dan te maken met [bijvoorbeeld] de volgende fasen, waarin de tijdsintervallen in een of andere eenheid zijn gegeven (voor het gemak: Timer -eenheden): Set 1 <= = = = = = = rood = = = = = = => <= = groen = = > <=oranje=> Set 2 <= = groen = = > <=oranje=> <= = = = = = = = rood = = = = = = => Tijdsintervallen: 1 6 3 1 6 3 We moeten dus ergens (al dan niet in een globale constante) de fase/interval-gegevens opslaan en na elke Timer -tik laten in die fase/interval-gegevens nazoeken of de Kruispunt.volgendeToestandmethode geactiveerd moet worden. En àls die laatste methode geactiveerd moet worden, moet natuurlijk duidelijk zijn, hoe de verschillende lichten aan- of uitgeschakeld moeten worden. Deelopdracht 10.5 b) Het verkeersregelsysteem in de in-werking -fase Analyseer de voor zo n in-werking -fase verder benodigde functionaliteit van de Kruispunt - objectklasse en ontwerp en implementeer die. Denk daarbij dus aan de verschillende fasen die een knipperende verkeerslichtencombinatie bij zo n kruispunt moet kunnen doorlopen en verzin zelf een geschikte datastructuur en een erbij behorende algoritme om de implementatie op een gemakkelijk mogelijke wijze te laten gebeuren. Test het geheel uit en pas je code zonodig aan. Suggestie: voeg een menu-optie toe om het systeem van de buiten-werking -toestand in de inwerking -toestand te laten overgaan en implementeer die. Pas eventuele resterende problemen in je applicatie aan, zodat je een correct werkend verkeersregelsysteem overhoudt. 4. Producten Als producten moet je je (uitgeteste!) C++-code inleveren (van deelopdrachten 10.1 t/m 10.5), die ervoor zorgt dat je programma voldoet aan de daar gevraagde specificaties en waarbij je uitwerking voldoet aan de kwaliteitscriteria zoals die gesteld worden (zie bij punt 5. Zelfreflectie). ). Let ook op het aanwezig zijn van de vereiste commentaarregels in elke functie/procedure. Lever indien mogelijk ook schermafdrukken van de uiteindelijke kruispunt/verkeerslichtensituatie in. 5. Zelfreflectie Via de Algoritmiek-webpagina (of direct via www.cs.kun.nl/~sjakie/pi-info/algemeen/criteria.html) kom je uit op criteria die we gebruiken voor de kwaliteit van je ingeleverde werk. Die criteria liggen o.a. op het gebied van naamgeving, lay-out, structuur, programmeerstijl, aanpak, realisering, en een juist gebruik van taalprimitieven. Ga voor jezelf na [voor zover van toepassing] in hoeverre je uitwerkingen voldoen aan deze criteria. => Inleveren van je producten: vóór vrijdag 13 januari 2006, 14:00 uur, en wel door de uiteindelijke inhoud van je [totale].cpp-bestand [ruim] op tijd op te sturen naar Algoritmiek@science.ru.nl, als losse attached file. Plaats in de email-subjectregel: Algoritmiek opdracht 10. Plaats duidelijk de namen, studentnummers en studierichting van zowel jezelf als je practicumpartner in alle meegestuurde bestanden, evenals het nummer van deze opdracht. Doe dit plaatsen van namen + studentnrs + Algoritmiek + opdrachtnummer óók in het message-deel van je email. Nogmaals: in de body van je email moet je het verkregen cursusevaluatie-getal vermelden! 5