PROG-2 logo
Neprihlásený používateľ

Špecifikácia zadania

Cieľom zadania je napísať konzolovú aplikáciu v jazyku C, ktorá bude simulovať súboj dvoch armád na bojovom poli. Armády budú pozostávať z bojových jednotiek. Každá jednotka môže mať rôzne predmety v inventári (z databázy predmetov). Používateľ si nakonfiguruje obidve armády pred začiatkom súboja. Súboj prebieha po kolách, v ktorých sa jednotky navzájom zasahujú pomocou svojich predmetov v inventári. Súboj môže skončiť víťazstvom niektorej z armád alebo remízou. V tomto zadaní si precvičíte polia, pointre, štruktúry a prácu s command-line argumentmi.

Workflow

Vaša konzolová aplikácia napísaná v jazyku C bude fungovať v týchto krokoch:

  1. Spustenie skompilovaného programu v termináli.
    • Používateľ v tomto kroku môže zadať $N$ - nepovinný CMD argument.
  2. Vytvorenie armád používateľom.
    • Počas načítavania jednotiek môže dôjsť ku chybovým situáciám, ktoré treba odhaliť.
  3. Výpis armád pred začiatkom súboja.
  4. Súboj prebiehajúci v kolách. Jedno kolo súboja sa skladá z nasledovných krokov.
    • Výpis pred kolom.
    • Útok obidvoch armád.
    • Výpis spôsobeného zranenia.
    • Aktualizácia armád.
    • Výpis po kole.
  5. Vyhodnotenie súboja.


Obrázok 1: Workflow diagram programu (veľký obrázok).


Bojové jednotky

V simulácii proti sebe bojujú dve armády. Každá armáda obsahuje bojové jednotky. Armáda musí mať počet jednotiek v intervale $\langle1,5\rangle$.

Štruktúra UNIT

Bojová jednotka je reprezentovaná štruktúrou UNIT.

#define MAX_NAME 100 // maximalna dlzka nazvu/mena

typedef struct unit {
    char name[MAX_NAME + 1]; // meno jednotky
    const ITEM *item1; // prvy predmet v inventari
    const ITEM *item2; // druhy predmet v inventari
    int hp; // zdravie
} UNIT;

Sloty

Bojová jednotka môže mať vo svojom inventári 1 alebo 2 predmety. Kapacita inventára je 2 sloty. To znamená, že jednotka môže byť vybavená 1 alebo 2 predmetmi, ktorých súčet slotov nepresahuje hodnotu 2.



Obrázok 2: Ilustrácia slotov (veľký obrázok).


Predmety

Každá bojová jednotka musí mať aspoň 1 predmet v inventári. Najviac môže mať 2 predmety. Pri vytváraní armády vyberá používateľ svojim jednotkám predmety z dodanej databázy.

Štruktúra ITEM

Každý predmet je reprezentovaný štruktúrou ITEM.

#define MAX_NAME 100 // maximalna dlzka nazvu/mena

typedef struct item {
    char name[MAX_NAME + 1]; // nazov predmetu
    int att; // utok
    int def; // obrana
    int slots; // pocet slotov, ktore zabera predmet v inventari
    int range; // utocny dosah/dostrel predmetu
    int radius; // sirka zasahu (alebo damage radius)
} ITEM;

Databáza predmetov

Jednotky môžu byť vybavené len takými predmetmi, ktoré pochádzajú z dodanej databázy predmetov. Táto databáza je reprezentovaná poľom items. Toto pole je deklarované v hlavičkovom súbore data.h a definované v zdrojovom súbore data.c.

#define NUMBER_OF_ITEMS 16 // velkost databazy predmetov

const ITEM items[NUMBER_OF_ITEMS] = {
        // predmety, ktore zaberaju 1 slot, zoradene podla atributu 'att'
        {.name = "wand", .att = 12, .def = 4, .slots = 1, .range = 4, .radius = 2}, // kuzelna palica
        {.name = "fireball", .att = 11, .def = 0, .slots = 1, .range = 3, .radius = 3}, // ohniva gula
        {.name = "sword", .att = 9, .def = 2, .slots = 1, .range = 0, .radius = 0}, // mec
        {.name = "spear", .att = 6, .def = 1, .slots = 1, .range = 1, .radius = 1}, // ostep
        {.name = "dagger", .att = 4, .def = 0, .slots = 1, .range = 0, .radius = 0}, // dyka
        {.name = "rock", .att = 3, .def = 0, .slots = 1, .range = 2, .radius = 1}, // skala
        {.name = "armor", .att = 2, .def = 7, .slots = 1, .range = 0, .radius = 0}, // brnenie
        {.name = "shield", .att = 2, .def = 6, .slots = 1, .range = 0, .radius = 0}, // stit
        {.name = "gloves", .att = 1, .def = 4, .slots = 1, .range = 0, .radius = 0}, // rukavice
        {.name = "helmet", .att = 1, .def = 5, .slots = 1, .range = 0, .radius = 0}, // helma
        {.name = "aura", .att = 0, .def = 8, .slots = 1, .range = 0, .radius = 0}, // ochranna aura

        // predmety, ktore zaberaju 2 sloty, zoradene podla atributu 'att'
        {.name = "cannon", .att = 12, .def = 0, .slots = 2, .range = 4, .radius = 4}, // kanon
        {.name = "axe", .att = 10, .def = 2, .slots = 2, .range = 1, .radius = 1}, // sekera
        {.name = "hammer", .att = 8, .def = 2, .slots = 2, .range = 1, .radius = 2}, // kladivo
        {.name = "crossbow", .att = 5, .def = 1, .slots = 2, .range = 3, .radius = 0}, // kusa
        {.name = "slingshot", .att = 2, .def = 0, .slots = 2, .range = 2, .radius = 1} // prak
};

