5. týždeň

Guess the Number

Štandardný vstup a výstup, premenné, aritmetické výrazy, ladenie programov, hra "Uhádni číslo, ktoré si myslím"

O čom je lab

Na tomto cvičení sa zoznámite so základnými nástrojmi, ktoré budete pri práci používať. Budete pracovať s prekladačom gcc a nástrojom make. Popri tom vytvoríte svoju prvú hernú pecku s názvom: Uhádni číslo, ktoré si myslím.

Ciele

  1. Learn to work with the gcc compiler and understand some of its switches.
  2. To learn how to work with the make tool and how to set it up using environment variables.
  3. Familiarize yourself with the basic data types of the C language.
  4. Learn to work with variables.
  5. Create simple arithmetic expressions.
  6. Master the functions for working with standard I/O.
  7. Master working with custom functions and functions that have a return value.

Postup

Krok 1: Warm up

V tomto kroku vytvoríte (svoj prvý) program v jazyku C, preložíte ho a nastavíte svoje prostredie.

Úloha 1.1

Vytvorte adresár ~/labs/lab05, do ktorého budete ukladať svoje kódy programov v jazyku C.

Môžete použiť nasledujúce príkazy:

  • ls listuje obsah adresára
  • mkdir nazov vytvorí nový adresár
  • cd nazov zmení aktuálny adresár na iný (podadresár)
  • cd .. zmení aktuálny adresár na nadradený adresár
  • rmdir nazov zmaže adresár (pre prázdny adresár)
  • rm -r nazov zmaže adresár (pre neprázdny adresár)

Úloha 1.2

Vo svojom obľúbenom textovom editore si vytvorte nový súbor s názvom guess.c.

Úloha 1.3

Vytvorte program, pomocou ktorého vypíšete na obrazovku populárnu vetu: Hello world!

Riešením môže byť nasledujúci kód:

#include <stdio.h>

int main(){
    printf("Hello world!\n");

    return 0;
}

Úloha 1.4

Upravte program tak, aby obsahoval úvodný dialóg hry Uhádni číslo, ktoré si myslím medzi používateľom a počítačom. V tomto dialógu program vyzve používateľa, aby zadal svoj tip, ktorý od neho následne načítate v samostatnej funkcii get_guess(). Tip vráťte ako návratovú hodnotu funkcie get_guess().

Tento dialóg môže vyzerať nasledovne, pričom vstup od používateľa je zvýraznený tučne a riadok začínajúci znakom $ predstavuje príkazový riadok:

$ ./guess
Pick a number between 1 and 100.
Your guess: 50
You have entered: 50

Riešením tohto problému môže byť nasledujúci fragment kódu:

#include <stdio.h>

int get_guess();  // function declaration

int main(){
    printf("Pick a number between 1 and 100.\n");

    int guess = get_guess();  // stores returned value in variable

    printf("You have entered: %d\n", guess);

    return 0;
}

// function definition
int get_guess(){
    printf("Your guess: ");

    int guess;
    scanf("%d", &guess);

    // returns value
    return guess;
}

Úloha 1.5

Preložte svoj program a overte jeho funkcionalitu.

Pre preklad ste doteraz používali nástroj make s pomocou súboru Makefile. Odteraz budete používať prekladač GNU C Compiler - gcc s nasledovnými parametrami:

gcc -std=c11 -Wall -Werror guess.c -lm -o guess

Význam jednotlivých parametrov je nasledovný:

  • -std=c11 Budeme používať verziu jazyka C z roku 2011
  • -Wall Zaujímať nás budú všetky upozornenia (warning), pretože nechceme, aby ste písali "len" kód, ktorý funugje, ale aby ste písali "dobrý kód" a prekladač vás na nič špeciálne nemusel upozorňovať (program vie fungovať aj napriek tomu, že vás prekladač niekoľkokrát upozornil)
  • -Werror Aby ste tieto upozornenia neignorovali, táto voľba ich všetky zmení na chyby, čím nedôjde ku vytvoreniu výsledného spustiteľného programu
  • guess.c Názov súboru, ktorý má byť preložený
  • -lm Pri preklade bude prilinkovaná aj matematická knižnica
  • -o guess Názov spustiteľného výsledného súboru, ktorý vznikne prekladom

