8. týždeň

Problem Set 3: Hangman & Morse

Ciele

  1. Vytvoriť vlastné funkcie podľa špecifikácie.
  2. Rutinne pracovať s jednorozmerným poľom a reťazcami.
  3. Používať pole ako parameter (vstupný a výstupný) vo funkciách.

Hangman

Určite ste už niekedy hrali hru so slovami Hangman, v ktorej je potrebné hádať slovo po písmenách. Uhádnuté písmená sa postupne dopisujú do tajničky a pri každom neúspešnom pokuse sa naopak nakreslí časť šibenice. Hra sa končí, keď hráč uhádne slovo alebo keď druhý hráč nakreslí celú šibenicu.

Spoločenské prevedenie hry Hangman. Jej napodobovanie sa však neodporúča.
Obr. 1: Spoločenské prevedenie hry Hangman. Jej napodobovanie sa však neodporúča.

V našej implementácii bude prvým hráčom používateľ, ktorý bude hádať slovo tak, že vždy napíše na obrazovku jedno písmeno. Na uhádnutie slova bude mať 8 pokusov. Druhým hráčom bude vždy počítač, ktorý si najskôr náhodne vyberie tajné slovo zo slovníka. Výber bude zabezpečený volaním funkcie int get_word(char secret[]), ktorá vráti slovo s max. dĺžkou 15 znakov (túto funkciu už budete mať pripravenú).

Vašou úlohou bude naprogramovať štyri funkcie:

  • int is_word_guessed(const char secret[], const char letters_guessed[]) - Zistí, či na základe hádaných písmen hráč uhádol tajné slovo (malé písmená).
  • void get_guessed_word(const char secret[], const char letters_guessed[], char guessed_word[]) - Aktualizuje hádané slovo (malé písmená) tak, že na tajných, zatiaľ neuhádnutých pozíciách bude obsahovať znak '_' a na odkrytých, už uhádnutých miestach bude obsahovať konkrétne písmeno.
  • void get_available_letters(const char letters_guessed[], char available_letters[]) - Aktualizuje zoznam dostupných písmen, ktoré ešte neboli hádané (malé písmená).
  • void hangman(const char secret[]) - Obsahuje funkcionalitu samotnej hry.

Všetkých 5 funkcií (štyri, ktoré máte naprogramovať a funkcia get_word(), ktorú už dostanete) sa bude nachádzať v súbore hangman.c. Ich deklarácie sú uvedené v súbore hangman.h. Hlavný program sa nachádza v súbore main.c, ktorý načíta náhodné slovo pomocou funkcie get_word() a odovzdá ho funkcii hangman() na realizáciu samotnej hry.

Okrem požadovaných funkcií si môžete vytvoriť aj vlastné funkcie. Tieto však budú súkromné pre Váš modul (nebudú deklarované v hlavičkovom súbore hangman.h).

Na začiatku hry je hráč oboznámený s dĺžkou tajného slova. V každom kole je spracované práve jedno písmeno a hráč je okamžite oboznámený o tom, či sa písmeno nachádza v tajnom slove alebo nie. Po každom kole by sa tiež malo zobraziť samotné slovo tak, aby bolo jasné, ktoré písmená už boli uhádnuté a ktoré zatiaľ nie.

Nezabudnite hráčovi pripomenúť, koľko pokusov má ešte k dipozícii. Hráč stráca pokus iba ak háda nesprávne, t.j. dané písmeno sa v tajnom slove nenachádza. Ak hráč zadá to isté písmeno viackrát, neprichádza o pokus, oznámte mu však, že sa jeho tip už zopakoval.

Hra končí, ak hráč uhádne tajné slovo alebo ak sa hráčovi minú všetky pokusy. Ak hráč neuhádne tajné slovo, toto slovo je vypísané na obrazovku.

Úloha 1: Je slovo uhádnuté?

