Toto je starší verze dokumentu!


Cvičení 02

Úvod do cvičení

Na cvičení budeme ďalej rozširovať projekt z minulého cvičenia. Finálnym cieľom bude rozšíriť implementáciu križovatky. Križovatka bude obsahovať semafor pre každý smer. Tiež si pre každý smer budeme pamätať frontu čakajúcich vozidiel, ktoré budú opúšťať križovatku, ak majú na semafore zelenú. Na križovatke môžu čakať vozidlá rôznych typov (osobné auto, nákladné auto, autobus), ktorým opustenie križovatky trvá rôznu dobu. Celá simulácia bude prebiehať v diskrétnych časových krokoch – „tikoch“ (môžete si to predstaviť tak, že sa na stav križovatky „pozeráme“ každých x sekúnd).

Časť kódu je už pre vás pripravená, stiahnite si archív.

Do súborov trafficlight.cpp a crossroad.cpp (prípadne aj trafficlight.h a crossroad.h ak ste ich modifikovali) doplňte váš kód z prvého cvičenia. (Alternatívne môžete použiť kód zo vzorového riešenia zverejneného na stránke prvého cvičenia.) Súbory pre križovatku obsahujú nový predpripravený kód, takže ich neprepisujte celé.

První úkol - IVehicle

Ako už bolo zmienené v úvode, na križovatku prichádzajú vozidlá rôznych typov (osobné auto, autobus, nákladné auto). U jednotlivých vozidiel nás bude zaujímať, aký čas im trvá opustenie križovatky. Výpočet tohto času sa u jednotlivých typov vozidiel líši – u osobného auta rozhoduje zrýchlenie, u autobusu počet cestujúcich a u nákladného auta množstvo nákladu (v skutočnom svete by samozrejme situácia bola komplikovanejšia, ale nám bude stačiť uvedené zjednodušenie). Aby sa nám s jednotlivými typmi vozidiel lepšie pracovalo (a mohli sme napríklad vytvoriť frontu vozidiel rôznych typov), použijeme pre triedy reprezentujúce jednotlivé typy vozidiel spoločného predka – abstraktnú triedu (rozhranie) IVehicle. Jednou z výhod takejto implementácie je aj to, že neskôr môžeme bez problémov pridávať ďalšie typy vozidiel.

Zadání úkolu

Definujte v súbore ivehicle.h abstraktnú triedu IVehicle.

  • Trieda bude obsahovať čiste virtuálnu verejnú metódu double timeToLeaveCrossroad() const.
  • Okrem toho pridajte virtuálny deštruktor (pre túto úlohu nie je nevyhnutný, je však dobrým zvykom pridať virtuálny deštruktor do každej abstraktnej triedy – bližšie info na prednáške).
  • Súbor ivehicle.cpp v tomto prípade nemusí vôbec existovať, prípadne môže ostať prázdny. V praxi by sme ho využili na definície metód, ktoré by boli spoločné pre všetky vozidlá.

Druhý úkol - Car

Trieda Car je prvým z potomkov triedy IVehicle, ktorý reprezentuje osobný automobil. Auto má zadaný čas akcelerácie z 0 na 100 km/h. Čas (v tikoch) na opustenie križovatky sa počíta ako štvrtina času akcelerácie z 0 na 100 km/h.

Zadání úkolu

Implementuje triedu Car – potomka triedy IVehicle. Implementáciu umiestnite do súborov car.cpp a car.h. Trieda bude obsahovať nasledujúce verejné metódy:

  • Konštruktor s jedným parametrom typu double, ktorý predstavuje čas akcelerácie z 0 na 100 km/h. Ak by bol zadaný parameter záporný, zmeňte znamienko na kladné.
  • Metódu timeToLeaveCrossroad (zdedená od IVehicle): počíta sa podľa vzorca v predchádzajúcom odstavci.

Třetí úkol - Truck