Vytvorenie armády

Používateľ po spustení programu vytvorí obidve armády. Bojové jednotky sa zadávajú z klávesnice (stdin). Povolený počet jednotiek v armáde je $\langle1,5\rangle$. Počiatočné zdravie každej jednotky je HP=100. Počas vytvárania armády môžu nastať chybové situácie, ktoré je nutné odhaliť.

Formát načítania

Formát načítania obidvoch armád je nasledovný:

  • Armáda 1:
    • Zadá sa počet jednotiek.
    • Zadajú sa všetky jednotky.
  • Armáda 2:
    • Zadá sa počet jednotiek.
    • Zadajú sa všetky jednotky.

Každá jednotka sa načíta v samostatnom riadku, ktorý má nasledovný tvar.

unit_name item1 item2

Vysvetlivky:

  • unit_name - meno jednotky, tester ho vždy zadá a nikdy nebude dlhšie ako MAX_NAME
  • item1 - názov 1. predmetu v inventári (jednotka musí mať aspoň jeden predmet)
  • item2 - názov 2. predmetu v inventári (tento predmet nie je nutné zadať)

Po zadaní názvu predmetu sa vyhľadá konkrétny predmet v databáze items. Pod textom uvádzame príklad vstupu (stdin), pomocou ktorého sa načítajú obidve armády (prvá armáda má 2 jednotky a druhá armáda má 4 jednotky).

2
Palo cannon
Pankrac shield rock
4
Jaro dagger gloves
Bohus slingshot
Bohdan rock
Jergus spear spear

Chybové situácie

Počas vytvárania armády môže nastať niektorá zo 4 chybových situácií (nebudú sa kombinovať). Ak program odhalí niektorú z nich, vypíše sa príslušné chybové hlásenie a program skončí (return 0).

  • ERR_UNIT_COUNT - táto chyba nastane, keď zadaný počet jednotiek nepatrí do intervalu $\langle1,5\rangle$. Nasleduje ukážka vstupu, ktorý spôsobí uvedenú chybu (prvá armáda má 6 jednotiek).
    6
    peter sword
    john dagger wand
    mike aura gloves
    steve gloves shield
    tom wand wand
    kane dagger dagger
    2
    tim crossbow
    paul fireball rock
  • ERR_ITEM_COUNT - táto chyba nastane, keď jednotka nemá zadaný predmet alebo je počet zadaných predmetov viac ako 2. Nasleduje ukážka vstupu, ktorý spôsobí uvedenú chybu (jednotka s menom paul má až 3 predmety).
    3
    peter sword
    john dagger wand
    mike sword
    5
    tim crossbow
    paul fireball rock armor
    ivan wand gloves
    igor axe
    robo slingshot
  • ERR_WRONG_ITEM - táto chyba nastane, keď používateľ zadá neplatný názov predmetu (neexistuje v databáze). Nasleduje ukážka vstupu, ktorý spôsobí uvedenú chybu (jednotka s menom paul má zadané neplatné názvy obidvoch predmetov).
    3
    peter sword
    john dagger wand
    mike aura fireball
    2
    tim crossbow
    paul paper dart
  • ERR_SLOTS - táto chyba nastane, keď má jednotka v inventári 2 predmety a súčet ich slotov je viac ako 2. Nasleduje ukážka vstupu, ktorý spôsobí uvedenú chybu (jednotka s menom gabe má predmety, ktorých súčet slotov je 4).
    1
    gabe hammer crossbow
    1
    henry sword

Výpis armád pred začiatkom súboja

Pred začiatkom súboja sa vypíše podrobný prehľad oboch načítaných armád (pod sebou). V armáde sa vypíšu všetky vytvorené jednotky. Pre každú jednotku sa vypíše jej meno, predmety a zdravie. Nasledujúci príklad ilustruje vstup do programu a k nemu prislúchajúci výpis armád.

Vstup:

1
Gejza crossbow
2
Alojz hammer
Felix armor spear

Výpis armád pred začiatkom súboja:

Army 1
    Unit: 0
    Name: Gejza
    HP: 100
    Item 1: crossbow,5,1,2,3,0

Army 2
    Unit: 0
    Name: Alojz
    HP: 100
    Item 1: hammer,8,2,2,1,2

    Unit: 1
    Name: Felix
    HP: 100
    Item 1: armor,2,7,1,0,0
    Item 2: spear,6,1,1,1,1

Vysvetlivky:

  • Riadok s textom Unit: predstavuje index jednotky resp. jej pozíciu na bojovom poli.
  • V riadku s výpisom konkrétneho predmetu sa za názvom predmetu vypíšu postupne atribúty att, def, slots, range a radius.