Naprogramujte funkciu int is_word_guessed(const char secret[], const char letters_guessed[]) s dvoma parametrami:

  • const char secret[] - Reťazec reprezentujúci tajné slovo (malé písmená)
  • const char letters_guessed[] - Reťazec so všetkými doposiaľ hádanými písmenami

Funkcia vráti hodnotu 1, ak tajné slovo uložené v parametri secret bolo uhádnuté (je ho možné zostaviť z hádaných písmen uložených v parametri letters_guessed). V opačnom prípade vráti funkcia hodnotu 0.

Príklad použitia funkcie:

printf("%d\n", is_word_guessed("secret", "aeiou"));
// prints: 0
printf("%d\n", is_word_guessed("hello", "aeihoul"));
// prints: 1

Poznámka

Pre znaky v celom programe platí, že ide o malé písmená abecedy.

Úloha 2: Aktualizácia hádaného slova

Naprogramujte funkciu void get_guessed_word(const char secret[], const char letters_guessed[], char guessed_word[]) s troma paramerami:

  • const char secret[] - Reťazec reprezentujúci tajné slovo (malé písmená)
  • const char letters_guessed[] - Reťazec so všetkými doposiaľ hádanými písmenami
  • char guessed_word[] - Výstupný parameter funkcie, ktorý bude reprezentovať reťazec hádaného slova

Funkcia nevráti žiadnu hodnotu.

Funkcia aktualizuje hádané slovo guessed_word tak, že na tajných, zatiaľ neuhádnutých pozíciách bude obsahovať znak '_' a na odkrytých, už uhádnutých miestach bude obsahovať konkrétne písmeno.

Príklad použitia funkcie:

char result[30];
get_guessed_word("container", "arpstxgoieyu", result);
// result = "_o_tai_er"

Poznámka

Nezabudnite, že pre znaky v celom programe platí, že ide o malé písmená abecedy.

Úloha 3: Aktualizácia dostupných písmen

Naprogramujte funkciu void get_available_letters(const char letters_guessed[], char available_letters[]) s dvoma parametrami:

  • char letters_guessed[] - Reťazec so všetkými doposiaľ hádanými písmenami (malé písmená)
  • char available_letters[] - Reťazec abecedy okrem doposiaľ hádaných písmen. Výstupný parameter funkcie.

Funkcia nevráti žiadnu hodnotu.

Funkcia aktualizuje reťazec dostupných písmen available_letters vynechaním už hádaných písmen pomocou parametra letters_guessed. Tento zoznam písmen bude usporiadaný vzostupne.

Príklad použitia funkcie:

char result[30];
get_available_letters("arpstxgoieyu", result);
// result = "bcdfhjklmnqvwz"

Poznámka

Nezabudnite, že pre znaky v celom programe platí, že ide o malé písmená abecedy.

Úloha 4: Hra

Naprogramujte funkciu void hangman(const char secret[]) s jednym parametrom:

  • const char secret[] - Reťazec reprezentujúci tajné slovo

Funkcia nevráti žiadnu hodnotu.

Funkcia realizuje interaktívnu hru medzi používateľom a počítačom s využitím Vami vytvorených funkcií int is_word_guessed(), void get_guessed_word() a void get_available_letters().

Na začiatku hra oboznámi hráča s dĺžkou tajného slova. V každom kole hry sa spracuje práve jedno písmeno a hráč je oboznámený s počtom zostávajúcich pokusov a reťazcom dostupných písmen. Po zadaní znaku je hráč okamžite informovaný o úspešnosti jeho tipu spolu s výpisom aktuálneho hádaného slova s vyznačenými uhádnutými písmenami.

Hráč však môže z klávesnice zadať ľubovoľné písmeno - aj veľké aj malé! V tomto prípade sú si písmená rovnocenné (nezáleží na ich veľkosti). Túto funkcionalitu naprogramujte priamo vo funkcii hangman().