Trieda Truck je druhým typom vozidla (potomkom IVehicle). Čas na opustenie križovatky závisí od hmotnosti nákladu a počíta sa pomocou nasledujúceho vzorca: Vzorec 1 kde cargo je aktuálna hmotnosť nákladu. (Vzorec je odvodený z logistickej funkcie. Hoci v našom prípade nemá použitie tejto funkcie reálny fyzikálny základ, ide o funkciu, ktorá má v praxi veľký význam.)

Zadání úkolu

Implementuje triedu Truck – potomka triedy IVehicle. Implementáciu umiestnite do súborov truck.cpp a truck.h. Trieda bude obsahovať nasledujúce verejné metódy:

  • Bezparametrický konštruktor – počiatočná hmotnosť nákladu je 0.
  • void setCargo(double weight) – nastaví hmotnosť nákladu na hodnotu zadanú ako parameter. Ak je nová hmotnosť záporná, zmeňte znamienko na kladné.
  • Metódu timeToLeaveCrossroad (zdedená od IVehicle): počíta sa podľa vzorca v predchádzajúcom odstavci, pre výpočet e^x použite exp(x) (exp je funkcia definovaná v hlavičkovom súbore cmath).

Čtvrtý úkol - Bus

Posledným typom vozidla v našej hierarchii je autobus. Čas na opustenie križovatky závisí od maximálneho množstva cestujúcich (kapacity autobusu) a od aktuálneho počtu cestujúcich a počíta sa podľa vzorca 0.25 + (0.25*passengers)/capacity kde passengers je aktuálny a capacity maximálny počet cestujúcich.

Zadání úkolu

Implementuje triedu Bus – potomka triedy IVehicle. Implementáciu umiestnite do súborov bus.cpp a bus.h. Trieda bude obsahovať nasledujúce verejné metódy:

  • Konštruktor s jedným parametrom typu unsigned int – parameter predstavuje kapacitu autobusu, na začiatku je autobus prázdny.
  • void addPassengers(unsigned int number) – pridá number cestujúcich, maximálne však do kapacity autobusu.
  • void removePassengers(unsigned int number) – odoberie number cestujúcich. Ak je cestujúcich v autobuse menej, bude nový počet cestujúcich 0.
  • Metódu timeToLeaveCrossroad (zdedená od IVehicle): počíta sa podľa vzorca v predchádzajúcom odstavci.
Otestujte si správnosť implementácie triedy Ivehicle a jej potomkov (cieľ vehicles_tests)

Pátý úkol - doplnenie križovatky

Poslednou súčasťou je doplnenie križovatky tak, ako bolo spomenuté v úvode (svetelná križovatka, fronty vozidiel). Hlavná časť simulácie (metóda tick) je už pre vás pripravená, úlohou je doplniť ďalšie pomocné metódy, ktoré táto metóda využíva. Metóda tick rieši najmä prepínanie stavov jednotlivých semaforov (na hlavnej ceste je zelená dlhšie) a volá metódu pre odstránenie vozidiel z fronty. Metóda je implementovaná tak, že na vedľajšej ceste je zelená 1 tik, na hlavnej ceste je zelená 2 tiky.

Ako dobrovoľnú (nebodovanú) domácu úlohu môžete implementáciu metódy tick vylepšiť. Môžete napríklad vytvoriť „inteligentnú“ križovatku, kde doba, po ktorú v danom smere svieti zelená, nejakým spôsobom závisí od aktuálneho množstva vozidiel čakajúcich v jednotlivých smeroch. Pre pokročilých študentov: upravte implementáciu tak, aby u jednotlivých vozidiel bolo možné zistiť, akú dobu (počet tikov) strávili na križovatke. Skúste optimalizovať metódu tick tak, aby priemerná doba čakania bola čo najmenšia.

Zadání úkolu

