HW02: Texas Hold'em Poker

Odevzdávání úkolu je ukončeno.
Autor zadání David Klaška
Úprava Patrick Ondika
Odevzdávané soubory main.c
Začátek odevzdávání viz diskusní fórum
Další odevzdání navíc do 2025-03-19 24:00
Konec odevzdání 2025-03-26 24:00
Vzorová implementace /home/kontr/pb071/hw02/poker
Opravy v zadání 2025-03-24 18:27

Představení úkolu

V této úloze se budeme věnovat světově nejpopulárnější karetní hře — pokeru. Tato hra je známá ve spoustě variant. My se zde budeme zabývat tou patrně nejrozšířenější, Texas Hold’em Poker, kdy každý hráč drží v ruce dvě karty a na stůl je rozdáno (až) pět karet společných pro všechny hráče.

Zadání

Vaším úkolem bude napsat program, který bude komunikovat s uživatelem prostřednictvím příkazové řádky. Váš program bude samozřejmě muset umět porovnávat mezi sebou jednotlivé karetní kombinace.

Jádrem problému bude algoritmus rozhodující, který ze dvou hráčů vyhrál, bude-li znát obě karty obou hráčů i všech pět karet ležících na stole.

Pravidla hry

Podrobnější popis pravidel naleznete např. na Wikipedii v článku Texas Hold’em Poker nebo seznamu karetních kombinací pokru.
  • Každý hráč má k dispozici dvě soukromé karty a pět společných karet, jež jsou vyloženy na stole. Z těchto sedmi karet sestavuje nejsilnější možnou kombinaci pěti karet. Nejsilnější pětikaretní kombinace vyhrává.

  • Nejvyšší kartou je A (výjimkou je postupka, kde ji lze použít jako kartu s hodnotou 1) — následují K, Q, J, 10, 9, 8, 7, 6, 5, 4, 3, 2.

  • Všechny barvy karet jsou rovnocenné.

Výherní kombinace od nejvyšší po nejnižší jsou následující:

  • Straight flush je čistá postupka (všechny karty mají stejnou barvu, např. 7 6 5 4 3). V případě, že mají oba hráči straight flush, vítěz je ten, kdo má tuto postupku zakončenou vyšší kartou. V případě shody dochází k remíze.

    A se může v čisté postupce vyskytovat jako karta nejvyšší (takové postupce se říká royal straight flush), nebo jako karta nejnižší (takové postupce se říká steel wheel).

  • Four of a kind (poker) je čtveřice, tzn. čtyři karty stejné hodnoty (např. 5 5 5 5). V případě, že mají oba hráči four of a kind, rozhoduje o vítězi hodnota čtveřice. V případě shody rozhoduje hodnota páté (doplňující) karty. Shoduje-li se i hodnota doplňující karty, dochází k remíze.

  • Full house je trojice a dvojice (např. 7 7 7 10 10). V případě, že mají oba hráči full house, rozhoduje o vítězi hodnota trojice. V případě shody rozhoduje hodnota dvojice. Shoduje-li se i hodnota dvojice, dochází k remíze.

  • Flush je pět karet libovolné hodnoty, ale stejné barvy (např. A 10 8 7 2). V případě, že mají oba hráči flush, rozhoduje nejvyšší karta. Shoduje-li se hodnota nejvyšší karty, rozhoduje druhá nejvyšší karta atd. Mají-li oba hráči pětice se stejnými hodnotami (seřazenými od nejvyšší karty po nejnižší), dochází k remíze.

  • Straight je špinavá postupka (např. 9 8 7 6 5). V případě, že mají oba hráči straight, vítěz je ten, kdo má tuto postupku zakončenou vyšší kartou. V případě shody dochází k remíze.

    Stejně jako v čisté postupce, i zde se A může vyskytovat jako karta nejvyšší (takové postupce se říká broadway straight), nebo jako karta nejnižší (takové postupce se říká baby straight).

  • Three of a kind je trojice (např. K K K). V případě, že mají oba hráči three of a kind, rozhoduje o vítězi hodnota trojice. V případě shody rozhoduje hodnota vyšší ze dvou doplňujících karet. Shodují-li se hodnoty vyšších doplňujících karet, rozhodují ty nižší. Shodují-li se i ty, dochází k remíze.

  • Two pair jsou dvě dvojice (např. 4 4 2 2). V případě, že mají oba hráči two pair, rozhoduje o vítězi hodnota vyšší dvojice. V případě shody rozhoduje hodnota nižší dvojice. Shodují-li se i hodnoty nižších dvojic, rozhodují hodnoty pátých (doplňujících) karet. Shodují-li se i ty, dochází k remíze.

  • Pair je dvojice (např. 5 5). V případě, že mají oba hráči pair, rozhoduje o vítězi hodnota dvojice. V případě shody rozhoduje nejvyšší ze tří doplňujících karet. Shoduje-li se nejvyšší z doplňujících karet, rozhoduje druhá nejvyšší. Pokud se shoduje i ta nejnižší, dochází k remíze.

  • High card je vysoká karta. Pokud ani jeden z hráčů nemá lepší kombinaci, rozhoduje o vítězi nejvyšší z pěti karet. V případě shody rozhoduje druhá nejvyšší atd. Mají-li oba hráči pětice se stejnými hodnotami (seřazenými od nejvyšší karty po nejnižší), dochází k remíze.

