Problem Set #5: Adventure

Ciele

  • Osvojiť si prácu s rozsiahlejším projektom.
  • Práca s typom void*.
  • Osvojiť si prácu s dynamickými zoznamami.
  • Osvojiť si prácu s union-mi.
  • Naučiť sa pracovať s bitovými maskami.
  • Naučiť sa písať regulárne výrazy.

Textové adventúry

Adventure Time 1 by Kika

História vývoja tohto žánru siaha do roku 1976-1977, kedy vznikla prvá textová adventúra (skrátene len textovka alebo tiež v angličtine interactive fiction) s názvom Colosal Cave Adventure (známa tiež pod názvami ADVENT, Colossal Cave alebo len Adventure). Odvtedy však vzniklo obrovské množstvo hier tohto typu a v histórii počítačových hier sa jedná o žáner, ktorý si zaslúži pozornosť, pretože textovky ponúkali možnosť vytvoriť v dobách, keď neexistovali žiadne akcelerované grafické karty a video pamäť počítačov sa rátala v jednotkách kB, akékoľvek dobrodružstvo, ktoré si hráč “vyrenderoval” vo svojej hlave. Čo teda textovky vlastne sú?

Pre textovky je charakteristický textový režim, pomocou ktorého hra komunikuje s hráčom, a pomocou ktorého rovnako hráč komunikuje s hrou. Príkladom môže byť nasledujúci fragment jednej z nich:

> kill dragon
WITH WHAT? YOUR BARE HANDS?
> yes
CONGRATULATIONS! YOU HAVE JUST VANQUISHED A DRAGON WITH YOUR BARE HANDS! (UNBELIEVABLE, ISN'T IT?)

Aby ste však mali lepšiu predstavu o tom, ako takýto typ hry vlastne vyzerá v praxi, môžete si niektorú textovku priamo vyskúšať:

  • Indiana Jones 2 (ZX Spectrum, 1987, CZ) - Hra je vhodná pre začiatočníkov, pretože nevyžaduje písanie príkazov, ale pracuje s roletovým menu. Tým sa viete jednoduchšie oboznámiť s typom hry ako takým.
  • Indiana Jones 3 (ZX Spectrum, 1990, CZ)
  • Adventure (on-line, 1976, EN)
  • Zork I (on-line, 1980, EN)

Hlavné stavebné prvky hry

V každej textovke je možné rozpoznať tieto prvky:

  • Príkazy - Hráč komunikuje s hrou prostredníctvom príkazov, ktoré zadáva z klávesnice. Každý príkaz má okrem svojho kľúčového slova aj opis, ktorý slúži ako dokumentácia pre použitie príkazu.
  • Predmety - Hráč počas hry bude vo svete nachádzať rozličné predmety. Tieto si bude môcť vziať so sebou do batohu a neskôr napr. vyložiť v niektorej z miestností alebo použiť v kombinácii s iným predmetom (napr. kľúč použije s dverami, čím sa bude môcť dostať do ďalšej miestnosti).
  • Miestnosti - Svet textovej adventúry je zložený z miestností, medzi ktorými sa je možné pohybovať. Každá miestnosť je definovaná opisom a má svoje meno. Okrem týchto dvoch základných atribútov bude každá miestnosť obsahovať aj zoznam možných východov z miestnosti a zoznam predmetov, ktoré sa v miestnosti nachádzajú. Miestnosti budú spolu vytvárať svet hry.

Základná funkcionalita hry

Adventure Time 2 by Kika

Vo všeobecnosti je možné správanie a vlastnosti textovky opísať v nasledujúcich bodoch:

  • hra musí byť dohrateľná za konečný počet krokov (príkazov)
  • hráč má k dispozícii sadu príkazov, pomocou ktorej komunikuje s hrou
  • hráč môže prechádzať medzi miestnosťami herného sveta v smeroch sever, juh, východ a západ
  • v každej miestnosti sa môže nachádzať 0 a viac predmetov
  • predmety je možné medzi miestnosťami prenášať
  • hráč má batoh, v ktorom môže prenášať predmety medzi miestnosťami
  • množstvo predmetov, ktoré môže hráč do batohu vložiť, je obmedzené
  • v hre existujú predmety, ktoré do batohu nie je možné vložiť

Moduly projektu

Váš projekt bude rozdelený do niekoľkých modulov:

  • Item - modul pre prácu s predmetmi v hre
  • Room - modul pre prácu s miestnosťami, resp. lokáciami v hre
  • World - modul pre reprezentáciu herného sveta
  • Command - modul pre prácu s príkazmi
  • Game - hlavný modul, v ktorom sa nachádza celá herná logika
  • Parser - modul pre rozpoznávanie vstupu od hráča
  • Container - modul na prácu so zoznamami (miestnostní, príkazov, predmetov a textov)
  • Backpack - modul na prácu s batohom

Modul Container

Tento modul obsahuje funkcie na prácu s dynamickými zoznamami, ktoré budete v hre používať. Či už na vytvorenie zoznamu všetkých príkazov v hre alebo na vytvorenie zoznamu predmetov nachádzajúcich sa v miestnosti alebo na zoznam príkazov tvoriacich históriu, ktoré hráč zadal.

Každý prvok, ktorý do zoznamu vložíte, bude štruktúrovaného typu struct container.

Enumeračný typ container_type

Tento typ definuje štyry typy údajov, ktoré je možné do kontajnera ukladať a to:

  • ROOM na vytvorenie zoznamu miestností
  • COMMAND na vytvorenie zoznamu príkazov
  • ITEM na vytvorenie zoznamu predmetov
  • TEXT na vytvorenie zoznamu reťazcov

Štruktúra container

Táto štruktúra reprezentuje kontajner, ktorý uchováva jeden zo štyroch rozličných typov údajov - miestnosť, predmet, príkaz alebo reťazec. Okrem samotných údajov obsahuje referenciu na ďalší kontajner, čím je možné vytvoriť jednosmerný spájaný zoznam.

Všetky zoznamy, ktoré v hre vytvoríte, budú vlastne zoznamy typu štruktúry container. Preto je tento modul jeden z najkľúčovejších.

Úloha #1: Funkcia create_container()

Pomocou tejto funkcie dôjde k vytvoreniu nového kontajneru, ktorý sa pripojí na koniec zoznamu existujúcich kontajnerov. Zoznam existujúcich kontajnerov je špecifikovaný parametrom first, ktorý ukazuje na prvý kontajner v zozname.

Funkcia vracia referenciu na nový vytvorený kontajner. V prípade, že údaje kontajnera nie sú poskytnuté (entry je NULL), funkcia vráti NULL. Funkcia vráti NULL aj v prípade, že typ nového kontajnera by mal byť iný ako typ prvého kontajnera v existujúcom zozname.

Úloha #2: Funkcia destroy_containers()

Táto funkcia odstráni z pamäte celý zoznam vytvorený z kontajnerov spolu s jeho obsahom. To znamená, že ak v zozname kontajnerov máte uložený zoznam príkazov, ktoré hra pozná, zavolaním tejto funkcie odstránite samotný zoznam, ale rovnako aj všetky príkazy, ktoré v ňom máte uložené.

Funkcia vracia hodnotu NULL ako referenciu na nový začiatok tohto zoznamu.

Úloha #3: Funkcia get_from_container_by_name()

Pomocou funkcie get_from_container_by_name() je možné získať prvok zo zoznamu kontajnerov na základe jeho mena, bez ohľadu na jeho typ.

Funkcia vráti prvý prvok zo zoznamu kontajnerov, ktorého meno sa zhoduje s daným menom, alebo NULL ak sa prvok s takým menom v zozname kontajnerov nevyskytuje.

Úloha #4: Funkcia remove_container()

Funkcia remove_container() odstráni zo zoznamu kontajnerov jeden kontajner, ktorý obsahuje daný záznam (entry), pričom samotný záznam ostane bez zmeny (nebude dealokovaný).

Funkcia vráti referenciu na prvý kontajner v upravenom zozname kontajnerov, alebo NULL ak po úprave ostane zoznam prázdny. Funkcia vráti NULL aj v prípade, že zoznam kontajnerov nie je daný (parameter first je NULL).

Modul Room

Tento modul poskytuje štruktúru room, pomocou ktorej je možné reprezentovať akúkoľvek miestnosť (lokáciu) hry. Rovnako obsahuje funkcie na prácu s miestnosťami.

Úloha #1: Funkcia create_room()

Každá miestnosť je definovaná názvom a opisom. Po zavolaní tejto funkcie dôjde k vytvoreniu novej miestnosti. Funkcia teda vráti referenciu na štruktúru room, ktorá bude mať nastavený názov a opis. V prípade, že sa miestnosť nepodarí vytvoriť, funkcia vráti hodnotu NULL.

Úloha #2: Funkcia set_exits_from_room()

Virtuálny svet sa skladá z miestností (lokácií), ktoré sú navzájom prepojené. Prepojenie bude reprezentované existenciou referencie z jednej miestnosti do druhej. Z každej miestnosti je možné vytvoriť prepojenie na miestnosť v štyroch smeroch - sever, juh, východ alebo západ.

Úloha #3: Funkcia show_room()

Potom, ako hráč vojde do niektorej miestnosti, sa na obrazovke zobrazí opis danej miestnosti, výpis východov z nej a zoznam predmetov, ktoré sa v miestnosti nachádzajú. Spôsob, akým tento výpis realizujete, je na vás a jeho výstup sa testovať nebude. Môže vyzerať nasledovne:

Nachadzas sa v chyzi svarneho suhaja. Na vychode sa nachadzaju dvere veduce z chyze von.
Mozne vychody z miesnosti:
    vychod
Vidíš:
    kluc
    popisany papier

Úloha #4: Funkcia destroy_room()

Pomocou tejto funkcie bude možné zrušiť zadanú miestnosť. Zrušenie miestnosti znamená, že ak pre jej vytvorenie potrebujete vyhradiť pamäť, tak pri jej rušení všetku vyhradenú pamäť uvoľníte.

Funkcia vracia hodnotu NULL ako novú referenciu na práve zrušenú miestnosť.

Príklad použitia

// create rooms first
struct room* home = create_room("start", "Nachadzas sa v chyzi svarneho suhaja. Na vychode sa nachadzaju dvere veduce z chyze von.");
struct room* garden = create_room("garden", "Stojis pred chyzou a rozoznavas zahradku, ktora je znacne neudrziavana. este ze husty lesik na severe v porovnani so zahradkou nicim nevynika.");

// set exits
set_exits_from_room(home, NULL, NULL, garden, NULL);
set_exits_from_room(garden, NULL, NULL, NULL, home);

// show room
show_room(home);
/*
Nachadzas sa v chyzi svarneho suhaja. Na vychode sa nachadzaju dvere veduce z chyze von.
Mozne vychody z miesnosti:
    vychod
*/

// destroy rooms
home = destroy_room(home);
garden = destroy_room(garden);

Úloha #5: Funkcia add_item_to_room()

Funkcia pridá do miestnosti predmet, ktorý je zadaný parametrom funkcie. Počet predmetov, ktoré sa môžu v miestnosti nachádzať, nie je obmedzený.

Príklad použitia

add_item_to_room(home, key);

Úloha #6: Funkcia get_item_from_room()

Funkcia vyhľadá v miestnosti predmet. Ak sa predmet v miestnosti nachádza, funkcia vráti jeho referenciu. Ak sa predmet v miestnosti nenachádza, vráti NULL. Predmet je daný svojím menom, pričom pri vyhľadávaní nezáleží na veľkosti písmen.

Príklad použitia:

struct item* item = get_item_from_room(home, "zlaty KLUC");

Úloha #7: Funkcia delete_item_from_room()

Funkcia odstráni predmet z miestnosti. Referencia na predmet sa nachádza v parametri funkcie. Ak sa predmet v miestnosti nachádza, bude odstránený zo zoznamu predmetov v miestnosti. Ak sa v miestnosti nenachádza, nič sa nestane.

Príklad použitia

delete_item_from_room(home, key);

Modul Command

Tento modul slúži na vytváranie a rušenie príkazov, ktoré je možné v hre používať. Každý príkaz sa pritom nutne skladá z názvu (kľúčového slova), jeho opisu a regulárneho výrazu na rozpoznanie príkazu (spolu s počtom rozpoznaných skupín).

Úloha #1: Funkcia create_command()

Táto funkcia vytvorí nový príkaz, pričom každý príkaz je identifikovaný pomocou svojho názvu (kľúčového slova) a opisu, ktorý sa použíje ako pomôcka. Príkaz môže byť vytvorený len vtedy, ak je zadaný jeho názov a opis.

Príklad použitia:

struct command* help = create_command("POMOC", "Zobrazi zoznam vsetkych prikazov", "(POMOC)", 1);

Úloha #2: Funkcia destroy_command()

Funkcia odstráni vytvorený príkaz z pamäte.

Príklad použitia:

help = destroy_command(help);

Modul Game

Tento modul obsahuje štruktúru game, ktorá predstavuje kontext hry. Dá sa teda skrze ňu dostať k svetu, zoznamu všetkých miestností, hráčovmu batohu a pod. Rovnako sa v module nachádza funkcia execute_command(), ktorá funguje ako hlavná funkcia hry, kde sa rozhodne, čo má ktorý príkaz vykonať.

Modul má zadefinovaný aj enumeračný typ gamestate, ktorý obsahuje 4 stavy, v ktorých sa môže hra nachádzať:

  1. PLAYING - základný stav, ktorý reprezentuje hru v stave hrania
  2. GAMEOVER - do tohto stavu sa hra dostane vtedy, ak hráč zadal príkaz na ukončenie hry alebo v hre zomrel
  3. SOLVED - stav, do ktorého sa hra dostane po úspešnom dohratí hry
  4. RESTART - stav hry, kedy došlo k zadaniu požiadavky na reštartovanie hry od začiatku pomocou príkazu RESTART

Úloha #1: Funkcia create_game()

Úlohou tejto funkcie je vytvoriť štruktúru game, ktorá obsahuje referencie na všetky potrebné komponenty hry. Takže súčasťou vytvorenia štruktúry game je aj vytvorenie štruktúr, ktoré štruktúra game obsahuje. Táto funkcia by sa teda mala spúšťať ako prvá pri vytváraní celej hry.

Funkcia vracia referenciu na vytvorenú štruktúru game.

Úloha #2: Funkcia destroy_game()

Táto funkcia uvoľní všetky vyhradené pamäťové prostriedky hry. Nie len tie, ktoré boli vyhradené pre štruktúru game, ale aj všetky tie, ktoré boli vyhradené pre vytvorenie členov štruktúry. Jedná sa teda o funkciu, ktorá by sa mala volať ako posledná pri ukončovaní hry.

Úloha #3: Funkcia play_game()

Táto funkcia predstavuje hernú (hlavnú) slučku hry a teda kombináciu operácií: input - update - render (načítanie vstupu od používateľa, aktualizáciu stavu sveta vykonaním zadaného príkazu a zobrazenie novej situácie).

Hra sa hrá dovtedy (herná slučka sa vykonáva dovtedy), pokiaľ nedôjde k zmene stavu z PLAYING na niektorý iný.

Pri spracúvaní vstupu sa môžete spoľahnúť na to, že jeho veľkosť nepresiahne 100 znakov. Veľkosť vstupného bufferu je daná makrom INPUT_BUFFER_SIZE.

Úloha #4: Funkcia execute_command()

Vykoná príkaz hry, ktorý funkcia dostala ako parameter. Funkcia síce nevracia žiadnu hodnotu, ale v závislosti od vykonaného príkazu môže byť jej výstupom zmena aktuálnej miestnosti alebo zmena stavu celej hry, ak hráč zadaným príkazom vykonal krok, vďaka ktorému zomrel (stratil sa v púšti, spadol z veľkej výšky, vystúpil do vesmíru bez skafandru a pod.).

Príklad použitia modulu

Použitie modulu ilustruje nasledujúci fragment kódu:

// create game first with all the members of the game structure
struct game* game = create_game();

// after successful creation, play the game
// game loop is located in the function play_game()
play_game(game);

// destroy game at the end
game = destroy_game(game);

Modul Parser

Tento modul bude zodpovedný za rozpoznanie a spracovanie vstupu zadaného používateľom. Pri spracovávaní vstupu vo všeobecnosti platí, že:

  • nEzálEží NA veľkostI vstUPNýCh znakOV, napr. vstup:

    POUZI KLUC

    a

    pouzi kluc

    reprezentuje rovnaký názov príkazu ako aj predmetu, ktorý má byť použitý

  • vstup môže byť tvorený aj množstvom bielych znakov pred a za samotným príkazom, ktoré parser ignoruje, napr. vstup definovaný reťazcom " pouzi kluc " parser dokáže rozpoznať ako príkaz POUZI a predmet KLUC.

Vo svojej hre implementujte minimálne tieto príkazy:

príkaz alias opis
KONIEC QUIT, EXIT Príkaz ukončí rozohratú hru. Nastaví príslušný stav hry.
SEVER S Presun do miestnosti nachádzajúcej sa na sever od aktuálnej. Zmení referenciu aktuálnej miestnosti.
JUH J Presun do miestnosti nachádzajúcej sa na juh od aktuálnej. Zmení referenciu aktuálnej miestnosti.
VYCHOD V Presun do miestnosti nachádzajúcej sa na východ od aktuálnej. Zmení referenciu aktuálnej miestnosti.
ZAPAD Z Presun do miestnosti nachádzajúcej sa na západ od aktuálnej. Zmení referenciu aktuálnej miestnosti.
ROZHLIADNI SA Príkaz vypíše aktuálne informácie o miestnosti, v ktorej sa hráč práve nachádza.
PRIKAZY HELP, POMOC Príkaz vypíše na obrazovku zoznam všetkých príkazov, ktoré hra poskytuje.
VERZIA Príkaz zobrazí číslo verzie hry, ľubovoľný sprievodný text a meno a priezvisko autora s kontaktom (e-mailová adresa, webová stránka).
RESTART Znovu spustí hru od začiatku. Zmení stav hry na požadovaný.
O HRE ABOUT Príkaz zobrazí krátky text, ktorý poslúži ako úvod do príbehu. Ako dobrý začiatok sa javí známy text: Kde bolo tam bolo, …
VEZMI Vloží predmet z miestnosti do batohu. Príkaz má jeden povinný parameter, ktorým je názov predmetu. Ak predmet nebude zadaný, program vypíše na obrazovku vhodnú hlášku (napr. Neviem, čo chceš vziať.).
POLOZ Položí predmet z batohu do miestnosti. Príkaz má jeden povinný parameter, ktorým je názov predmetu. Ak predmet nebude zadaný, program vypíše na obrazovku vhodnú hlášku (napr. Neviem, čo chceš položiť.)
INVENTAR I Zobrazí obsah hráčovho batohu.
POUZI Použije predmet z batohu alebo miestnosti. Príkaz má jeden povinný parameter, ktorým je názov predmetu. Ak predmet nebude zadaný, program vypíše na obrazovku vhodnú hlášku (napr. Neviem, čo chceš použiť.).
PRESKUMAJ Vypíše opis predmetu, ktorý sa musí nachádzať v miestnosti alebo batohu. Príkaz má jeden povinný parameter, ktorým je názov predmetu. Ak predmet nebude zadaný alebo sa nenájde v batohu alebo v miestnosti, program vypíše na obrazovku vhodnú hlášku (napr. Neviem, čo chceš preskúmať.).
NAHRAJ LOAD Príkaz zabezpečí nahratie uloženej pozície hry z disku. Voliteľným parametrom je cesta k súboru.
ULOZ SAVE Príkaz uloží stav rozohratej hry na disk. Voliteľným parametrom je cesta k súboru.

História príkazov

V rámci štruktúrovaného typu struct parser sa nachádza položka history, ktorá obsahuje referenciu na zoznam zadávaných príkazov. To znamená, že keď zadáte príkaz hry, automaticky sa do tohto zoznamu uloží. Samozrejme - nemusí sa ukladať každý príkaz, ako napr. nesprávne vstupy (AHOJ JANO a pod.) alebo príkazy, ktoré sa nepodieľajú na riešení hry (VERZIA, O HRE a pod.).

Tento zoznam sa použije na uloženie rozohratého stavu hry po zadaní príkazu ULOZ alebo na nahratie uloženého stavu hry pomocou príkazu NAHRAJ. Pri ukladaní histórie do súboru ukladajte každý príkaz na samostatný riadok. Pri načítavaní uloženej histórie zasa zabezpečte, aby sa po načítaní nachádzal v zozname histórie len načítaný zoznam.

Úloha #1: Funkcia create_parser()

Funkcia vytvorí štruktúru parser a vráti jej referenciu alebo NULL, ak sa štruktúru nepodarilo vytvoriť. Okrem toho však parser vytvorí zoznam príkazov, ktorým bude parser rozumieť.

Úlohad #2: Funkcia destroy_parser()

Funkcia odstráni z pamäte štruktúru parser a rovnako tak uvoľní oba zoznamy, ktoré sú členmi tejto štruktúry.

Funkcia vracia hodnotu NULL ako referenciu na novú štruktúru.

Úloha #3: Funkcia parse_input()

Úlohou tejto funkcie je rozpoznať vstup, ktorý používateľ zadal. V prípade, že vstup úspešne rozpozná, funkcia vráti referenciu na rozpoznaný príkaz. Ak sa funkcii vstup nepodarí rozpoznať, vráti NULL.

Pri rozpoznávaní vstupu nezabudnite, že nezáleží na veľkosti písmen, a že parser má ignorovať nepotrebné biele znaky v príkaze.

Príklad použitia modulu

Použitie modulu Parser demonštruje nasledujúci fragment kódu:

// create parser first
struct parser* parser = create_parser();
/*
 * Inside of the function create and add commands to the list of existing commands
 *
 */

 // parse input in the game loop
 char* input = "     VERZIA   ";
 struct command* cmd = parse_input(parser, input);
 // cmd now should have reference to the command "VERZIA"

// if input is unknown, parsing will return NULL
 char* unknown_input = "   CO JE V MIESTNOSTI?  ";
 cmd = parse_input(parser, unknown_input);
 // cmd = NULL;

// destroy parser at the end of game
parser = destroy_parser(parser);

Modul World

Modul obsahuje funkcie na vytvorenie a zrušenie sveta hry. Pri spustení hry sa zavolá funkcia create_world(), v rámci, ktorej dôjde k vytvoreniu jednotlivých lokácií, z ktorých sa skladá herný svet, ich vzájomného prepojenia, ako aj umiestnenia predmetov do miestnostní. Pri ukončovaní hry sa zasa zavolá funkcia destroy_world(), pomocou ktorej dôjde k odstráneniu celého virtuálneho sveta (všetkých lokácií) spolu s predmetmi, ktoré boli v jednotlivých miestnostiach umiestnené.

Úloha #1: Funkcia create_world()

Funkcia vytvorí virtuálny svet a vráti referenciu naň. Vytvorí teda spájaný zoznam kontajnerov, kde každý bude obsahovať jednu miestnosť.

Okrem zoznamu všetkých miestností vytvorí aj vzájomné prepojenie jednotlivých miestností - nastaví východy z miestností.

Úloha #2: Funkcia destroy_world()

Funkcia odstráni zoznam všetkých miestností, z ktorých sa skladá svet. Odstráni kontajnery spolu s miestnosťami v nich a predmetmi, ktoré sa v miestnostiach nachádzajú.

Funkcia vracia hodnotu NULL ako novú referenciu na zoznam miestností.

Úloha #3: Funkcia add_room_to_world()

Funkcia add_room_to_world() pridá do zoznamu sveta novú miestnosť. Ak svet (ešte) neexistuje, funkcia svet vytvorí (bude pozostávať z jednej miestnosti). Inak funkcia pridá do sveta miestnosť z parametra a vráti referenciu na vzniknutý kontajner miestnosti.

Pridať je však možné len takú miestnosť, ktorá je v svete jedinečná podľa názvu. Ak bude funkcia volaná s miestnosťou, ktorej názov už má iná miestnosť vo svete, nová miestnosť do sveta pridaná nebude a funkcia vráti NULL.

Úloha #4: Funkcia get_room()

Funkcia vráti miestnosť nachádzajúcu sa v svete na základe jej názvu. Ak sa vo svete taká miestnosť nenachádza, funkcia vráti NULL.

Toto je užitočné v prípadoch, ak chcete vytvoriť prechod z aktuálnej miestnosti do ďalšej až po splnení nejakej podmienky. Napr. po odomknutí dverí nachádzajúcich sa v miestnosti sa otvorí tajný priechod alebo po použití teleportu vás tento prenesie do novej miestnosti (zmení sa hodnota aktuálnej miestnosti v štruktúre game)

Príklad použitia

Použitie modulu World demonštruje nasledujúci fragment kódu:

// create world
struct container* world = create_world();
/*
 * This function creates world structure and initializes the world with usage of function add_room_to_world():
 *
 * struct room* home = create_room("start", "Nachadzas sa v chyzi svarneho suhaja. Na vychode sa nachadzaju dvere veduce z chyze von.");
 * world = add_room_to_world(NULL, home);
 * struct room* garden = create_room("garden", "Stojis pred chyzou a rozoznavas zahradku, ktora je znacne neudrziavana. este ze husty lesik na severe v porovnani so zahradkou nicim nevynika.");
 * add_room_to_world(world, garden);
 *
 * // during the creation of world, you also set exits
 * set_exits_from_room(home, NULL, NULL, garden, NULL);
 * set_exits_from_room(garden, NULL, NULL, NULL, home);
 *
 * // and later you will place the items in the rooms
 */

// search the room by it's name
struct room* start_room = get_room(world, "start");
// returns reference to the room

struct room* station = get_room(world, "station");
// returns NULL

// destroy room at the end
world = destroy_world(world);

Modul Item

Tento modul slúži na vytváranie a rušenie predmetov, ktoré je možné v hre nájsť, vziať do batohu alebo nejakým spôsobom použiť. Okrem toho obsahuje enumeračný typ enum properties, pomocou ktorého je možné nastaviť každému predmetu niekoľko vlastností.

O každom predmete pritom poznáme jeho:

  • meno
  • opis, a
  • súbor vlastností.

Meno predmetu sa zobrazí vždy vtedy, keď hráč vojde do miestnosti a v miestnosti sa nejaký predmet alebo predmety nachádzajú. Rovnako sa zobrazí vtedy, keď hráč zadá príkaz INVENTÁR, ktorým zobrazí obsah batohu.

Opis predmetu sa zobrazí po zadaní príkazu PRESKUMAJ predmet.

Vlastnosti predmetu viete použiť rozličným spôsobom. Napr. ak nie je predmet prenositeľný (nemá nastavenú vlastnosť MOVABLE), nebudete ho môcť vziať do batohu. Jednotlivé predmety môžu mať naraz samozrejme viacero vlastností - môžu byť aj prenositeľné (vlastnosť MOVABLE) a rovnako môžu byť použiteľné (vlastnosť USABLE).

Úloha #1: Funkcia create_item()

Táto funkcia vytvorí nový predmet, pričom meno predmetu aj jeho opis sú povinné. Ak niektorý z parametrov nie je zadaný alebo je referenciou na NULL, predmet nebude vytvorený a vráti NULL. V opačnom prípade vráti referenciu na predmet v pamäti.

Príklad použitia:

struct item* key = create_item("ZLATY KLUC",
    "Zlaty kluc pravdepodobne od zlatej zamky. Su na nom viditelne vyryte pismena AB",
    MOVABLE | USABLE );

Úloha #2: Funkcia destroy_item()

Funkcia odstráni vytvorený predmet z pamäte. Vracia referenciu NULL.

Príklad použitia:

key = destroy_item(key);

Modul Backpack

Tento modul obsahuje štruktúru struct backpack, ktorá reprezentuje hráčov batoh. Batoh sa bude nachádzať v hre len jeden a budeme o ňom vedieť:

  • koľko predmetov sa do neho vojde (kapacita batohu)
  • koľko predmetov sa v ňom práve nachádza
  • zoznam predmetov, ktoré sa v ňom nachádzajú

Okrem štruktúry struct backpack sa v tomto module nachádzajú funkcie, pomocou ktorých je možné s batohom pracovať.

Úloha #1: Funkcia create_backpack()

Funkcia na vytvorenie batohu. Parametrom funkcie je jeho kapacita. Funkcia vracia referenciu na novovytvorený batoh.

Príklad použitia:

struct backpack* backpack = create_backpack(5);

Úloha #2: Funkcia destroy_backpack()

Funkcia odstráni vytvorený batoh z pamäte. Vracia referenciu NULL.

Príklad použitia:

backpack = destroy_backpack(backpack);

Úloha #3: Funkcia add_item_to_backpack()

Funkcia pridá (vloží) predmet do batohu. V prípade, že sa predmet podarilo do batohu vložiť (v batohu je ešte voľné miesto a predmet je prenositeľný), funkcia vráti hodnotu true. V opačnom prípade funkcia vracia hodnotu false.

Príklad použitia:

add_item_to_backpack(backpack, key);

Úloha #4: Funkcia get_item_from_backpack()

Funkcia prehľadá batoh a pokúsi sa v ňom nájsť predmet, ktorého meno je zadané v parametri funkcie. Ak sa predmet v batohu nachádza, funkcia vráti jeho referenciu. Ak sa predmet v batohu nenachádza, funkcia vráti NULL. V mene predmetu nezáleží na veľkosti písmen.

Príklad použitia:

struct item* item = get_item_from_backpack(backpack, "zlaty klucik");

Úloha #5: Funkcia delete_item_from_backpack()

Funkcia odstráni predmet z batohu. Predmet je zadaný parametrom funkcie. Predmet samotný ale nebude odstránený z pamäte.

delete_item_from_backpack(backpack, key);

Práca s pamäťou

Pri implementácii funkcií create_* pre vytváranie štruktúr používaných v hre, je potrebné zabezpečiť, aby všetky položky štruktúr boli “vlastnené” danou štruktúrou a aby boli alokované v heape. V náväznosti na to, všetka pamäť referencovaná zo štruktúr by mala byť uvoľnená v príslušných destroy_* funkciách.

Úspešný scenár hry

Úspešný scenár hry predstavuje postupnosť príkazov, ktoré je potrebné zadať, aby hráč hru prešiel od začiatku do jej úspešného cieľa. Tento scenár je možné považovať za návod na prejdenie celej hry.

Každý príkaz úspešného scenára sa nachádza na samostatnom riadku. Príklad takéhoto scenára je uvedený nižšie:

pouzi skala
juh
zapad
juh
juh
vychod
pouzi skala
sever
vezmi mec
vychod
vychod
sever
preskumaj truhlica
vezmi strieborny kluc
vezmi lano
juh
vychod
pouzi strieborny kluc
juh
vezmi elixir
vychod
juh
pouzi mec
juh
vezmi tajomna krabicka
sever
sever
vychod
vezmi modry plamen
sever
pouzi elixir
sever
vezmi zlaty kluc
vychod
vychod
vezmi kladivo
zapad
poloz tajomna truhlica
pouzi kladivo
vezmi naramok
zapad
zapad
pouzi naramok
sever
pouzi lano
poloz lano
sever
pouzi mec
vychod
vychod
pouzi zlaty kluc
sever
pouzi modry plamen
zapad
vezmi princezna

Aké sú požiadavky na vytvorenie úspešného scenára?

Samozrejme sa nejedná len o nejakú náhodnú postupnpsť príkazov. Aby ste mohli takýto scenár vytvoriť, musíte si vlastnú hru vymyslieť. To obnáša vytvorenie mapy herného sveta a predmetov, s ktorými môže hráč akýmkoľvek spôsobom interagovať.

Pre vytvorenie úspešného scenára a teda aj samotnej hry je potrebné splniť nasledovné:

  • dĺžka minimálneho scenára je aspoň 25 krokov
  • počet predmetov v celej hre je minimálne 5
  • počet miestností v hre je minimálne 15
  • hra musí obsahovať minimálne základnú sadu príkazov
  • hra musí byť implementovaná pomocou knižnice stiahnuteľnej zo stránky
  • v hre nepoužívať názvy príkazov alebo predmetov v tvare KLUC1, KRASNA_PRINCEZNA, KRASNAPRINCEZNA, ale v zrozumiteľnom tvare s použitím medzery a bez použitia čísiel (vhodnou náhradou miesto tvaru KLUC1 je napr. ZLATY KLUC, STRIEBORNY KLUC a pod.)

Odovzdávanie projektu

Zadanie sa odovzdáva prostredníctvom systému na správu verzií Git na serveri git.kpi.fei.tuke.sk. Riešenie tejto úlohy odovzdáte ako súčasť vášho projektu.

Štruktúra vášho projektu bude vyzerať nasledovne:

.
├── ps5
│   ├── backpack.c
│   ├── backpack.h
│   ├── command.c
│   ├── command.h
│   ├── container.c
│   ├── container.h
│   ├── game.c
│   ├── game.h
│   ├── item.c
│   ├── item.h
│   ├── main.c
│   ├── Makefile
│   ├── parser.c
│   ├── parser.h
|   ├── scenario.txt
│   ├── room.c
│   ├── room.h
│   ├── world.c
│   └── world.h
└── README

kde význam súborov v priečinku ps5/ je nasledovný:

  • backpack.c, backpack.h - Zdrojový kód a hlavičkový súbor pre modul Backpack.
  • command.c, command.h - Zdrojový kód a hlavičkový súbor pre modul Command.
  • container.c, container.h - Zdrojový kód a hlavičkový súbor pre modul Container.
  • game.c, game.h - Zdrojový kód a hlavičkový súbor pre modul Game.
  • item.c, item.h - Zdrojový kód a hlavičkový súbor pre modul Item.
  • parser.c, parser.h - Zdrojový kód a hlavičkový súbor pre modul Parser.
  • room.c, room.h - Zdrojový kód a hlavičkový súbor pre modul Room.
  • world.c, world.h - Zdrojový kód a hlavičkový súbor pre modul World.
  • main.c - Zdrojový kód obsahujúci funkciu main().
  • Makefile - Makefile súbor obsahujúci ciele na vygenerovanie samostatných modulov a cieľ all a clean
  • scenario.txt - úspešný scenár hry
  • README - Súbor, v ktorom bude uvedené označenie vašej skupiny, ktorú navštevujete na cvičeniach v tvare:
GROUP : A1

Ak ste opakujúci študent, uveďte v README skupinu v tvare:

GROUP : O<P>

kde <P> nahradíte písmenom paralelky, ktorá podľa rozvrhu zodpovedá vášmu študijnému programu (napr. A pre informatikov).

Kostra projektu

Z nasledujúceho odkazu si stiahnite súbor adventure.zip, ktorý obsahuje kostru projektu. Tento balíček obsahuje hlavičkové súbory projektu.

V prostredí OS Linux môžete pre stiahnutie použiť príkaz wget v tvare:

wget https://kurzy.kpi.fei.tuke.sk/pvjc/2024/download/adventure.zip

Hodnotenie a testovanie

V rámci tohto zadania nie je predpísaná grafická podoba výstupu ani dialóg medzi používateľom a počítačom. V tom máte voľnú ruku.

Vaše hodnotenie sa bude odvíjať od výsledku testov, ktorými vaše zadanie úspešne prejde. Overovať sa bude:

  • Štruktúra vášho projektu (či sa v ňom nachádzajú všetky potrebné súbory).
  • Statická analýza vášho kódu pomocou nástroja cppcheck.
  • Kontrola únikov v pamäti pomocou nástroja valgrind.
  • Prítomnosť globálnych premenných vo vašom kóde.
  • Funkčnosť vašej implementácie.

Váš kód sa bude prekladať prekladačom gcc s nasledovnými prepínačmi a knižnicami:

$ gcc -std=c11 -Werror -Wall -Wconversion -lm

Testovanie vašich riešení sa bude vykonávať automaticky každé 3 hodiny.

Vaše riešenia opäť prejdú kontrolou originality. Preto sa pri práci na vašom zadaní správajte podľa pravidiel etického kódexu! V prípade, že odovzdáte zadanie, ktoré nie je vaše, môžete byť vylúčení z predmetu!

Ďalšie zdroje