Pravidlá formátovania:

  • Za výpisom každej bojovej jednotky musí byť vypísaný 1 prázdny riadok.
  • Musia sa dodržať predpísané menovky Army, Unit:, Name:, HP:, Item 1: a Item 2:
  • Toleruje sa ľubovoľné množstvo medzier v riadku (na začiatku, v strede, na konci), t.j. môžete použiť ľubovoľné horizontálne odsadenie alebo horizontálne rozostupy medzi hodnotami.
  • Atribúty predmetov musia byť oddelené čiarkou. Za posledným atribútom (radius) nesmie byť čiarka.
  • Kontrola je case-insensitive.

Príklad očakávaného správneho výstupu a reálneho výstupu programu, ktorý bude testerom tolerovaný.

Očakávaný správny výstup

Army 1
    Unit: 0
    Name: Gejza
    HP: 100
    Item 1: crossbow,5,1,2,3,0

Army 2
    Unit: 0
    Name: Alojz
    HP: 100
    Item 1: hammer,8,2,2,1,2

    Unit: 1
    Name: Felix
    HP: 100
    Item 1: armor,2,7,1,0,0
    Item 2: spear,6,1,1,1,1

Výstup tolerovaný testerom

Army 1
Unit:0
Name:Gejza
HP:    100
Item 1:    crossbow, 5, 1, 2, 3, 0

Army 2
Unit:   0
Name:    Alojz
HP:100
Item 1:   hammer,8,  2, 2 , 1 , 2

Unit: 1
Name: Felix
HP:      100
Item 1:    armor,2 , 7 , 1 , 0 , 0
Item 2: spear,6 ,1 ,1 ,1 , 1

Bojové pole

Bojové pole si predstavujeme ako 1D pole, v ktorom sú umiestnené bojové jednotky oboch armád.

Vlastnosti a rozmiestnenie jednotiek

Bojové pole je rozdelené na 10 pozícií (5 pozícií vľavo pre armádu 1 a 5 pozícií vpravo pre armádu 2). Pozície sa indexujú od stredu (hranice medzi armádami) smerom von.



Obrázok 3: Vizualizácia bojového poľa (veľký obrázok).


Po načítaní armády sa rozmiestnia jednotky na bojové pole. Indexy načítaných jednotiek v rámci jednotlivých armád sa musia zhodovať s pozíciami na bojovom poli. Pre rozmiestnenie jednotiek platí, že jednotka s indexom 0 je najbližšie k stredu bojového poľa. Jednotka s indexom 1 je o 1 pozíciu ďalej od stredu, atď.

V nasledovnom príklade ilustrujeme rozmiestnenie konkrétnych jednotiek na bojové pole.

5
Milan hammer
Jano fireball aura
Palo crossbow
Peter spear armor
Lukas spear armor
5
Adam spear armor
Stano crossbow
Riso wand
Fero sword shield
Rudo fireball aura


Obrázok 4: Rozmiestnenie jednotiek na bojovom poli (veľký obrázok).


Range a radius

Atribút $range$ predstavuje útočný dosah konkrétneho predmetu. Ak platí $pos <= range$, kde $pos$ je pozícia bojovej jednotky na bojovom poli, tak vtedy môže jednotka vykonať s daným predmetom útok. Cieľom každého útoku je vždy nepriateľská jednotka na pozícii 0.

Atribút $radius$ predstavuje akčný rádius konkrétneho predmetu. Hovorí o tom, koľko jednotiek sa zasiahne navyše okrem cieľovej nepriateľskej jednotky. Rádius sa šíri od stredu bojového poľa smerom von. Ak napr. $radius = 2$, znamená to, že okrem cieľovej nepriateľskej jednotky na pozícii 0 sa zasiahnu ešte 2 nepriateľské jednotky, konkrétne jednotky na pozíciách 1 a 2.

Na obrázku 5 vidíme ilustráciu útoku jednotky s menom Riso na pozícii 2 v armáde 2, ktorá sa snaží zaútočiť na nepriateľskú armádu 1 (vľavo). Jednotka Riso môže vykonať útok predmetom wand, nakoľko tento predmet má útočný dosah 4 (platí, že $pos <= range$). Jednotka Riso vykoná útok na nepriateľské jednotky v armáde 1 na pozíciách 0,1,2 nakoľko predmet wand má akčný rádius 2.



Obrázok 5: Význam atribútov range a radius (veľký obrázok).


Súboj

Po vytvorení a výpise armád začína súboj, ktorý prebieha v kolách pokiaľ niektorá z armád nestratí všetky svoje jednotky (alebo obidve armády súčasne).

Kolo

Postupnosť krokov v každom kole sa dá vyjadriť nasledujúcim pseudokódom.

while(!is_game_over()){
    print_units(); // vypis armad na zaciatku kola
    attack_army(); // armada 1 vykona utok
    attack_army(); // armada 2 vykona utok
    print_damage(); // vypis sposobenych zraneni
    update_army(); // armada 1 sa aktualizuje
    update_army(); // armada 2 sa aktualizuje
    print_units(); // vypis armad na konci kola
}

