Pizzafutár

Használható segédanyagok: Haskell könyvtárainak dokumentációja, (lokális!) Hoogle, a tárgy honlapja és a BE-AD rendszerbe feltöltött beadandók. Ha bármilyen kérdés, észrevétel felmerül, azt a felügyelőknek kell jelezni, nem a diáktársaknak!

A feladatok egymásra épülnek ezért érdemes ezeket a megadásuk sorrendjében megoldani, de legalább megérteni az aktuális feladatot megelőző feladatokat! A függvények definíciójában lehet, sőt javasolt is alkalmazni a korábban definiált függvényeket (függetlenül attól, hogy sikerült-e azokat megadni).

Tekintve, hogy a tesztesetek, bár odafigyelés mellett íródnak, nem fedik le minden esetben a függvény teljes működését, határozottan javasolt még külön próbálgatni a megoldásokat beadás előtt, vagy megkérdezni a felügyelőket!

Részpontszámokat csak az elégséges szint elérése után lehet kapni!

A feladat összefoglaló leírása

A Kukutyin Dolce Vita pizzéria hálózat szeretne mobil pizzériákat indítani, amelyek napközben kitelepülnek a város különböző pontjaira és a helyszínen kiszolgálják a vevőket, illetve házhozszállítást is végeznek. Céljuk, hogy a pizzériákból minél előbb kiérjen a pizza a megrendelőhöz.

Felkértek bennünket, hogy segítsük Őket az új üzleti modelljük sikerre vitelében. A feladatul azt kaptuk, hogy a pizzériák helyszíneit ismerve, megpróbáljuk a megrendelőket a lehető legközelebbi pizzériából kiszolgálni. Egyszerűsítésként, egyszerre csak egy megrendelést tudnak teljesíteni, azaz nem kötik össze több megrendelés kiszállítását.

A cégtől megkapjuk a legfrissebb útinformációkat és a pizzériák elhelyezkedését. A feladatunk az lesz hogy ennek ismeretében megadjuk, hogy melyik pizzériából és milyen útvonalon kell teljesíteni a különböző megrendeléseket.

A dolgunk megkönnyítése céljából, a kapott leírásból készítettünk egy gráfot. A leírásban szereplő kereszteződéseket a gráf csúcsaiként (vagy csomópontjaiként), a kereszteződéseket összekötő útszakaszok pedig a gráf éleiként fogjuk jelölni. Az élek irányítottak, azaz ha egy a és b között található él, akkor a-ból b-be közvetlenül el tudunk jutni. Ez fordítva nem igaz, hacsak nincs egy b-ből a-ba mutató él is a gráfban. A feladat megoldása során feltételezhetjük, hogy a gráf összefüggő, azaz két tetszőlegesen kiválasztott csúcsa között létezik út.

A gráf reprezentációja

A gráfot (Map) egy listával adjuk meg, amelynek rendezett párok az elemei (Section). Egy rendezett pár (Section) két csúcsból (Junction) áll, amely a köztük lévő közvetlen utat adja meg. Ha egy (a,b) rendezett pár megtalálható a listában, ez azt jelenti hogy az a-ból b-be át tudunk jutni közvetlenül.

(A type kulcsszó segítségével a gráf csúcsainak azonosítására használt Junction típus az Int típus, a Section típus a Node típusból képzett párok (Node, Node) típusának egy másik neve lesz. A Map típus a Edge típusból alkotott listákat/sorozatokat fogja jelenteni, hasonlóan a String és [Char] viszonyához. A programban ez semmilyen további megszorítást nem indukál, csupán a beszédesebb függvénytípusok kialakításában segít.)

Példák gráfokra

A megoldás teszteléséhez bevezetjük a következő két gráfot:

PizzaDelivery4a3b994c7b3e671b27f0595eb0b5ee37.png

PizzaDelivery1a7aa4eefed1cda71537efb9b1b0c18d.png

Kereszteződések meghatározása (2 pont)

Az egyes műveletekhez és teszteléshez szükségünk lehet a gráfban található összes csomópontra.

Definiáljuk azt a függvényt, amelyik megadja egy gráf összes csomópontját! Az eredmény minden csomópontot egyszer tartalmazzon és ezek érték szerint növekvő sorrendben szerepeljenek!

junctions :: Map -> [Junction]

Test>
[] :: [Junction]
Test>
[2, 3] :: [Junction]
Test>
[1, 2, 3] :: [Junction]
Test>
[1, 2, 3, 4, 5, 6] :: [Junction]
Test>
[1, 2, 3, 4, 5, 6, 7, 8, 9] :: [Junction]