Program môžete preložiť aj použitím nástroja make, ako tomu bolo doteraz, avšak bez použitia súboru Makefile. Všeobecná syntax použitia je nasledovná:

make guess

Poznámka

Nami poskytnutý server aj VM už majú prednastavené premenné prostredia tak, aby preklad prebehol s takými prepínačmi, aké sú popísané vyššie. Avšak v prípade, že používate vlasnú distribúciu linuxu, predvolené správanie Vám zabezpečí preklad nasledovne:

cc guess.c -o guess

Aby bolo možné dosiahnuť rovnaké správanie ako použitím prekladača gcc, je potrebné nastaviť premenné prostredia CFLAGS, LDLIBS a CC v súbore ~/.bashrc:

export CFLAGS="-std=c11 -Wall -Werror"
export LDLIBS="-lm"
export CC=gcc

Aby sa Vaše nastavenia prejavili, bude potrebné sa najskôr odhlásiť a potom opäť prihlásiť. Prípadne skúste zadať príkaz exec bash.

Ak sa zmeny neprejavili, premenujte súbor ~/.bashrc na ~/.profile a opäť sa odhláste a prihláste.

Preložený program následne spustíte z adresára, v ktorom sa nachádza:

./guess

Krok 2: The Game Continues

V tomto kroku už zostáva iba vytvoriť samotnú hru.

Pravidlá hry sú veľmi jednoduché: Počítač vygeneruje náhodné číslo z určitého rozsahu, ktoré má hráč uhádnuť. Hráč sa potom pokúša tipovať, aké číslo si počítač myslí a ten mu zakaždým povie, či je jeho číslo väčšie, menšie alebo rovné s hráčovým tipom.

Cieľom nasledujúcich úloh je precvičiť si prácu s funkciami, ktoré vedia vrátiť hodnotu, prácu so štandardným vstupom a výstupom, ako aj s jednoduchými výrazmi.

Úloha 2.1

Upravte program tak, aby vygeneroval náhodné číslo vo funkcii get_secret(), načítal od hráča jeho tip a následne ho vyhodnotil správou na obrazovke.

Pre vygenerovanie náhodného čísla z intervalu <1, 100> v jazyku C je možné použiť nasledujúci fragment kódu:

srand(time(NULL));
int secret = (rand() % 100) + 1;

Avšak v prípade, že náhodné číslo chceme generovať v samostatnej funkcii get_secret() (a vrátiť ho ako návratovú hodnotu), zápis bude odlišný:

int main(){
    srand(time(NULL));

    int secret = get_secret();

    // ......
}

int get_secret(){
    return (rand() % 100) + 1;
}

Navyše, jazyk C nepozná funkcie srand(), rand() ani time(). Aby prekladač vedel, o aké funkcie sa jedná, importujte ich z hlavičkových súborov stdlib.h a time.h.

Príklad výstupu z programu je nasledovný, pričom vstup od používateľa je zvýraznený tučne a riadky začínajúce znakom $ predstavujú príkazový riadok:

$ ./guess
Pick a number between 1 and 100.
Your guess: 50
Hmm... My number is bigger than yours.
$ ./guess
Pick a number between 1 and 100.
Your guess: 50
Hmm... My number is smaller than yours.
$ ./guess
Pick a number between 1 and 100.
Your guess: 50
Congratulations! You found it!

Úloha 2.2

Upravte svoje riešenie tak, aby hráč mohol číslo hádať dovtedy, pokiaľ ho neuhádne.

Príklad výstupu z programu je nasledovný, pričom vstup od používateľa je zvýraznený tučne a riadok začínajúci znakom $ predstavuje príkazový riadok:

$ ./guess
Pick a number between 1 and 100.
Your guess: 50
Hmm... My number is bigger than yours.
Your guess: 75
Hmm... My number is bigger than yours.
Your guess: 87
Hmm... My number is smaller than yours.
Your guess: 81
Congratulations! You found it!

Úloha 2.3