Vysvetlivky:

  • Funkcia is_game_over() zistí, či súboj armád neskončil. To nastane, ak existuje armáda, ktorá stratila všetky svoje jednotky. Súboj môže skončiť aj remízou.
  • Funkcia print_units() vypíše jednotky v obidvoch armádach (vypíšu sa len mená a zdravie).
  • Funkcia attack_army() vykoná útok všetkých jednotiek v armáde útočníka na armádu obrancu.
  • Funkcia print_damage() vypíše prehľad o zraneniach spôsobených jednotlivými jednotkami počas útoku (vypíšu sa len tie jednotky a predmet, ktoré sa reálne zúčastnili útoku).
  • Funkcia update_army() aktualizuje armádu. To znamená, že sa aktualizuje zdravie všetkých jednotiek podľa spôsobeného zranenia a z armády sa odstránia všetky jednotky, ktoré boli počas útoku zničené, t.j. $HP <=0$.
Funkcia print_units()

Táto funkcia má za úlohu vypísať aktuálny stav oboch armád. Vypíše sa len meno a zdravie každej jednotky. Na začiatku výpisu sa zobrazí poradové číslo armády, ktorá sa vypisuje. Meno a zdravie sú oddelené čiarkou. Jednotky sú oddelené medzerami. Pod textom je ukážka výstupu z tejto funkcie.

1: Niko,100 Odin,100
2: Oldo,100 Ondro,100 Oskar,100 Oto,100
Funkcia print_damage()

Táto funkcia má za úlohu vypísať pre každú jednotku v oboch armádach zranenie, ktoré spôsobila nepriateľovi. Každá jednotka, ktorá spôsobila zranenie sa vypíše v samostatnom riadku. Vo výpise figurujú len tie jednotky a predmety, ktoré reálne útočili. Pod textom je ukážka výstupu z tejto funkcie.

1,Niko,gloves:       [Oldo,1]
1,Niko,wand:         [Oldo,12] [Ondro,10] [Oskar,7]
1,Odin,fireball:     [Oldo,11] [Ondro,9] [Oskar,6] [Oto,9]
2,Oldo,dagger:       [Niko,1]
2,Ondro,axe:         [Niko,2] [Odin,10]
2,Oskar,rock:        [Niko,1] [Odin,3]

Štruktúra jedného riadku výpisu:

  • Najprv sa vypíše informácia o útočiacej jednotke, ktorá sa skladá z nasledovných častí, ktoré sú oddelené čiarkou:
    • Číslo armády, do ktorej jednotka patrí
    • Meno jednotky
    • Predmet, ktorý jednotka použije na útok (najprv sa útočí predmetom č. 1 a potom predmetom č. 2)
  • Následne sa vypíše zoznam všetkých zasiahnutých jednotiek nepriateľskej armády, kde o každej zasiahnutej jednotke sa vypíšu tieto informácie, ktoré sú oddelené čiarkou:
    • Meno zasiahnutej jednotky
    • Zranenie, ktoré bolo spôsobené zasiahnutej jednotke.
Celkový výpis v rámci kola

V tejto časti uvádzame príklad celkového výpisu, ktorý sa uskutoční v rámci jedného kola. Má 4 časti. Najprv sa vypíše poradové číslo kola. Následne, na začiatku kola sa zavolá funkcia print_units(). Po vykonaní útoku oboch armád sa zavolá funkcia print_damage(). Na konci kola sa ešte raz zavolá funkcia print_units().

Round 1
1: Niko,100 Odin,100
2: Oldo,100 Ondro,100 Oskar,100 Oto,100
1,Niko,gloves:       [Oldo,1]
1,Niko,wand:         [Oldo,12] [Ondro,10] [Oskar,7]
1,Odin,fireball:     [Oldo,11] [Ondro,9] [Oskar,6] [Oto,9]
2,Oldo,dagger:       [Niko,1]
2,Ondro,axe:         [Niko,2] [Odin,10]
2,Oskar,rock:        [Niko,1] [Odin,3]
1: Niko,96 Odin,87
2: Oldo,76 Ondro,81 Oskar,87 Oto,91

Útok armády

V každom kole vykonajú útok obidve armády súčasne. Útoku sa zúčastní každá živá jednotka v armáde. Každá jednotka zaútočí 0, 1 alebo 2 predmetmi v inventári podľa toho, či má predmet dostatočný útočný dosah (range).

Každá jednotka sa pokúsi zaútočiť všetkými predmetmi v inventári (pokus zlyhá ak predmet nemá dostatočný dosah). Najprv sa snaží vykonať útok predmetom č. 1 (item1) a potom predmetom č. 2 (item2). Ak dôjde k útoku, jednotka spôsobí zranenie 1 alebo viacerým nepriateľským jednotkám podľa toho, či sa nachádzajú v akčnom rádiuse predmetu).

Vzorec na výpočet zranenia

Každý predmet v inventári spôsobí brániacej sa jednotke nejaké zranenie. Celkové zranenie $DMG_{Attacker}$, ktoré spôsobí útočiaca jednotka sa vypočíta ako súčet zranení, ktoré spôsobia všetky predmety útočníka, ktoré boli použité pri útoku.

$DMG_{Attacker} = DMG_{Item 1} + DMG_{Item 2}$, kde:

  • $DMG_{Item 1}$ - zranenie spôsobené 1. predmetom
  • $DMG_{Item 2}$ - zranenie spôsobené 2. predmetom