Formát vstupu a výstupu

Karty budou reprezentovány dvojicí znaků:

  • první z nich určuje hodnotu; 2, 3, 4, 5, 6, 7, 8, 9, T, J, Q, K, A pro po řadě 2, 3, …​, 10, J, Q, K, A,

  • druhý barvu; h, d, s, c pro po řadě (srdce, hearts), (káry, diamonds), (piky, spades), (kříže, clubs).

Vstup bude sestávat z několika po sobě jdoucích instancí (situací), výstup pak z odpovědí na jednotlivé instance. Program tedy neskončí hned po vyřešení jediné instance, ale až když se na vstupu nenachází žádná další instance (End of File).

Porovnávání dvou hráčů

Vstupní instance budou sestávat ze tří řádků:

  • na prvním budou dvě karty jednoho hráče,

  • na druhém dvě karty druhého hráče,

  • na třetím pět karet ležících na stole

Váš program odpoví pro každou instanci tím, který hráč vyhrál, tedy:

  • Player 1, pokud vyhrál první hráč,

  • Player 2, pokud vyhrál druhý,

  • Draw, pokud nastala remíza.

Karty jsou na jednom řádku oddělené mezerou. Každý řádek na výstupu je ukončen znakem pro nový řádek.

Korektnost vstupu

Program musí ověřit, že formát vstupu odpovídá popisu výše. Pro jednoduchost nemusíte vyžadovat přesně jednu mezeru, stačí ověřit následující podmínky:

  • Karta má správný tvar a je v aktuální instanci jedinečná.

  • Mezi kartami na řádku je alespoň jeden bílý znak (nemusí být nutně mezera).

  • Na řádku se nachází očekávaný počet karet a pak znak \n.

  • Instance je kompletní, tj. pokud se povede načíst první řádek, pak musí být na vstupu i zbylé řádky instance.

Pokud některá podmínka neplatí, vypište nějakou smysluplnou zprávu ukončenou znakem \n na standardní chybový výstup voláním funkce fprintf() s prvním argumentem stderr. Tato zpráva musí být pouze na jediném řádku, tzn. pokud vypisujete část vstupu, dejte si pozor, aby program nevypsal další '\n' uvnitř zprávy. Další instance se po chybě zpracovávat nesmí, program ukončete s libovolným návratovým kódem.

Příklad použití funkce fprintf():

