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.
Upozornenie
Toto zadanie je potrebné odovzdať do 5. may. 2023
23:59:59. Na diskusiu používajte kanál na slack-u
#adventure
.
Textové adventúry
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)
Poznámka
Pred ďalším čítaním by ste si naozaj mali niektorú textovku vyskúšať, pretože vašou úlohou v tomto zadaní bude jednu vlastnú textovku vytvoriť. Áno - v jazyku C.
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
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 hreRoom
- modul pre prácu s miestnosťami, resp. lokáciami v hreWorld
- modul pre reprezentáciu herného svetaCommand
- modul pre prácu s príkazmiGame
- hlavný modul, v ktorom sa nachádza celá herná logikaParser
- modul pre rozpoznávanie vstupu od hráčaContainer
- 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íkazovITEM
na vytvorenie zoznamu predmetovTEXT
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
(home, NULL, NULL, garden, NULL);
set_exits_from_room(garden, NULL, NULL, NULL, home);
set_exits_from_room
// show room
(home);
show_room/*
Nachadzas sa v chyzi svarneho suhaja. Na vychode sa nachadzaju dvere veduce z chyze von.
Mozne vychody z miesnosti:
vychod
*/
// destroy rooms
= destroy_room(home);
home = destroy_room(garden); 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
(home, key); add_item_to_room
Ú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.
Poznámka
Funkcia odstráni daný predmet len z miestnosti. Neodstráni ho však z
pamäte. Táto funkcia je užitočná v implementácii príkazu
VEZMI
, kedy sa predmet z miestnosti vloží do batohu.
Príklad použitia
(home, key); delete_item_from_room
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:
= destroy_command(help); 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ť:
PLAYING
- základný stav, ktorý reprezentuje hru v stave hraniaGAMEOVER
- do tohto stavu sa hra dostane vtedy, ak hráč zadal príkaz na ukončenie hry alebo v hre zomrelSOLVED
- stav, do ktorého sa hra dostane po úspešnom dohratí hryRESTART
- stav hry, kedy došlo k zadaniu požiadavky na reštartovanie hry od začiatku pomocou príkazuRESTART
Ú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()
(game);
play_game
// destroy game at the end
= destroy_game(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íkazPOUZI
a predmetKLUC
.
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.
Upozornenie
Do histórie rozhodne neukladajte príkazy ULOZ
a
NAHRAJ
. Mohli by spôsobiť zacyklenie pri načítavaní vášho
stavu hry.
Ú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? ";
= parse_input(parser, unknown_input);
cmd // cmd = NULL;
// destroy parser at the end of game
= destroy_parser(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
= destroy_world(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",
| USABLE ); MOVABLE
Úloha #2: Funkcia
destroy_item()
Funkcia odstráni vytvorený predmet z pamäte. Vracia referenciu
NULL
.
Príklad použitia:
= destroy_item(key); 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:
= destroy_backpack(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:
(backpack, key); add_item_to_backpack
Ú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.
Poznámka
Ak sa predmet v batohu nachádza, nebude z neho odstránený. Funkcia vráti len referenciu na daný predmet.
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.
(backpack, key); delete_item_from_backpack
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.
Poznámka
Pozor na to, že vyššie uvedené platí aj pre položky name
a description
.
Ú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 tvaruKLUC1
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 modulBackpack
.command.c
,command.h
- Zdrojový kód a hlavičkový súbor pre modulCommand
.container.c
,container.h
- Zdrojový kód a hlavičkový súbor pre modulContainer
.game.c
,game.h
- Zdrojový kód a hlavičkový súbor pre modulGame
.item.c
,item.h
- Zdrojový kód a hlavičkový súbor pre modulItem
.parser.c
,parser.h
- Zdrojový kód a hlavičkový súbor pre modulParser
.room.c
,room.h
- Zdrojový kód a hlavičkový súbor pre modulRoom
.world.c
,world.h
- Zdrojový kód a hlavičkový súbor pre modulWorld
.main.c
- Zdrojový kód obsahujúci funkciumain()
.Makefile
-Makefile
súbor obsahujúci ciele na vygenerovanie samostatných modulov a cieľall
aclean
scenario.txt
- úspešný scenár hryREADME
- 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).
Upozornenie
Je dôležité, aby vaše súbory zachovali uvedenú štruktúru. Ak sa niektorý zo súborov síce v repozitári nachádza, ale v inom priečinku, bude to považované za chybu a takýto projekt nebude považovaný za správny! Ak sa naopak vo vašom projekte nachádzajú súbory alebo priečinky navyše, tieto nebudú považované za chybu.
Upozornenie
Pri názvoch priečinkov, súborov a obsahu súboru README
záleží na veľkosti písmen!
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
- textovkářův ráj - živý československý portál venujúci sa textovkám
- Introduction to Interactive Fiction
- Historie vývoje počítačových her (134. část – české textovky I) - seriál na serveri root.cz, ktorý sa od časti 134 venuje českým a slovenským textovkám.
- Interactive Fiction (Wikipedia)
- Text-based game (Wikipedia)
- Usborne Children’s Books - Zoznam kníh vydavateľstva Usborne z roku 1980 je zdarma. Vás môže zaujímať titul Write your own adventure programs for your microcomputer
- Syntax Guessing - článok venovaný problému hádania príkazov (najmä v textovkách)