Zranenie, ktoré spôsobí útočník zvoleným predmetom sa vypočíta podľa nasledovného vzťahu.

$DMG_{item} = max(1,ATT_{item} - DEF_{total})$, kde:

  • $ATT_{item}$ - atribút att zvoleného predmetu útočníka
  • $DEF_{total}$ - celková obrana vypočítaná ako súčet atribútov def všetkých predmetov obrancu.

Na obrázku 6 vidíme ilustráciu výpočtu zranenia, ktoré spôsobí jednotka s menom Fero (vľavo) jednotke s menom Milan (vpravo). Predpokladáme, že pozície týchto jednotiek na bojovom poli spĺňajú podmienku na vykonanie útoku, t.j. $pos <= range$.



Obrázok 6: Výpočet zranenia (veľký obrázok).


Aktualizácia armády

Aktualizácia armád nastane až po tom, keď obidve armády dokončia svoj útok v rámci kola. Skladá sa z 2 fáz.

  1. Aktualizácia zdravia všetkých jednotiek (t.j. zníženie HP o hodnotu príslušného zranenia).
  2. Odstránenie zničených jednotiek z armády. Miesto po zničených jednotkách bude vyplnené zostávajúcimi živými jednotkami (posunú sa smerom do stredu bojového poľa). Odstránenie zničených jednotiek ilustruje obrázok 7.


Obrázok 7: Odstránenie zničených jednotiek z armády (veľký obrázok).


Vyhodnotenie súboja

Po každom kole sa vyhodnotí, či súboj neskončil. Ak súboj skončil, môže mať 3 výsledky:

  • Víťazom je armáda 1 (armáda 2 je zničená). Vypíše sa WINNER: 1.
  • Víťazom je armáda 2 (armáda 1 je zničená). Vypíše sa WINNER: 2.
  • Remíza (po útoku v rámci kola zostali obidve armády zničené). Vypíše sa NO WINNER.

Command-line argumenty

Používateľ môže pri spustení programu v termináli zadať nepovinný argument $N$. Tento argument stanovuje maximálny počet kôl súboja. Ak $N = 0$, vykoná sa len úvodný výpis armád. Ak používateľ nezadá $N$, súboj prebehne až do konca. Na spracovanie CMD argumentov použite parametre argc a argv, ktoré vstupujú do main funkcie. Viac o spracovaní CMD argumentov.



Obrázok 8: Zadanie CMD argumentu $N$ do programu. (veľký obrázok).


Štruktúra celkového výstupu

Celkový výpis programu má nasledujúcu štruktúru.

  1. Najprv sa vypíše úvodný výpis armád na začiatku súboja.
  2. V rámci každého kola sa vypíšu informácie o armádach a spôsobenom zranení.
    1. Poradové číslo kola
    2. Výpis armád na začiatku kola
    3. Výpis spôsobeného zranenia.
    4. Výpis armád na konci kola
  3. Na konci sa vypíše výsledok súboja.

Príklad celkového výstupu programu (bez zadania argumentu $N$):

Vstup

4
Wren rock
Elio sword dagger
Juno sword helmet
Hippo sword wand
3
Smag spear rock
Rio helmet rock
Tipo wand wand

Výstup

Army 1
    Unit: 0
    Name: Wren
    HP: 100
    Item 1: rock,3,0,1,2,1

    Unit: 1
    Name: Elio
    HP: 100
    Item 1: sword,9,2,1,0,0
    Item 2: dagger,4,0,1,0,0

    Unit: 2
    Name: Juno
    HP: 100
    Item 1: sword,9,2,1,0,0
    Item 2: helmet,1,5,1,0,0

    Unit: 3
    Name: Hippo
    HP: 100
    Item 1: sword,9,2,1,0,0
    Item 2: wand,12,4,1,4,2

Army 2
    Unit: 0
    Name: Smag
    HP: 100
    Item 1: spear,6,1,1,1,1
    Item 2: rock,3,0,1,2,1

    Unit: 1
    Name: Rio
    HP: 100
    Item 1: helmet,1,5,1,0,0
    Item 2: rock,3,0,1,2,1

    Unit: 2
    Name: Tipo
    HP: 100
    Item 1: wand,12,4,1,4,2
    Item 2: wand,12,4,1,4,2

Round 1
1: Wren,100 Elio,100 Juno,100 Hippo,100
2: Smag,100 Rio,100 Tipo,100
1,Wren,rock:         [Smag,2] [Rio,1]
1,Hippo,wand:        [Smag,11] [Rio,7] [Tipo,4]
2,Smag,spear:        [Wren,6] [Elio,4]
2,Smag,rock:         [Wren,3] [Elio,1]
2,Rio,rock:          [Wren,3] [Elio,1]
2,Tipo,wand:         [Wren,12] [Elio,10] [Juno,5]
2,Tipo,wand:         [Wren,12] [Elio,10] [Juno,5]
1: Wren,64 Elio,74 Juno,90 Hippo,100
2: Smag,87 Rio,92 Tipo,96