if (!card_is_valid(card))
    fprintf(stderr, "%s: invalid card\n", card);

Tato funkce se chová jako printf(FORMAT, …​), ale výstup se objeví na chybovém výstupu. Text zpráv je na vás, ale měly by dávat smysl a musí být jasné, proč došlo k chybě. Například error není vhodná zpráva.

Kompilace

Některé testy naostro zkouší velký počet instancí (až 2¹⁸). Proto se tyto testy kompilují s přepínačem -O2, který zapne některé optimalizace.

Je však nezbytné vyzkoušet si kompilaci řešení na Aise i s tímto přepínačem. Kompilátor totiž může při optimalizaci kódu odhalit problémy, pro které vygeneruje další varování.

# Nejdřív vyzkoušejte kompilaci běžným způsobem
login@aisa:~$ gcc -std=c99 -pedantic -Wall -Wextra -Werror -o poker main.c

# Pak vyzkoušejte kompilaci s -O2
login@aisa:~$ gcc -O2 -std=c99 -pedantic -Wall -Wextra -Werror -o poker main.c

Ukázkové příklady

Pro vstup:

3d 3h
Ac Ts
Qd 8s 2c 4c Kh
4s 8d
5c 8h
7c 7h Ac Kd 2d
6s Kh
Ah 3c
Ac 4h 5h Ks Jh

je očekávaným výstupem

Player 1
Draw
Player 2

Komentář:

  • V prvním případě má první hráč dvojici trojek (3 3), a druhý žádnou dvojici, tedy nezáleží na hodnotě jeho karet.

  • V druhém případě první hráč hraje 7 7 A K 8, druhý hráč hraje 7 7 A K 8 (porovnává se vždycky pouze pět nejlepších karet, v tomto případě pár a tři zbylé nejvyšší karty). Pár 7 7 je pro oba hráče společný, a zbytek karet má stejnou hodnotu, proto dochází k remíze.

To, že jeden drží 5, zatímco druhý 4, je již nepodstatné. * Ve třetím případě má první hráč dvojici (K K), ale druhý má vyšší dvojici (A A).

Bonusová rozšíření

Rozšíření na více hráčů [10 bodů]

Upravte implementaci tak, aby fungovala pro 2 až 8 hráčů.

Počet hráčů pro danou instanci dostane program na příkazové řádce. Způsob jak správně přečíst argument najdete v přiložené kostře, ošetřit jeho korektnost je na vás. Formát vstupu zůstává stejný; program čeká na 2 karty pro každého hráče, poté 5 karet na stole, a při chybě vypíše vhodnou chybovou hlášku.

Výstup programu také zůstává stejný. Program vypíše hráče s nejsilnější kombinací, a pokud výherní kombinaci má více hráčů, výstupem bude

Draw

Tipy a poznámky

  • Pro případ, že byste měli nejasnosti týkající se formátu vstupu a výstupu nebo srovnávání jednotlivých karetních kombinací, můžete srovnat chování vašeho programu se vzorovým řešením, které je vám k dispozici na Aise na /home/kontr/pb071/hw02/poker.

    Vzorová implementace s přepínačem -e (explain) navíc vysvětlí na výstupu, který hráč má jakou kombinaci.

  • Výstup vašeho programu bude testován strojově, nesmí se tedy lišit ani v jediném znaku.

  • K otestování korektnosti vstupu stačí vhodně použít funkci scanf(). Doporučujeme si přečíst dokumentaci.

  • Časová efektivita vašeho programu je také důležitá, proto si dávejte pozor, aby váš program nebyl zbytečně pomalý.

Opravy v zadání

Fix solution path in Tips and Notes

2d69a7b 2025-03-24 18:27 Roman Lacko
 -257,3 +257,3  Draw
   chování vašeho programu se vzorovým řešením, které je vám k dispozici
-  na Aise na `{page.solution-path}`.
+  na Aise na `{{ page.solution-path }}`.
 +