Obmedzte počet pokusov, ktoré môže hráč pri hádaní použiť, na 5. Ak ich vyčerpá, hráč číslo neuhádol.

V tomto momente budú existovať dva konce - úspešný a neúspešný. Úspešné ukončenie hry je rovnaké ako uvádza príklad výstupu v predchádzajúcej úlohe. Ukážka neúspešného hádania je zobrazená nižšie, pričom vstup od používateľa je zvýraznený tučne a riadok začínajúci znakom $ predstavuje príkazový riadok:

$ ./guess
Pick a number between 1 and 100. You have max. 5 attempts.
Your guess: 50
Hmm... My number is bigger than yours.
Your guess: 75
Hmm... My number is bigger than yours.
Your guess: 87
Hmm... My number is smaller than yours.
Your guess: 81
Hmm... My number is bigger than yours.
Your guess: 84
Hmm... My number is smaller than yours.
Game over. My number was 82.

Úloha 2.4

Svoje riešenie rozšírte o zobrazenie informácie o aktuálnom čísle pokusu. Dialóg pre načítanie číselného tipu sa nachádza v samostatnej funkcii, preto je číslo pokusu potrebné odovzdať funkcii get_guess() ako vstupný parameter.

Príklad výstupu z programu je nasledovný, pričom vstup od používateľa je zvýraznený tučne a riadok začínajúci znakom $ predstavuje príkazový riadok:

$ ./guess
Pick a number between 1 and 100. You have max. 5 attempts.
Your 1. guess: 50
Hmm... My number is bigger than yours.
Your 2. guess: 75
Hmm... My number is bigger than yours.
Your 3. guess: 87
Hmm... My number is smaller than yours.
Your 4. guess: 81
Hmm... My number is bigger than yours.
Your 5. guess: 84
Hmm... My number is smaller than yours.
Game over. My number was 82.

Úloha 2.5

Rozšírte svoje riešenie tak, aby sa po skončení hádania hra opýtala, či si hráč chce zahrať znova. Ak áno, spustí hru znova. Ak nie, program sa ukončí.

Príklad výstupu z programu je nasledovný, pričom vstup od používateľa je zvýraznený tučne a riadok začínajúci znakom $ predstavuje príkazový riadok:

$ ./guess
Pick a number between 1 and 100. You have max. 5 attempts.
Your 1. guess: 50
Hmm... My number is bigger than yours.
Your 2. guess: 75
Congratulations! You found it!
Play again? (y/n) n
See you later!

Upozornenie

Pri načítavaní znaku nezabudnite vyčistiť vstupný buffer pred aj po načítavaní vstupu od používateľa. Pred načítaním z neho odstránite znak nového riadku a po načítaní z neho odstránite znaky, ktoré sú tam navyše spolu s novým riadkom (miesto jedného znaku používateľ môže zadať aj postupnosť znakov yes). Vyčistiť buffer môžete napr. takto:

while( getchar() != '\n' )
    ;

Tento kód môžete oddeliť do samostatnej funkcie clear_buffer().

Upozornenie

Nepoužívajte nekonečný cyklus!

Úloha 2.6

Rozšírte funkciu get_secret() o dva parametra a upravte výpočet pre generovanie náhodného čísla tak, aby bolo z intervalu daného parametrami funkcie: int get_secret(const int start, const int end).

Poznámka

Kľúčové slovo const znamená, že parameter je konštanta, t.j. jeho hodnota sa v priebehu vykonávania funkcie nemení.

Krok 3: Macros

V tomto kroku upravíte Váš program tak, aby používal makrá.

Úloha 3.1

Doplňte do Vášho programu 3 makrá: START, END a ATTEMPTS.

  • START Predstavuje začiatok intervalu, v rámci ktorého sa majú generovať náhodné hodnoty
  • END Predstavuje koniec intervalu, v rámci ktorého sa majú generovať náhodné hodnoty
  • ATTEMPTS Predstavuje celkový počet pokusov na hádanie v rámci jednej hry

Úloha 3.2

Upravte Váš program tak, aby používal vytvorené makrá.

Doplňujúce úlohy

Úloha A.1