Elérhető csúcsok megadása (1 pont)

Adjuk meg azt a függvényt, amely egy adott csúcs szomszédait adja meg! Szomszéd alatt most azokat a csúcsokat értjük, amelyek elérhetőek a megadott csúcsból.

outNeighbours :: Map -> Junction -> [Junction]

Test>
[2, 3] :: [Junction]
Test>
[4, 5, 2] :: [Junction]
Test>
[6] :: [Junction]

Házhozszállítás útvonalainak előkészítése

A feladat megoldása során útvonalakat fogunk meghatározni, ezért az kód olvashatóságának növelése céljából bevezetjük a Route típus. A Route szinonimával jelzett lista minden esetben egy útvonalat, vagyis szomszédos csúcsok sorozatát fogja jelenteni.

type Route = [Junction]

FONTOS: A útvonalak fordított sorrendben adottak, azaz a kiindulópont mindig az utolsó eleme! Tehát, a [4,3,2,1] útvonal az 1-es csúcsból indul és a 4-es csúcsban ér véget.

PizzaDelivery387beba03261e2e9f6959ae56b8eb322.png

Egy útvonal lehetséges folytatásai (2 pont)

Amennyiben egy csúcsból több irányba is tovább tudunk haladni, úgy minden irányhoz egy új útvonalat fogunk megadni.

Adjuk meg azt a műveletet, amely egy adott útvonalat az összes lehetséges módon megpróbál egy újabb rendelkezésre álló szomszédos csúccsal bővíteni!

Ügyeljünk arra, hogy csak olyan csúcsokkal bővítsük az útvonalat, amely még nem szerepel az útvonalban! Amennyiben nincs ennek a feltételnek megfelelő egyetlen csúcs sem, akkor az eredmény egy üres lista legyen!


Test>
[] :: [Route]
Test>
[[1, 2], [3, 2], [4, 2]] :: [Route]
Test>
[[4, 3], [5, 3], [2, 3]] :: [Route]

Segítség:

Amennyiben a jelenlegi útvonalunk [3,1] úgy a map1 esetében a 2, 4, 5 sorszámú (az ábrán pirossal jelzett) csúcsokkal bővíthetjük az útvonalat.

PizzaDelivery77d11a82082494bcea29b247d842513b.png

Tehát az eredmény három útvonalat fog tartalmazni, ez a [[2, 3, 1], [4, 3, 1], [5, 3, 1]] lista. Szemléltetve:

PizzaDeliveryecd784253053844a45ff9dbe21e1cb43.png

PizzaDelivery7ecd0c7ae0793df9b73ce7ea219d613c.png

PizzaDelivery76b954057c54069a0de8dc17879f1c4e.png

Több útvonal összes folytatása (2 pont)

Adott egy útvonalsorozat, amelynek minden elemét folytatni szeretnénk.

Definiáljuk azt a függvényt, amely egy útvonalsorozat minden elemét az összes lehetséges módon megpróbál bővíteni! Használjuk a step műveletet!


Test>
[[2, 1], [3, 1]] :: [Route]
Test>
[[3, 2, 1], [4, 2, 1], [4, 3, 1], [5, 3, 1], [2, 3, 1]] :: [Route]
Test>
[[4, 3, 2, 1], [5, 3, 2, 1], [6, 4, 2, 1], [6, 4, 3, 1], [2, 4, 3, 1], [6, 5, 3, 1], [4, 2, 3, 1]] :: [Route]
Test>
[[6, 4, 3, 2, 1], [6, 5, 3, 2, 1], [4, 6, 5, 3, 1], [6, 4, 2, 3, 1]] :: [Route]

Útvonalak meghatározása

A feladat ezen részében megpróbáljuk megadni egy a csúcsból induló b csúcsban végződő legrövidebb utat vagy utakat (amennyiben több is van belőle).

Adott pontban végződő utak kiválogatása (1 pont)

Adott egy útvonalakat tartalmazó sorozat és szeretnénk tudni, hogy ezek közül melyik útvonalak érnek véget a megadott kereszteződésben.

Definiáljuk azt a műveletet, amely csak az adott pontban végződő útvonalakat adja meg!

Megjegyzés: Ne feledjük, hogy az útvonalak fordított sorrendben adottak.


Test>
[] :: [Route]
Test>
[[1]] :: [Route]
Test>
[] :: [Route]
Test>
[] :: [Route]
Test>
[[6, 4, 2, 1], [6, 4, 3, 1], [6, 5, 3, 1]] :: [Route]
Test>
[] :: [Route]

