Obsah lekce:
Na konci minulé lekce jsme měli za úkol vyřešit zamíchání kartiček. Nyní si shrneme realizaci tohoto úkolu.
Do pole pexesa máme načtené hodnoty obrázků. Ty jsou však seřazeny v pořadí, v jakém jsme pole plnili hodnotami (záleží na variantě, kterou jsme si vybrali v předchozí lekci). Tento stav ovšem není ideální jako počáteční stav hry pexeso. Potřebujeme, aby kartičky byly seřazeny náhodně.
Pro náhodné seřazení kartiček budeme potřebovat funkci, která nám vygeneruje náhodné číslo. V jazyce C pro tento účel souží funkce rand(). Tuto funkci najdeme deklarovanou v hlavičkovém souboru stdlib.h, takže pro správnou funkčnost programu budeme muset přidat řádek #include <stdlib.h>.
Aby generátor náhodných čísel generoval pokaždé jiná náhodná čísla, je nutné tento generátor inicializovat. Pro inicializaci generátoru poslouží funkce srand, kterou na začátku funkce main zavoláme ve tvaru srand((unsigned int) time(NULL)) a protože při této inicializaci využijeme i časovou funkci, musíme přidat ještě jeden hlavičkový soubor - soubor time.h.
#include "stdafx.h" #include <stdlib.h> #include <time.h> int _tmain(int argc, _TCHAR* argv[]) { int i; srand((unsigned int) time(NULL)); for (int i = 0; i < 10; i++) { printf("%d",rand()); } fflush(stdin); getchar(); return 0; }
Když výše uvedený program spustíme, zjistíme, že program generuje čísla z celého rozsahu datového typu int, což je pro naše použití problém. My potřebujeme čísla pouze z určitého intervalu. Pro tento účel použijeme operátor %, kterým získáme zbytek po celočíselném dělení.
#include "stdafx.h" #include <stdlib.h> #include <time.h> int _tmain(int argc, _TCHAR* argv[]) { int i, cislo; srand((unsigned int) time(NULL)); for (int i = 0; i < 10; i++) { cislo = rand()%10; // vygeneruje číslo od 0 do 9 printf("%d", cislo); } fflush(stdin); getchar(); return 0; }
Nyní je třeba generování náhodného čísla aplikovat na naše pole s pexesem. Budeme postupovat tak, že opět pomocí dvou vnořených cyklů projdeme celé pole pexesa a pro každou kartičku vygenerujeme novou pozici (dvě náhodné čísla udávající nový řádek a nový sloupec). Poté prohodíme hodnotu obrázku na aktuální pozici s hodnotou obrázku na pozici nové.
Celý postup, který jsme si nyní popsali si uložíme do funkce, kterou pojmenujeme zamichanipexesa.
Nyní k samotnému postupu. Nejprve si vytvoříme funkci pro zamíchání pexesa s lokálními proměnnými radek, sloupec a r, s. První dvojice těchto proměnných nám bude sloužit pro vytvoření cyklu pro průchod pole s pexesem. Ta druhá bude sloužit pro vygenerování dvojice čísel, která nám bude udávat novou pozici kartičky.
void zamichanipexesa() { int radek, sloupec; int r, s; }
Následně naši proceduru doplníme o již zmiňovaný cyklus, který nám projde postupně všechny kartičky pexesa. Jedná se o podobný cyklus, se kterým jsme se setkali u inicializace pexesa a u zobrazení hodnoty obrázku.
void zamichanipexesa() { int radek, sloupec; int r, s; for (radek = 0; radek < n; radek++) { for (sloupec = 0; sloupec < n; sloupec++) { } } }
Do cyklu, který máme vytvořen, nyní doplníme 2 řádky, které nám umožní vygenerovat dvojici náhodných čísel od 0 do n-1. Ta nám bude sloužit pro určení nové pozice kartičky. Pro ověření funkce našeho programu si vygenerovanou dvojici hodnot zatím pouze vypíšeme. (pozn. aby nám tato procedura fungovala, musíme její volání doplnit do hlavní části našeho programu, za volání inicializace pexesa.)
void zamichanipexesa() { int radek, sloupec; int r, s; for (radek = 0; radek < n; radek++) { for (sloupec = 0; sloupec < n; sloupec++) { r = rand()%n; s = rand()%n; printf("%d %d\n", r, s); } } }
Nyní již zbývá jen prohodit aktuální obrázek s obrázkem na pozici, která byla vygenerována pomocí funkce random. Jakým způsobem zaměnit dvě hodnoty?
Pro záměnu dvou hodnot budeme potřebovat pomocnou proměnnou. Například záměna čísel a a b by vypadala následovně:
pom = a; a = b; b = pom;
Tento postup nyní implementujeme do naší funkce (tučně jsou zobrazeny doplněné řádky):
void zamichanipexesa() { int radek, sloupec; int r, s; int pom; for (radek = 0; radek < n; radek++) { for (sloupec = 0; sloupec < n; sloupec++) { r = rand()%n; s = rand()%n; pom = p[radek][sloupec].obrazek; p[radek][sloupec].obrazek = p[r][s].obrazek; p[r][s].obrazek = pom; } } }
Nyní se již kartičky pexesa zobrazí v náhodném uspořádání. Výstup programu může vypadat podobně jako na níže uvedeném obrázku.
Nyí již máme kartičky pexesa zamíchány a zobrazeny. Abychom si správně zobrazovali průběh hry, je třeba změnit způsob zobrazování kartiček pexesa dle toho, zda jsou otočené a zda jsou odebrané.
Nejprve ošetřeme stavy, zda je kartička otočená nebo ne. V případě, že je kartička otočená, je třeba zobrazit hodnotu obrazek, v opačném případě hodnotu 0.
Vytvoříme si novou funkci pro zobrazení pexesa. Tato funkce bude obsahovat stejnou konstrukci kódu, jako zobrazení pexesa v hlavní části programu. Poté si ji upravíme, tak aby zobrazovala aktuální situaci hry (původní kód zobrazení čísla obrázku však v hlavní části programu ponecháme, alespoň po dobu vývoje a testování našeho programu).
Vytvořte funkci zobrazenipexesa, která zobrazí hodnotu obrazek z našeho pole a volání této funkce umístěte před konec programu (před řádek fflush(stdin);)
void zobrazenipexesa() { int radek, sloupec; for (radek = 0; radek < n; radek++) { for (sloupec = 0; sloupec < n; sloupec++) { printf("%d ", p[radek][sloupec].obrazek); } printf("\n"); } }
Nyní máme proceduru pro zobrazení pexesa, která vznikla osamostatněním zobrazení pexesa z hlavní části našeho programu. Dále tuto proceduru rozšíříme o podmínku, která bude zjišťovat zda je kartička viditelná nebo ne.
void zobrazenipexesa() { int radek, sloupec; for (radek = 0; radek < n; radek++) { for (sloupec = 0; sloupec < n; sloupec++) { if (p[radek][sloupec].viditelna == 1) { printf("%d ", p[radek][sloupec].obrazek); } else { printf("0 "); } } printf("\n"); } }
Nyní nám v programu přibyla podmínka if ... else, která porovnává hodnotu viditelna (if (p[radek][sloupec].viditelna == 1)).
V případě, že viditelna obsahuje hodnotu 1, vypíše se hodnota obrazek, jinak se vypíše hodnota 0.
Dále si program rozšíříme o nezobrazování odebraných kartiček. V případě, že byla kartička odebrána, zobrazí se znak X, jinak se provede podmínka, kterou jsme si definovali v předchozím kroku.
void zobrazenipexesa() { int radek, sloupec; for (radek = 0; radek < n; radek++) { for (sloupec = 0; sloupec < n; sloupec++) { if (p[radek][sloupec].odebrana == 1) { printf("X "); } else { if (p[radek][sloupec].viditelna == 1) { printf("%d ", p[radek][sloupec].obrazek); } else { printf("0 "); } } } printf("\n"); } }
Výsledný výpis této procedury bude vypadat následovně:
Nyní již máme definované zobrazení pexesa tak, aby se správně zobrazily kartičky dle toho, zda jsou otočené nebo odebrané. Dále si vytvoříme proceduru, která bude vyžadovat vstup od uživatele, tj. otočení kartičky.
Narozdíl od předchozích funkcí bude tato procedura mít vstupní parametry. Vstupními parametry budou souřadnice kartičky (řádek a sloupec), která má být otočena.
void otockarticku(int radek, sloupec); { p[radek][sloupec].viditelna = 1; }
Funkce pro otočení kartičky zatím vypadá celkem jednoduše. Pouze načteme dva parametry, které udávají číslo řádku a sloupce a následně odpovídající kartičce pexesa nastavíme hodnotu viditelna na 1. Funkci poté budeme volat například následovně: otockarticku(r, s);
.Abychom mohli tuto funkci vyzkoušet, otočíme si dvě kartičky a znovu si zobrazíme naše pexeso. Pro provedení níže uvedeného kódu budeme potřebovat vytvořit 4 globální proměnné typu byte: radek1, sloupec1, radek2 a sloupec2.
... printf("Zadejte souradnice 1. karticky (radek): "); scanf("%d", radek1); printf("Zadejte souradnice 1. karticky (sloupec): "); scanf("%d", sloupec1); printf("Zadejte souradnice 2. karticky (radek): "); scanf("%d", radek2); printf("Zadejte souradnice 2. karticky (sloupec): "); scanf("%d", sloupec2); otockarticku(&p, radek1,sloupec1); otockarticku(&p, radek2,sloupec2); zobrazenipexesa(); ...
Pro úplnost je vhodné ještě uvést kompletní kód, ke kterému jsme se dostali na konci dnešní lekce.
#include "stdafx.h" #define n 6 #define maxhracu 4 typedef struct { int obrazek; int viditelna; int odebrana; } karticka; typedef struct { char jmeno[255]; int skore; } hrac; karticka p[n][n]; hrac ph[maxhracu]; void inicializacepexesa() { int radek, sloupec, pocet; pocet = 1; for (radek = 0; radek < n; radek++) { for (sloupec = 0; sloupec < n; sloupec++) { pocet = pocet + 1; p[radek][sloupec].obrazek = pocet % 2; p[radek][sloupec].viditelna = 0; p[radek][sloupec].odebrana = 0; } } } void zamichanipexesa() { int radek, sloupec; int r, s; int pom; for (radek = 0; radek < n; radek++) { for (sloupec = 0; sloupec < n; sloupec++) { r = rand()%n; s = rand()%n; pom = p[radek][sloupec].obrazek; p[radek][sloupec].obrazek = p[r][s].obrazek; p[r][s].obrazek = pom; } } } void zobrazenipexesa() { int radek, sloupec; for (radek = 0; radek < n; radek++) { for (sloupec = 0; sloupec < n; sloupec++) { if (p[radek][sloupec].odebrana == 1) { printf("X "); } else { if (p[radek][sloupec].viditelna == 1) { printf("%d ", p[radek][sloupec].obrazek); } else { printf("0 "); } } } printf("\n"); } } void otockarticku(int radek, sloupec); { p[radek][sloupec].viditelna = 1; } int _tmain(int argc, _TCHAR* argv[]) { int radek, sloupec, pocet; inicializacepexesa(); zamichanipexesa() for (radek = 0; radek < n; radek++) { for (sloupec = 0; sloupec < n; sloupec++) { printf("%d ", p[radek][sloupec].obrazek); } printf("\n"); } printf("Zadejte souradnice 1. karticky (radek): "); scanf("%d", radek1); printf("Zadejte souradnice 1. karticky (sloupec): "); scanf("%d", sloupec1); printf("Zadejte souradnice 2. karticky (radek): "); scanf("%d", radek2); printf("Zadejte souradnice 2. karticky (sloupec): "); scanf("%d", sloupec2); otockarticku(radek1,sloupec1); otockarticku(radek2,sloupec2); zobrazenipexesa(); fflush(stdin); getchar(); return 0; }
Jakým způsobem je nutné ošetřit funkci pro otočení kartičky, aby nedocházelo k chybovým stavům (podmínkou je otočit vždy dvě různé kartičky z našeho pexesa)?. Navrhněte úpravu této funkce.