Round 2
1: Wren,64 Elio,74 Juno,90 Hippo,100
2: Smag,87 Rio,92 Tipo,96
1,Wren,rock:         [Smag,2] [Rio,1]
1,Hippo,wand:        [Smag,11] [Rio,7] [Tipo,4]
2,Smag,spear:        [Wren,6] [Elio,4]
2,Smag,rock:         [Wren,3] [Elio,1]
2,Rio,rock:          [Wren,3] [Elio,1]
2,Tipo,wand:         [Wren,12] [Elio,10] [Juno,5]
2,Tipo,wand:         [Wren,12] [Elio,10] [Juno,5]
1: Wren,28 Elio,48 Juno,80 Hippo,100
2: Smag,74 Rio,84 Tipo,92

Round 3
1: Wren,28 Elio,48 Juno,80 Hippo,100
2: Smag,74 Rio,84 Tipo,92
1,Wren,rock:         [Smag,2] [Rio,1]
1,Hippo,wand:        [Smag,11] [Rio,7] [Tipo,4]
2,Smag,spear:        [Wren,6] [Elio,4]
2,Smag,rock:         [Wren,3] [Elio,1]
2,Rio,rock:          [Wren,3] [Elio,1]
2,Tipo,wand:         [Wren,12] [Elio,10] [Juno,5]
2,Tipo,wand:         [Wren,12] [Elio,10] [Juno,5]
1: Elio,22 Juno,70 Hippo,100
2: Smag,61 Rio,76 Tipo,88

Round 4
1: Elio,22 Juno,70 Hippo,100
2: Smag,61 Rio,76 Tipo,88
1,Elio,sword:        [Smag,8]
1,Elio,dagger:       [Smag,3]
1,Hippo,wand:        [Smag,11] [Rio,7] [Tipo,4]
2,Smag,spear:        [Elio,4] [Juno,1]
2,Smag,rock:         [Elio,1] [Juno,1]
2,Rio,rock:          [Elio,1] [Juno,1]
2,Tipo,wand:         [Elio,10] [Juno,5] [Hippo,6]
2,Tipo,wand:         [Elio,10] [Juno,5] [Hippo,6]
1: Juno,57 Hippo,88
2: Smag,39 Rio,69 Tipo,84

Round 5
1: Juno,57 Hippo,88
2: Smag,39 Rio,69 Tipo,84
1,Juno,sword:        [Smag,8]
1,Juno,helmet:       [Smag,1]
1,Hippo,wand:        [Smag,11] [Rio,7] [Tipo,4]
2,Smag,spear:        [Juno,1] [Hippo,1]
2,Smag,rock:         [Juno,1] [Hippo,1]
2,Rio,rock:          [Juno,1] [Hippo,1]
2,Tipo,wand:         [Juno,5] [Hippo,6]
2,Tipo,wand:         [Juno,5] [Hippo,6]
1: Juno,44 Hippo,73
2: Smag,19 Rio,62 Tipo,80

Round 6
1: Juno,44 Hippo,73
2: Smag,19 Rio,62 Tipo,80
1,Juno,sword:        [Smag,8]
1,Juno,helmet:       [Smag,1]
1,Hippo,wand:        [Smag,11] [Rio,7] [Tipo,4]
2,Smag,spear:        [Juno,1] [Hippo,1]
2,Smag,rock:         [Juno,1] [Hippo,1]
2,Rio,rock:          [Juno,1] [Hippo,1]
2,Tipo,wand:         [Juno,5] [Hippo,6]
2,Tipo,wand:         [Juno,5] [Hippo,6]
1: Juno,31 Hippo,58
2: Rio,55 Tipo,76

Round 7
1: Juno,31 Hippo,58
2: Rio,55 Tipo,76
1,Juno,sword:        [Rio,4]
1,Juno,helmet:       [Rio,1]
1,Hippo,wand:        [Rio,7] [Tipo,4]
2,Rio,helmet:        [Juno,1]
2,Rio,rock:          [Juno,1] [Hippo,1]
2,Tipo,wand:         [Juno,5] [Hippo,6]
2,Tipo,wand:         [Juno,5] [Hippo,6]
1: Juno,19 Hippo,45
2: Rio,43 Tipo,72

Round 8
1: Juno,19 Hippo,45
2: Rio,43 Tipo,72
1,Juno,sword:        [Rio,4]
1,Juno,helmet:       [Rio,1]
1,Hippo,wand:        [Rio,7] [Tipo,4]
2,Rio,helmet:        [Juno,1]
2,Rio,rock:          [Juno,1] [Hippo,1]
2,Tipo,wand:         [Juno,5] [Hippo,6]
2,Tipo,wand:         [Juno,5] [Hippo,6]
1: Juno,7 Hippo,32
2: Rio,31 Tipo,68

Round 9
1: Juno,7 Hippo,32
2: Rio,31 Tipo,68
1,Juno,sword:        [Rio,4]
1,Juno,helmet:       [Rio,1]
1,Hippo,wand:        [Rio,7] [Tipo,4]
2,Rio,helmet:        [Juno,1]
2,Rio,rock:          [Juno,1] [Hippo,1]
2,Tipo,wand:         [Juno,5] [Hippo,6]
2,Tipo,wand:         [Juno,5] [Hippo,6]
1: Hippo,19
2: Rio,19 Tipo,64