Vytvorte program, pomocou ktorého načítate zo vstupu postupnosť čísel, pričom načítavanie ukončíte zadaním hodnoty 0. Na obrazovku vypíšte najväčšie zadané číslo, najmenšie zadané číslo, súčet všetkých zadaných čísiel a ich aritmetický priemer.

Úloha A.2

Vytvorte program, ktorý zo vstupu načíta hodnotu N (celé kladné číslo) a na jeho základe vypíše na obrazovku Pascalov trojuholník.

Úloha A.3

Upravte program guess tak, aby pri výzve na zadanie tipu vedel hráč zadať len čísla z uvedeného rozsahu. Ak zadá nesprávny vstup, hra ho vyzve o opätovné zadanie.

Úloha A.4

Vytvorte nový program guess_my_number tak, aby počítač hádal číslo, ktoré si myslíte Vy ako hráč a Vy mu pomocou odpovedí (moje je väčšie/menšie/rovné) budete pomáhať. Pre realizovanie programu použite metódu Bisekcie (Bisection search, metóda delenia intervalov).

Príklad výstupu komunikácie z programu je nasledovný, pričom vstup od používateľa je zvýraznený tučne:

$ ./guess_my_number
Please think of a number between 0 and 100!
Is your secret number 50?
Enter 'h' to indicate the guess is too high. Enter 'l' to indicate the guess is too low. Enter 'c' to indicate I guessed correctly. l
Is your secret number 75?
Enter 'h' to indicate the guess is too high. Enter 'l' to indicate the guess is too low. Enter 'c' to indicate I guessed correctly. l
Is your secret number 87?
Enter 'h' to indicate the guess is too high. Enter 'l' to indicate the guess is too low. Enter 'c' to indicate I guessed correctly. h
Is your secret number 81?
Enter 'h' to indicate the guess is too high. Enter 'l' to indicate the guess is too low. Enter 'c' to indicate I guessed correctly. l
Is your secret number 84?
Enter 'h' to indicate the guess is too high. Enter 'l' to indicate the guess is too low. Enter 'c' to indicate I guessed correctly. h
Is your secret number 82?
Enter 'h' to indicate the guess is too high. Enter 'l' to indicate the guess is too low. Enter 'c' to indicate I guessed correctly. l
Is your secret number 83?
Enter 'h' to indicate the guess is too high. Enter 'l' to indicate the guess is too low. Enter 'c' to indicate I guessed correctly. c
Game over. Your secret number was: 83

Úloha A.5

Modifikujte program guess_my_number nasledovne: Nech A si myslí číslo a B háda myslené číslo. Používateľ nech zadá interval, na ktorom je číslo hádané. Výstupom nech je konverzácia medzi A a B. Počítač teda bude komunikovať sám so sebou.

Úloha A.6

Zopakujte si prácu s generátorom náhodných čísel (Úloha 2.1) a naprogramujte počítadlo pre "Produkt si práve pozerá" podľa vzoru skúsených programátorov z internetu (viď obrázok). Kreativite sa medze nekladú.

Skúsenosti z internetu: Produkt si práve pozerá X zákazníkov
Obr. 1: Skúsenosti z internetu: Produkt si práve pozerá X zákazníkov

Doplňujúce zdroje

  1. Pavel Herout: Učebnice jazyka C (1. díl) - kapitola 5.1, 5.4 a 5.5, 9.2
  2. Funkcia printf(): c-reference, cplusplus.com - Loads the data from the given locations, converts them to character string equivalents and writes the results to stdout.
  3. Funkcia scanf(): c-reference, cplusplus.com - Reads data from stdin, interprets it according to format and stores the results into given locations.
  4. Funkcia getchar(): c-reference, cplusplus.com - Reads the next character from stdin.
  5. Funkcia srand(): c-reference, cplusplus.com - Seeds the pseudo-random number generator used by rand() with the value seed.
  6. Funkcia rand(): c-reference, cplusplus.com - Returns a pseudo-random integral value between ​0​ and RAND_MAX (0 and RAND_MAX included).
  7. Funkcia time(): c-reference, cplusplus.com - Returns the current calendar time encoded as a time_t object.
  8. Clearing the input buffer in C

Video