Hra by mala po každom pokuse vypísať počet zostávajúcich pokusov. Počet pokusov sa znižuje o jedno, ak bol tip hráča nesprávny. Naopak, počet pokusov zostáva rovnaký, ak bol tip hráča správny, ale aj keď hráč zadá opakovane rovnaký tip. Vtedy je potrebné o tom hráča informovať.

Slovo, ktoré ste získali ako výstup z funkcie get_guessed_word(), kvôli zvýšeniu čitateľnosti vypisujte v tvare l a _ _ s _ a _ e.

Ak hráč zadá celé slovo (nie iba 1 písmeno), hra ihneď končí a hráčovi oznámi, či vyhral alebo nie.

Príklady použitia programu

Ak hráč uhádne tajné slovo, hra končí:

$ ./hangman
Welcome to the game, Hangman!
I am thinking of a word that is 7 letters long.
-------------
You have 8 guesses left.
Available letters: abcdefghijklmnopqrstuvwxyz
Please guess a letter: l
Good guess: _ _ _ _ l _ _
-------------
You have 8 guesses left.
Available letters: abcdefghijkmnopqrstuvwxyz
Please guess a letter: e
Good guess: _ _ _ _ l e _
-------------
You have 8 guesses left.
Available letters: abcdfghijkmnopqrstuvwxyz
Please guess a letter: d
Oops! That letter is not in my word: _ _ _ _ l e _
-------------
You have 7 guesses left.
Available letters: abcfghijkmnopqrstuvwxyz
Please guess a letter: a
Oops! That letter is not in my word: _ _ _ _ l e _
-------------
You have 6 guesses left.
Available letters: bcfghijkmnopqrstuvwxyz
Please guess a letter: U
Good guess: _ u _ _ l e _
-------------
You have 6 guesses left.
Available letters: bcfghijkmnopqrstvwxyz
Please guess a letter: u
Oops! You've already guessed that letter: _ u _ _ l e _
-------------
You have 6 guesses left.
Available letters: bcfghijkmnopqrstvwxyz
Please guess a letter: s
Good guess: _ u _ _ l e s
-------------
You have 6 guesses left.
Available letters: bcfghijkmnopqrtvwxyz
Please guess a letter: p
Good guess: p u _ p l e s
-------------
You have 6 guesses left.
Available letters: bcfghijkmnoqrtvwxyz
Please guess a letter: r
Good guess: p u r p l e s
-------------
Congratulations, you won!

Ak sa hráčovi minie všetkých 8 pokusov, hra končí spolu s vypísaním tajného slova:

$ ./hangman
Welcome to the game, Hangman!
I am thinking of a word that is 10 letters long.
-------------
You have 8 guesses left.
Available letters: abcdefghijklmnopqrstuvwxyz
Please guess a letter: a
Oops! That letter is not in my word: _ _ _ _ _ _ _ _ _ _
-------------
You have 7 guesses left.
Available letters: bcdefghijklmnopqrstuvwxyz
Please guess a letter: e
Good guess: _ _ _ e _ e _ _ e _
-------------

...

-------------
You have 1 guesses left.
Available letters: hjklmnpqrstvwxyz
Please guess a letter: h
Oops! That letter is not in my word: u _ d e _ e _ _ e d
-------------
Sorry, you ran out of guesses. The word was undeserved.

Ak hráč zadá celé slovo, hra končí a hráč ihneď vie, či vyhral alebo nie:

$ ./hangman
Welcome to the game, Hangman!
I am thinking of a word that is 4 letters long.
-------------
You have 8 guesses left.
Available letters: abcdefghijklmnopqrstuvwxyz
Please guess a letter: e
Oops! That letter is not in my word: _ _ _ _
-------------
You have 7 guesses left.
Available letters: abcdfghijklmnopqrstuvwxyz
Please guess a letter: ball
Congratulations, you won!
$ ./hangman
Welcome to the game, Hangman!
I am thinking of a word that is 4 letters long.
-------------
You have 8 guesses left.
Available letters: abcdefghijklmnopqrstuvwxyz
Please guess a letter: o
Good guess: _ o _ _
-------------
You have 8 guesses left.
Available letters: abcdefghijklmnpqrstuvwxyz
Please guess a letter: word
Sorry, bad guess. The word was goal.