Round 10
1: Hippo,19
2: Rio,19 Tipo,64
1,Hippo,sword:       [Rio,4]
1,Hippo,wand:        [Rio,7] [Tipo,4]
2,Rio,helmet:        [Hippo,1]
2,Rio,rock:          [Hippo,1]
2,Tipo,wand:         [Hippo,6]
2,Tipo,wand:         [Hippo,6]
1: Hippo,5
2: Rio,8 Tipo,60

Round 11
1: Hippo,5
2: Rio,8 Tipo,60
1,Hippo,sword:       [Rio,4]
1,Hippo,wand:        [Rio,7] [Tipo,4]
2,Rio,helmet:        [Hippo,1]
2,Rio,rock:          [Hippo,1]
2,Tipo,wand:         [Hippo,6]
2,Tipo,wand:         [Hippo,6]
1:
2: Tipo,56

WINNER: 2


Obrázok 9: Štruktúra celkového výstupu. (veľký obrázok).


Projekt

Dodaný CLion projekt sa skladá z 3 častí:

  • Adresár include, ktorý obsahuje hlavičkový súbor data.h
    /*
     *  Makra, struktury, deklaracie
     */
    
    #ifndef DATA_H
    #define DATA_H
    
    #define NUMBER_OF_ITEMS 16  // velkost databazy predmetov
    #define MAX_NAME 100        // maximalna dlzka nazvu/mena
    #define MIN_ARMY 1          // minimalny pocet bojovych jednotiek v armade
    #define MAX_ARMY 5          // maximalny pocet bojovych jednotiek v armade
    
    // Struktura pre predmet v inventari
    typedef struct item {
        char name[MAX_NAME + 1];        // nazov predmetu
        int att;                        // utok
        int def;                        // obrana
        int slots;                      // pocet slotov, ktore zabera predmet v inventari
        int range;                      // utocny dosah/dostrel predmetu
        int radius;                     // sirka zasahu (alebo damage radius)
    } ITEM;
    
    // Struktura pre bojovu jednotku
    typedef struct unit {
        char name[MAX_NAME + 1];    // meno jednotky
        const ITEM *item1;          // prvy predmet v inventari (ukazuje do pola 'items')
        const ITEM *item2;          // druhy predmet v inventari (ukazuje do pola 'items')
        int hp;                     // zdravie (z angl. hit points)
    } UNIT;
    
    // databaza predmetov, ktore moze mat bojova jednotka v inventari
    extern const ITEM items[NUMBER_OF_ITEMS];
    
    #endif //DATA_H
  • Adresár src, ktorý obsahuje zdrojový súbor data.c a z4.c
    /*
     *  Definicie
     */
    
    #include "data.h"
    
    // databaza predmetov, ktore moze mat bojova jednotka v inventari
    const ITEM items[NUMBER_OF_ITEMS] = {
            // predmety, ktore zaberaju 1 slot, zoradene podla atributu 'att'
            {.name = "wand", .att = 12, .def = 4, .slots = 1, .range = 4, .radius = 2}, // kuzelna palica
            {.name = "fireball", .att = 11, .def = 0, .slots = 1, .range = 3, .radius = 3}, // ohniva gula
            {.name = "sword", .att = 9, .def = 2, .slots = 1, .range = 0, .radius = 0}, // mec
            {.name = "spear", .att = 6, .def = 1, .slots = 1, .range = 1, .radius = 1}, // ostep
            {.name = "dagger", .att = 4, .def = 0, .slots = 1, .range = 0, .radius = 0}, // dyka
            {.name = "rock", .att = 3, .def = 0, .slots = 1, .range = 2, .radius = 1}, // skala
            {.name = "armor", .att = 2, .def = 7, .slots = 1, .range = 0, .radius = 0}, // brnenie
            {.name = "shield", .att = 2, .def = 6, .slots = 1, .range = 0, .radius = 0}, // stit
            {.name = "gloves", .att = 1, .def = 4, .slots = 1, .range = 0, .radius = 0}, // rukavice
            {.name = "helmet", .att = 1, .def = 5, .slots = 1, .range = 0, .radius = 0}, // helma
            {.name = "aura", .att = 0, .def = 8, .slots = 1, .range = 0, .radius = 0}, // ochranna aura
    
            // predmety, ktore zaberaju 2 sloty, zoradene podla atributu 'att'
            {.name = "cannon", .att = 12, .def = 0, .slots = 2, .range = 4, .radius = 4}, // kanon
            {.name = "axe", .att = 10, .def = 2, .slots = 2, .range = 1, .radius = 1}, // sekera
            {.name = "hammer", .att = 8, .def = 2, .slots = 2, .range = 1, .radius = 2}, // kladivo
            {.name = "crossbow", .att = 5, .def = 1, .slots = 2, .range = 3, .radius = 0}, // kusa
            {.name = "slingshot", .att = 2, .def = 0, .slots = 2, .range = 2, .radius = 1} // prak
    };
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include "data.h" // NEMENIT, inak vas kod neprejde testom !!!
    
    // chybove hlasenia
    #define ERR_UNIT_COUNT "ERR_UNIT_COUNT"
    #define ERR_ITEM_COUNT "ERR_ITEM_COUNT"
    #define ERR_WRONG_ITEM "ERR_WRONG_ITEM"
    #define ERR_SLOTS "ERR_SLOTS"
    
    int main(const int argc, char *argv[]) {
        return 0;
    }
  • Súbor CMakeLists.txt, ktorý obsahuje nastavenie projektu.