Van-e adott pontban végződő útvonal a sorozatban (1 pont)

Adjuk meg azt a függvényt, amely igazat ad vissza, ha a megadott sorozatban van olyan útvonal, amelyik a megadott pontban végződik!


Test>
False :: Bool
Test>
True :: Bool
Test>
True :: Bool
Test>
True :: Bool
Test>
False :: Bool
Test>
False :: Bool

Kezdeti útvonal megadása (1 pont)

Adjuk meg azt a függvényt, amely inicializál egy útvonal-sorozatot a megadott csúcsból! A függvény eredménye egy lista, amely egy egy elemű útvonalat tartalmaz (a megadott kiindulóponttal).

Amennyiben a csúcs nem szerepel a megadott térképen, úgy adjunk egy hibaüzenetet az error függvény segítségével!

Segítség: Az error függvénynek egy String típusú értéket kell paraméterül adni, amely maga a hiba szövege.


Test>
[[1]] :: [Route]
Test>
[[4]] :: [Route]
Test>
[[7]] :: [Route]
Test>
⊥₁ :: [Route]
⊥₁: initRoutes: invalid junction node
CallStack (from HasCallStack):
  error, called at ./PizzaDelivery.lhs:353:19 in main:PizzaDelivery

Legrövidebb útvonalak (3 pont)

Adjuk meg azt a függvényt, amely megadja az összes legrövidebb útvonalat a kiindulópontból a megadott végpontig!

A működés:

Megjegyzések:


Test>
[[2]] :: [Route]
Test>
[[5, 3, 1]] :: [Route]
Test>
[[3, 2, 4, 6, 5]] :: [Route]

Optimális útvonal kiválasztása

A függvényeink könnyebb olvashatósága érdekében, bevezetünk a Pizzeria szinonimát. Ezzel olyan kereszteződéseket fogunk jelölni, amelyekben található kitelepült pizzéria.

type Pizzeria = Junction

A feladatunk az lesz, hogy több pizzéria helyszínét ismerve, adjuk meg a legközelebbi pizzériából induló útvonalat a megrendelőhöz.

Útvonal minden pizzériából (1 pont)


Test>
[[4, 2, 1], [4, 3, 1]] :: [Route]
Test>
[[4, 2], [4, 6, 5]] :: [Route]
Test>
[[4, 2], [4, 2, 1], [4, 3, 1], [4, 6, 5]] :: [Route]

Egy optimális útvonal kiválasztása (2 pont)

Adott a pizzériák pozícióinak nemüres listája és a megrendeléshez legközelebbi kereszteződés azonosítója.

Adjuk meg azt az útvonalat amely a legkevesebb csomópontot tartalmazza! Amennyiben több azonos hosszúságú legrövidebb út is létezik, adjuk meg az elsőt ezek közül.


Test>
[3, 1] :: Route
Test>
[4, 6] :: Route
Test>
[[1], [2, 1], [3, 1], [4, 6], [5, 3, 1], [6]] :: [Route]
Test>
[[1], [2, 1], [3], [4, 3], [5, 3], [6, 4, 3]] :: [Route]
Test>
[[1], [2, 5, 7, 8, 9, 6, 1], [3], [4, 3], [5, 7, 8, 9, 6, 1], [6, 1], [7, 8, 9, 6, 1], [8, 9, 6, 1], [9, 6, 1]] :: [Route]

Megrendelések csoportosítása kiszolgálási hely szerint (4 pont)

A pizzéria minőségbiztosítási osztálya szeretné tudni, hogy a nap folyamán melyik pizzéria mennyi megrendelést teljesített. Az eddig ismerteken túl, kapunk egy listát, amelyben megtalálhatók a rendelések helyszínei (OrderList).

type OrderList = [Junction]

Adjuk meg azt a függvényt, amely a megrendelés listából előállítja csoportosítva, hogy melyik pizzéria teljesítette a megrendelést! Az eredmény egy rendezett párokból ((Pizzeria, OrderList)) álló sorozat, amely a kiszolgálást végző pizzériák szerint osztja szét a megrendelések listáját.

A működés:

clusterByDistance :: Map -> [Pizzeria] -> OrderList -> [(Pizzeria, [Junction])]

Test>
[(1, [1, 2, 3, 5]), (4, [4, 6])] :: [(Pizzeria, [Junction])]
Test>
[(1, [1, 2, 3, 5]), (6, [4, 6])] :: [(Pizzeria, [Junction])]
Test>
[(1, [1, 2]), (3, [3, 5]), (6, [4, 6])] :: [(Pizzeria, [Junction])]

Pontozás