Ak hráč zadá znak, ktorý nie je písmeno, počet zvyšných pokusov sa nezmení:

$ ./hangman
Welcome to the game, Hangman!
I am thinking of a word that is 7 letters long.
-------------
You have 8 guesses left.
Available letters: abcdefghijklmnopqrstuvwxyz
Please guess a letter: a
Good guess: _ a _ _ _ a _
-------------
You have 8 guesses left.
Available letters: bcdefghijklmnopqrstuvwxyz
Please guess a letter: e
Oops! That letter is not in my word: _ a _ _ _ a _
-------------
You have 7 guesses left.
Available letters: bcdfghijklmnopqrstuvwxyz
Please guess a letter: @
Oops! '@' is not a valid letter: _ a _ _ _ a _
-------------
You have 7 guesses left.
Available letters: bcdfghijklmnopqrstuvwxyz
Please guess a letter: h
Good guess: h a _ _ _ a _
-------------
You have 7 guesses left.
Available letters: bcdfgijklmnopqrstuvwxyz
Please guess a letter: N
Good guess: h a n _ _ a n
-------------
You have 7 guesses left.
Available letters: bcdfgijklmopqrstuvwxyz
Please guess a letter: hangman
Congratulations, you won!

Morse Code

Morzeovka je telekomunikačná technológia, ktorá kóduje znaky na postupnosť signálov dvoch dĺžok – dlhý a krátky. Presne pomocou tejto technológie volal o pomoc Titanic keď narazil do ľadovca. Vaším druhým zadaním bude práve implementácia nástroja schopného kódovať text na morzeovku a naopak. Jej kódovaciu tabuľku môžete nájsť napríklad tu.

V našom zadaní bude krátky signál predstavovať znak '.' a dlhý signál znak '-'. Nezabudnite, že kódované znaky v morzeovke musia byť oddelené medzerou.

Úloha 5:

Naprogramujte funkciu void text_to_morse(const char text[], char output[]) s dvoma parametrami:

  • const char text[] - pole znakov (reťazec) ako text, ktorý má funkcia preložiť do morzeovky,
  • char output[] - výstupné pole preloženého reťazca na znaky morzeovky.

Funkcia nevráti žiadnu hodnotu. Funkcia naplní pole znakmi morzeovky tak, že každý kódovaný znak bude oddelený medzerou.

Príklad použitia:

char output[150];

text_to_morse("Hello", output);
puts(output);
//prints: .... . .-.. .-.. ---

Úloha 6:

Naprogramujte funkciu void morse_to_text(const char morse[], char output[]) s dvoma parametrami:

  • const char morse[] - pole znakov morzeovky, ktorý má funkcia preložiť do späť do abecedy,
  • char output[] - výstupné pole preloženého reťazca na znaky abecedy.

Funkcia nevráti žiadnu hodnotu. Funkcia naplní pole znakmi abecedy tak, že tentokrát znaky ani slova nebudú nijako oddelené medzerou.

Príklad použitia:

char output[150];

morse_to_text(".... . .-.. .-.. ---", output);
//prints: HELLO

Úloha 7:

Naprogramujte funkciu int is_morse_code_valid(const char morse[]) s jednym parametrom:

  • const char morse[] - pole znakov morzeovky.

Funkcia vráti hodnotu 1 v prípade, že kód morzeovky je validný, t.j každý nájdený znak sa dá identifikovať a preložiť. Funkcia vráti hodnotu 0 v prípade, že sa našiel aspoň jeden nevalidný znak morzeovky.

Príklad použitia:

if (is_morse_code_valid(".... . .-.. .-.. ---")) {
     printf("Code is valid! \n");
   } else {
    printf("Code is invalid! \n");
   }
    //prints: Code is valid!

   //....
    if (is_morse_code_valid(".... . .-.--. .-.. ---")) {
     printf("Code is valid! \n");
   } else {
    printf("Code is invalid! \n");
   }

   //prints: Code is invalid!

Odovzdávanie projektu

Zadanie odovzdajte do 23.11.2023 (štvrtok). Posledné testovanie prebehne v tento deň o polnoci.

Zadanie sa odovzdáva prostredníctvom systému na správu verzií Git na serveri git.kpi.fei.tuke.sk.

Názov Vášho projektu musí byť v tvare: zap-2023-id.

Projekt musí mať nasledujúcu štruktúru priečinkov a súborov:

.
├── ps3
│   ├── hangman.c
│   ├── hangman.h
│   ├── morse.h
│   ├── morse.c
│   ├── main.c
└── README

Význam jednotlivých súborov je nasledovný:

  • README - Súbor, v ktorom bude uvedená Vaša skupina, ktorú navštevujete na cvičeniach v tvare:
GROUP : C1
  • /ps3/hangman.c - Zdrojový kód knižnice pre hru hangman.
  • /ps3/hangman.h - Hlavičkový súbor knižnice hangman.
  • /ps3/morse.h - Hlavičkový súbor knižnice morse.
  • /ps3/morse.c - Zdrojový kód pre morse.
  • /ps3/main.c - Zdrojový kód obsahujúci funkciu main().

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.

Upozornenie

Pri názvoch priečinkov, súborov a obsahu súboru README záleží na veľkosti písmen!

Poznámka

Ak sa vo Vašom projekte budú nachádzať ďalšie súbory okrem požadovaných, ich existencia nebude považovaná za chybu.

Upozornenie

Ak používate Makefile, preklad programu uskutočnite pomocou príkazu make all.

Kostra projektu

Z nasledujúceho odkazu si stiahnite súbor hangman.zip, ktorý obsahuje kostru projektu. Tento balíček obsahuje nasledujúce súbory:

  • words.txt - Súbor, ktorý obsahuje 55900 rozličných anglických slov.
  • hangman.h - Hlavičkový súbor, v ktorom sa nachádzajú deklarácie všetkých požadovaných funkcií Hangmana.
  • hangman.c - Zdrojový súbor, v ktorom sa bude nachádzať Vaša výsledná implementácia, a v ktorom sa nachádza už implementovaná funkcia get_word() pre načítanie náhodného slova zo súboru words.txt.
  • Makefile - Súbor pre preklad programu, ktorý uskutočnite pomocou príkazu make all.
  • morse.h - Hlavičkový súbor, v ktorom sa nachádzajú deklarácie všetkých požadovaných funkcií Morzeovky.
  • morse.c - Zdrojový súbor, v ktorom sa bude nachádzať Vaša výsledná implementácia.

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

wget http://kurzy.kpi.fei.tuke.sk/zap/resources/hangman.zip

Hodnotenie a testovanie

Za zadanie môžete získať max. 10 bodov. Ľubovoľné 2 body sa započítajú do zápočtu, ostatné body sa započítajú do skúšky. Počet získaných bodov sa bude odrážať 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).
  • Funkčnosť Vašej implementácie.

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

gcc -std=c11 -Werror -Wall -lm

Ak používate nami poskytnutý Makefile, preklad programu uskutočnite pomocou príkazu:

make all

Za chybu sa bude považovať:

  • Ak vo Vašej implementácii použijete globálnu premennú.
  • Ak počas prekladu dôjde ku chybe (upozornenia sú priamo konvertované na chyby).
  • Ak Vaša implementácia neprejde niektorým z testov.

Testovanie Vašich riešení sa bude vykonávať automaticky každé 3 hodiny a to konkrétne o 0300, 0600, 0900, 1200, 1500, 1800, 2100 a 2400.

Vaše riešenia 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!