Obrázok 10: Štruktúra CLion projektu. (veľký obrázok).


Ako testovať váš program?

K dispozícii máte balík s testovacími súbormi pomocou, ktorých si viete otestovať funkcionalitu vášho programu v jednotlivých scenároch.

Balík je distribuovaný ako ZIP súbor s názvom z4_testing.zip, ktorý obsahuje tieto adresáre:

  • stdin - adresár, ktorý obsahuje TXT súbory, v ktorých sú uložené vstupy do programu pre všetky testovacie prípady a scenáre (odporúčame skompilovaný program spúšťať pomocou presmerovania štandardného vstupu z týchto súborov)
  • stdout - adresár, ktorý obsahuje správne výstupy pre všetky testovacie prípady vo všetkých scenároch


Obrázok 11: Štruktúra balíka s testovacími súbormi (veľký obrázok).


Ako spustiť program s presmerovaním štandardného vstupu z TXT súboru

Štandardný vstup programu vieme v termináli jednoducho presmerovať pomocou operátora <. Použitím tohto operátora sa vstup bude čerpať z dodaného textového súboru a nie z klávesnice. Príklad spustenia programu s presmerovaním štandardného vstupu v Linux termináli je uvedený pod textom.

./my_program < input.txt

Uvádzame príklad ako testovať váš program pomocou dodaných TXT súborov pre jednotlivé scenáre. V príklade je program testovaný pre náhodne vybrané testovacie prípady v scenári 1. Program z4 predstavuje skompilované zadanie č. 4 a predpokladáme, že sa nachádza v adresári stdin.

./z4 < scenar_1/example_1.txt
./z4 < scenar_1/example_2.txt
./z4 < scenar_1/example_3.txt

Uvádzame aj príklad ako si rýchlo v Linux termináli otestovať, či váš program vypísal očakávaný výstup pre zvolený testovací scenár a prípad. Využijeme na to presmerovanie štandardného vstupu a príkaz diff na porovnanie obsahu dvoch textov. Budeme predpokladať, že skompilovaný program sa nachádza na rovnakej úrovni ako adresáre stdin a stdout. V tomto príklade testujeme, či náš program správne funguje v testovacom prípade 2 v rámci scenára 4. Ak sa po spustení tohto príkazu nič nevypíše, signalizuje to, že výstup nášho programu sa nelíši od správneho výstupu.

./z4 < stdin/scenar_4/example_2.txt  | diff - stdout/scenar_4/example_2.txt --ignore-trailing-space

Poznámka: pokročilí programátori a nadšenci si môžu proces testovania a porovnávania výstupov programu so správnym výstupom automatizovať pomocou Python, Linux shell skriptov resp. Powershell skriptov a pod. Ideálny nástrojom na porovnávanie obsahu dvoch TXT súborov je príkaz diff. Viac informácií nájdete na tomto odkaze.

Hodnotenie zadania

Odovzdávací systém otestuje a vyhodnotí nasledovné oblasti funkcionality vášho programu. Na získanie bodov za konkrétny testovací scenár je nutné, aby testom prešli všetky testovacie prípady v danom scenári.

Testovacie scenáre
Scenár 1
Detekcia chýb pri načítavaní jednotiek
1,0 b
Scenár 2
Načítanie armád + úvodný výpis (používateľ zadá N=0)
1,0 b
Scenár 3
Výpis priebehu súboja, 1 vs. 1 (používateľ nezadá N)
1,0 b
Scenár 4
Výpis priebehu súboja, veľa jednotiek (používateľ zadá N > 0)
3,0 b
Scenár 5
Výpis priebehu súboja, veľa jednotiek (používateľ nezadá N)
3,0 b
Scenár 6
Kontrola výpisu víťaza (používateľ nezadá N)
1,0 b
Súčet 10,0 b

Testovacie príklady

Všetky testovacie príklady na stiahnutie

V testovacom scenári 4 boli pre jednotlivé príklady použité tieto hodnoty CMD parametra $N$.

  • example_1.txt ... $N = 10$
  • example_2.txt ... $N = 5$
  • example_3.txt ... $N = 14$
  • example_4.txt ... $N = 12$
  • example_5.txt ... $N = 11$
  • example_6.txt ... $N = 16$
  • example_7.txt ... $N = 10$
  • example_8.txt ... $N = 12$
  • example_9.txt ... $N = 190$

Zdroje

Nasledujúce zdroje vám môžu pomôcť pri programovaní zadania. Odporúčame si tieto zdroje preštudovať. Na prístup k niektorým zdrojom potrebujete byť prihlásení vo vašom univerzitnom Google STU konte.

Prednášky
Jazyk C
Zdroje použité pri tvorbe zadania a prezentácie
Copyright © 2025, Pavol Marák, ÚIM FEI STU.
Vyrobené pomocou Django a Spectre.css.
Regulárne výrazy testujeme pomocou Regular Expression 101.
Videá prehrávame pomocou Plyr prehrávača.
Animácie vytvárame pomocou Animate.css.