Upravte triedu Crossroad nasledovne:

  • Pridajte atribúty pre reprezentáciu štyroch semaforov (jeden pre každý smer, môžete napr. použiť pole) a pre fronty vozidiel – inštancie triedy QueueOfVehicles (opäť pre každý smer jedna fronta). Trieda QueueOfVehicles je pre vás pripravená v súboroch queueofvehicles.h a queueofvehicles.cpp, pričom na jej použitie vám stačí nahliadnuť do dokumentácie jednotlivých metód (nie je nutné rozumieť, ako presne je trieda implementovaná).
  • Pridajte konštruktor s dvoma parametrami typu Direction, ktorý po vytvorení triedy nastaví hlavnú cestu podobným spôsobom ako metóda setMainroad. Ak zadané parametre nepredstavujú dva rôzne smery, použije sa ako hlavná cesta vo východo-západnom smere.
  • Upravte oba konštruktory tak, aby sa v ich inicializačných sekciách nastavila hodnota atribútu activeLight na WEST a hodnota atribútu ticksToChange na nulu (ide o pomocné atribúty využívané metódou tick, nie je nutné presne rozumieť ich významu).
  • Do oboch konštruktorov pridajte kód, ktorý zapne všetky semafory (zavolá metódu turnOn).

Pridajte nasledujúce privátne metódy:

  • TrafficLight* getLight (Direction d) – vráti ukazateľ na semafor zo zadaného smeru (prípadne nullptr ak d nepredstavuje korektný smer).
  • QueueOfVehicles* getQueue (Direction d) – vráti ukazateľ na frontu vozidiel zo zadaného smeru (prípadne nullptr ak d nepredstavuje korektný smer).
  • void dequeue(Direction d) – Ak zadaný smer je nesprávny, alebo na semafore v zadanom smere nesvieti zelená, metóda neurobí nič. V opačnom prípade metóda zo začiatku fronty v zadanom smere odstráni čo najviac vozidiel tak, aby súčet časov, ktoré vozidlá potrebujú na opustenie križovatky neprekročil 1. Ak by nastala situácia, že hneď prvé vozidlo vo fronte potrebuje na opustenie križovatky čas väčší ako 1, potom frontu opustí práve toto vozidlo.
Metódy getLight a getQueue sú využívané len v dodanej metóde tick, aby táto metóda bola nezávislá na tom, akým spôsobom si pamätáte atribúty pre semafory a fronty. Ak máte semafory a fronty reprezentované rozumným spôsobom (napr. poľom), môžete namiesto implementácie uvedených metód upraviť priamo metódu tick.

Pridajte nasledujúce verejné metódy:

  • bool isColorOn(Direction d, Color color) const – vráti true ak na semafore zo zadaného smeru svieti zadaná farba, ináč vráti false.
  • void enqueue(Direction d, IVehicle *v) – pridá zadané vozidlo do fronty zo zadaného smeru. (Ak je smer nesprávny, neurobí nič.)
  • int getNumberOfVehicles(Direction d) const – vráti počet vozidiel, ktoré čakajú vo fronte zo zadaného smeru. (Ak je smer nesprávny, vráti nulu)
  • string stateToString() const – vráti reťazec, ktorý pre každú svetovú stranu (v poradí sever, východ, juh, západ) bude obsahovať jeden riadok vo formáte <názov svetovej strany>: <počet vozidiel> vehicles, <stav semafora>, kde názov svetovej strany je anglicky veľkými písmenami, <počet vozidiel> je hodnota, ktorú vráti getNumberOfVehicles pre danú svetovú stranu a <stav semafora> je reťazec, ktorý vráti metóda stateToString pre semafor z daného smeru.
  • void printState() const – vypíše reťazec popisujúci stav križovatky vo formáte ako má stateToString a pridá na koniec ešte jedno odriadkovanie navyše.
Na prevod čísla na reťazec môžete použiť predpripravenú funkciu to_string. (V C++11 je táto funkcia defaultne dostupná.)
Jednoduchú ukážku funkcionality triedy nájdete v súbore crossroad_demo.cpp (cieľ crossroad_demo). Otestujte si správnosť vašej implementácie (cieľ crossroad_tests).

Vzorové řešení

Bude zveřejněno v pátek 2. 10. odpoledne.

Cvičenie pripravil Marek Klučár. O cvičenie sa v súčasnosti stará Peter Stanko. Ak v zadaní, pripravenom kóde alebo vzorovom riešení objavíte nejaké nedostatky/chyby, píšte prosím na stanko@mail.muni.cz

QR Code
QR Code public:pb161_fall14_cviko02 (generated for current page)