Witaj, Gościu O nas | Kontakt | Mapa
Wortal Forum PHPEdia.pl Planeta Kubek IRC Przetestuj się!
Spis treści

Spis treści

O autorze

O autorze

Sławomir Lis
Reklama

Reklama

Algorytmy do gry Poker

Reprezentacja kart w pamięci komputera

0

1

2

3

4

5

6

7

i tak dalej ...

Taki sposób numerowania kart powoduje, że łatwo możemy napisać funkcje zwracające nam kolor i figurę karty o podanym numerze porządkowym. Na przykład kolor trefl występuje w kartach o numerach: 0,4,8,12,16,20 (zakładając, że gramy od dziewiątek czyli 24 kartami). Zauważ, że te liczby są zawsze podzielne przez 4. Czyli:

Oczywiście zasada ta obowiązuje także jeśli używany więcej niż 24 kart.

Czyli:

Jeśli nazwy kart i kolorów zdefiniujemy następująco

DEFINE ('TREFL',     0);
DEFINE ('PIK',       1);
DEFINE ('KARO',      2);
DEFINE ('SERCE',     3);
DEFINE ('DZIEWIATKA',0);
DEFINE ('DZIESIATKA',1);
DEFINE ('WALET',     2);
DEFINE ('DAMA',      3);
DEFINE ('KROL',      4);
DEFINE ('AS',        5);

Wtedy moglibyśmy napisać takie funkcje:

function JakiKolor($karta) {
    return $karta % 4;
}
function JakaFigura($karta){
    return floor($karta/4);
}

Zostało nam jeszcze napisać funkcję odwrotną do obydwu powyższych funkcji, to znaczy taką, która zwróci numer karty (liczbę) na podstawie koloru i figury.

function JakaKarta($figura,$kolor){
    return $figura * 4 + $kolor;
}

Tasowanie i rozdanie kart

Karty będziemy przechowywać w tablicach. Zdefiniujemy tablicę symbolizująca talię kart (czyli liczby całkowite, które je symbolizują) oraz tablice, w których będziemy przechowywać karty poszczególnych graczy (w tym komputera, który także może być graczem). Jeśli gramy talią składającą się z 24 kart (od dziewiątek do asów), to będziemy potrzebowali tablicy 24-elementowej. Na początku gry musimy zainicjować tablicę talii kart, to znaczy przydzielić wartości wszystkim elementom tablicy.

for ($i=0;$i<=23;$i++) $talia[$i] = $i;
for ($i=0;$i<=23;$i++){
    $los         = rand(0,23);
    $element1    = $talia[$i];
    $element2    = $talia[$los];
    $talia[$i]   = $element2;
    $talia[$los] = $element1;
}

W tej chwili karty już są potasowane i możemy je rozdać. Czyli ustawić wartości w tablicach symbolizujących karty graczy. Każdy gracz dostaje po 5 kart, tak więc będziemy potrzebowali przynajmniej dwóch tablic 5-elementowych.

$_SESSION['wolna'] = 0;

for($i=0;i<=$4;$i++){
    gracz1[$i] = $talia[$i];
    $gracz2[$i] = $talia[$i+5];
    // ... i tak dalej (jeśli jest graczy więcej niż dwóch)
}

zmienna $_SESSION['wolna'] służy do oznaczenia ostatniej rozdanej karty z tali, żebyśmy mogli wiedzieć od jakiej karty możemy później pobierać karty z tali podczas wymiany. Użyta tu zmienna powinna być widoczna w całym kodzie programu, dlatego do jej przechowywania używamy sesji.

Sprawdzenie wyniku gracza

To mamy zarówno rowerek (rowerek jest zawsze bo jest najgorszy), parę, trójkę oraz fulla. Jako, ze full jest najmocniejszy ustalamy, że mamy właśnie fulla. Przykładowy kod mógłby wyglądać tak:

$wynik = ROWEREK;
for ($i=JEDNA_PARA; $i<=DUZY_POKER; $i++){
    if ($tablica_wynikow[$i]==true){
        $wynik = $i;
    }
}

Gdzie tablica wyników zawiera elementy typu boolean (true lub false), może to być też 0 i 1, tak czy inaczej true (1) oznacza, że rozdanie spełnia wynik. Na przykład jeśli $tablica_wyników[JEDNA_PARA]==true to znaczy, że rozdanie zawiera jedną parę, co wcale nie wyklucza, że istnieje jeszcze trójka, co oznacza także, że $tablica_wynikow[TROJKA]==true.

No tak, ale zanim będziemy mogli skorzystać z tablicy wyników, musimy ją najpierw wypełnić, czyli musimy napisać odpowiednie funkcje, które odpowiedzą nam na pytanie czy jest spełniony odpowiedni wynik. Tych funkcji będzie 11 czyli tyle ile możliwych wyników.

function Rowerek($rozdanie);
function Jedna_Para ($rozdanie);
function Dwie_Pary  ($rozdanie);
function Trojka ($rozdanie);
function Maly_Strit ($rozdanie);
function Duzy_Strit ($rozdanie);
function Full   ($rozdanie);
function Kareta ($rozdanie);
function Kolor  ($rozdanie);
function Maly_Poker ($rozdanie);
function Duzy_Poker ($rozdanie);

Każda z tych funkcji przyjmuje jeden argument w postaci tablicy przedstawiającej rozdanie (5 kart), a zwraca wartość true lub false (1 lub 0) w zależności od tego czy rozdanie spełnia wynik. Właściwie pierwszej funkcji Rowerek() wcale nie musimy pisać, bo z założenia zawsze zwraca true i nawet w pętli szukającej najlepszego rozwiązania indeks zerowy został pominięty, a przed rozpoczęciem pętli wynik ustawiony na zero. Jeśli jednak chcielibyśmy koniecznie napisać tą funkcję to wyglądała by ona tak

function Rowerek($rozdanie){
    return true;
}

Tak więc w rzeczywistości nawet nie potrzebujemy używać argumentu $rozdanie, bo z góry zakładamy, że rozdanie jest rowerkiem. Teraz bierzemy się za pozostałe 10 funkcji:

function Ilosc_Figur($rozdanie){
    $tablica_figur = Array(0,0,0,0,0,0);
    for ($i=DZIEWIATKA;$i<=AS;$i++){
        $tablica_figur[Jaka_Figura($rozdanie[$i])]++;
    }
    return $tablica_figur;
}

Pętla for sprawdza po kolei wszystkie karty w rozdaniu, to znaczy pobiera numer karty $rozdanie[$i], na tej podstawie dowiadujemy się z jaką figurą mamy do czynienia. Jest to możliwe dzięki napisanej w pierwszym rozdziale prostej funkcji Jaka_Figura(). Następnie w tablicy figur, którą powinniśmy na początku wyzerować, zwiększmy wartość elementu o indeksie odpowiadającym numerowi porządkowemu figury. Suma wszystkich elementów tablicy figur zawsze będzie równa 5, bo tyle kart jest w rozdaniu.

Wróćmy teraz do pisania naszej funkcji Jedna_Para(). Skorzysta ona z tablicy figur. Jeśli znajdzie w niej wartość równą 2 to znaczy, że w rozdaniu trafiły się dwie karty o tej samej figurze.

function Jedna_Para($rozdanie){
    return in_array( 2, Ilosc_Figur($rozdanie) );
}

Funkcja Dwie_Pary() będzie działać na podobnej zasadzie. Różnica będzie polegać na tym, że zamiast sprawdzać czy w ogóle istnieje w tablicy figur element o wartości 2 to będziemy liczyć ilość elementów o wartości 2. Muszą wystąpić dwie takie dwójki, zresztą więcej dwójek być nie może bo jeśli byśmy mieli już 3 dwójki oznaczało by to że mamy przynajmniej 6 kart, a przecież w rozdaniu mamy 5 kart.

function Dwie_Pary($rozdanie){
    $ilePar = 0;
    $figury = Ilosc_Figur($rozdanie);
    for ($i=DZIEWIATKA;$i<=AS;$i++){
        if ($figury[$i]==2){
            $ilePar++;
        }
    }
    return ($ilePar==2);
}

Funkcja Trojka() jest prawie identyczna do funkcji Jedna_Para z tym, że szukamy w tablicy figur wartości 3 a nie 2.

function Trojka($rozdanie){
    return in_array(3,Ilosc_Figur($rozdanie))
}
function Maly_Strit($rozdanie){
    $wynik = true;
    $figury = Ilosc_Figur($rozdanie);
    for (i=DZIEWIATKA;i<=KROL;i++){
        if ($figury[$i]!=1){
            $wynik = false;
        }
    }
    return wynik;
}
function Duzy_Strit($rozdanie){
    $wynik = true;
    $figury = Ilosc_Figur($rozdanie);
    for (i=DZIESIATKA;i<=AS;i++){
        if ($figury[$i]!=1){
            $wynik = false;
        }
    }
    return $wynik;
}

Napisanie funkcji Full() jest jeszcze łatwiejsze, bo wystarczy sprawdzić czy posiadamy jedną parę i trójkę. Skorzystamy oczywiście ze wcześniej napisanych funkcji.

function Full($rozdanie){
    return  (Para($rozdanie) and Trojka($rozdanie));
}

Funkcja Kareta() - 4 takie same figury - jest funkcją analogiczną do funkcji Jedna_Para() oraz Trojka().

function Kareta($rozdanie){
    return in_array(4,Ilosc_Figur($rozdanie));
}

Następną funkcją do zaprogramowania będzie Kolor(). Tym razem będziemy sprawdzali nie figury, ale kolory. Wszystkie pięć kart powinny być tego samego koloru, a więc na początku sprawdzamy kolor pierwszej karty z talii, a następnie w pętli sprawdzamy, czy każda kolejna karta jest tego samego koloru co pierwsza, jeśli nie to znaczy, że koloru nie ma.

function Kolor($rozdanie){
    $wynik = true;
    $pierwszy = JakiKolor($rozdanie[0]);
    for ($i=1;$i<=4;$i++){
        if (JakiKolor($rozdanie[$i])!=$pierwszy){
            $wynik = false;
        }
    }
    return $wynik;
}

Funkcje Maly_Poker() oraz Duzy_Poker będą już bardzo łatwe do napisania, bo podobnie jak funkcja Full() korzystają już z istniejących funkcji. Małego pokera mamy gdy posiadamy małego strita w kolorze. Dużego pokera mamy gdy posiadamy dużego strita w kolorze. A więc:

function Maly_Poker($rozdanie){
    return (Maly_Strit($rozdanie) and Kolor($rozdanie));
}

function Duzy_Poker(rozdanie){
    return (Duzy_Strit($rozdanie) and Kolor($rozdanie));
}

To już wszystkie potrzebne nam funkcje. Dzięki nim wiemy co nam wyszło, jaki mamy wynik rozdania. Jak widać żadna z funkcji nie zajmuje więcej niż kilka linijek. Funkcje korzystają z siebie nawzajem oraz z jednej dodatkowej pomocniczej funkcji Ilosc_Figur().

Automatyczna wymiana kart

Często w grze bierze udział komputer, dlatego trzeba nauczyć go inteligentnie, nieprzypadkowo wymieniać karty. Musimy rozważyć oczywiście wszystkie przypadki rozdania, to znaczy, będziemy inaczej postępowali, gdy przed wymianą trafi nam się rowerek, a inaczej gdy trafi nam się para lub dwie itd. Oczywiście jeśli trafi nam się Poker, Kolor, Kareta, Full, czy duży lub mały strit, to powstrzymujemy się od wymiany, bo mamy bardzo dobre karty. W przypadku gdy mamy parę, dwie pary lub trójkę to możemy jeszcze poprawić swoją sytuację wymieniając karty, które nie są nam potrzebne.

$figury = Ilosc_Figur($rozdanie);
$do_wymiany = (false,false,false,false,false);

for ($i=0;$i<=4;$i++){
    if ($figury[JakaFigura($rozdanie[$i])]==1){
        $do_wymiany[$i]=true;
    }
}

W powyższym kodzie karty które występują tylko raz oznaczamy jako karty do wymiany. Do zapamiętania tego służy dodatkowa tablica o nazwie $do_wymiany. Wymiana kart może wyglądać tak:

for ($i=0;$i<=4;$i++){
    if ($do_wymiany[$i]){
        $rozdanie[$i]=$talia[$_SESSION['wolna']++];
    }
}

Gdzie zmienna o nazwie $_SESSION['wolna'] służy do oznaczenia indeksu ostatniej nie rozdanej jeszcze karty z talii. Tak właściwie to nie musimy wymieniać kart w dwóch etapach. Możemy w ogóle nie używać tablicy $do_wymiany tylko od razu wymienić odpowiednie karty. Tak więc zamiast tego wszystkiego co powyżej, moglibyśmy napisać:

$figury = Ilosc_Figur($rozdanie);
for ($i=0;$i<=4;$i++){
     if ($figury[JakaFigura($rozdanie[$i])]==1){
         $rozdanie[i]=$talia[$_SESSION['wolna']++];
    }
}

Porównanie wyników

Tutaj trzeba przypomnieć informacje z trzeciego podrozdziału sprawdzenie wyników. Każdy wynik jest oznaczony jakąś liczbą w taki sposób, że im większa liczba tym lepszy wynik rozdania, czyli:Porównanie wynikówTutaj trzeba przypomnieć informacje z trzeciego podrozdziału sprawdzenie wyników. Każdy wynik jest oznaczony jakąś liczbą w taki sposób, że im większa liczba tym lepszy wynik rozdania, czyli:

Define('ROWEREK',       0);	
Define('JEDNA_PARA',    1);	
Define('DWIE_PARY',     2);	
Define('TROJKA',        3);	
Define('MALY_STRIT',    4);	
Define('DUZY_STRIT',    5);	
Define('FULL',          6);
Define('KARETA',        7);
Define('KOLOR',         8);	
Define('MALY_POKER',    9);
Define('DUZY_POKER',   10);

Takie podejście do do problemu, ułatwia nam ustalenie kto jest zwycięzcą. Wystarczy wybrać największą liczbę ze zbioru licz przedstawiających wyniki graczy. Przykładowy kod:

$max = $wyniki_graczy[0];
for ($i=1;$i<=$ilosc_graczy;$i++){
     if ($wyniki_graczy[$i]>$max){
         $max = $wyniki_graczy[$i];
    }
}

Oczywiście może się zdarzyć, że dwóch graczy ma taki sam wynik. Na przykład oboje mogą mieć dwie pary. Można wtedy wziąć pod uwagę jakie figury lub karty posiadają gracze z takim samym wynikiem. Nie jest to raczej trudne gdyż wszystkie figury, a także kolory mają swoje liczby porządkowe, które mówią też o ich priorytecie. Na przykład AS ma największa przydzieloną liczbę (5) a DZIEWIĄTKA najmniejszą liczbę (0). A wiec w przypadku gdy wyniki dwóch lub większej ilości graczy są takie same, a nie chcemy podawać wyniku remisowego, możemy zsumować wszystkie liczby porządkowe kart tych graczy i porównać je.

Informacje na podobny temat:
Wasze opinie
Wszystkie opinie użytkowników: (8)
Błędy merytoryczne cd.
Wtorek 21 Kwiecień 2009 10:46:02 am - jojo <jojo_at_jabster.pl>

Rozpędziłem się w poprzednim komentarzu: gracz 2 ma 5 punktów ;)

Błędy merytoryczne
Wtorek 21 Kwiecień 2009 10:40:51 am - jojo

Widzę w tym artykule kilka błędów merytorycznych.

Przede wszystkim chodzi mi o kolejność układów. Do strita jest dobrze, apóźniej jest już przemieszane. Po stricie powinien być kolor dalej full, kareta i poker.

Po drugie w pokera powinno się jednak grać całą talią (52 karty) i o ile w tym wypadku funkcje sprawdzające obecność par, trójki, fulla, koloru czy karety powinny się sprawdzić, o tyle funkcja sprawdzająca strita już się wyłoży. Generalnie w pokerze nie powinno się rozróżniać mały/duży strit. Strit to po prostu 5 kart po kolei i takiego sprawdzenia należałoby użyć. Jeśli dwóch graczy ma jednocześnie strita o zwycięstwie decyduje najwyższa karta. To samo się tyczy pokera. Rozróżnianie na duży/mały ma sens tylko przy grze 24 kartami. Przy pełnej talii mamy pokera, który faktycznie jest stritem w kolorze, natomiast, to co nazywasz "Dużym pokerem" nazywa się po prostu "Poker królewski" i w całej grze są tylko 4 możliwe takie układy. W związku z tym sprawdzenie tego układu powinno polegać na sprawdzeniu czy jest pokerem oraz czy najwyższą kartą jest as.

Trzecia sprawa to wyznaczanie zwycięzcy w przypadku podobnych układów. Proponujesz tu sumowanie wartości kart. Niestety taki algorytm nie zadziała. Przykład:
Gracz 1: 991010A
Gracz 2: 99JJ10
Zgodnie z zasadami powinien wygrać gracz 2, ponieważ ma wyższą drugą parę. Natomiast jeśli zsumujemy wartości ich kart, dla gracza pierwszego otrzymamy wartość 7, a dla gracza 2 dostaniemy 6. Tego typu układy jeszcze łatwiej byłoby uzyskać przy grze pełną talią.

Ogólnie artykuł jest nieco naiwną próbą sprowadzenia dość skomplikowanej gry, jaką jest poker, do poziomu prościutkiego skryptu. Występują tu oczywiste błędy merytoryczne, o których pisałem wyżej, a także sporo literówek.

Na koniec cytat za Wikipedią:
"Poker - gra karciana (...), której celem jest wygranie pieniędzy od pozostałych uczestników". Więc ja się pytam: A gdzie licytacja? :)

Full? - tak
Piątek 27 Marzec 2009 11:58:19 pm - SebaZ <sebaz_at_extreme-fusion.pl>

Wystarcza, bo full to własnie para + trójka

powtorzenia w tekscie
Piątek 27 Marzec 2009 10:18:01 pm - dr_bonzo

@scanner (chyba do ciebie uwaga :D)
w ostatnim podrozdziale prawie wszystko jest po 2 razy wpisane.

full -2
Piątek 27 Marzec 2009 10:15:50 pm - dr_bonzo

@slawek: ok, masz racje
tamten komentarz pisalem po obejrzeniu funkcji Full() a nie zajrzeniu do Jedna_Para().

return in_array( 2, Ilosc_Figur($rozdanie) );

to akceptuje tylko karty ktore wystapily TYLKO w ilosci 2 :)

----------
inne uwagi:

1. tasowanie kart przeciez jest.. shuffle()

2. "gracz dostaje po 5 kart"
for($i=0;i<=$4;$i++){ // zadziala
for($i=0;i< $5;$i++){ // ale czy nie lepsze/czytelniejsze = 5 to 5

para
Piątek 27 Marzec 2009 9:44:37 pm - php programmer <slawek.lis.gdynia_at_wp.pl>

i nie zawiera..
funkcja jedna para nie liczy par, które znajdują się w trójce

full house...
Piątek 27 Marzec 2009 9:20:37 pm - dr_bonzo

@scanner: ta para dodatkowo NIE MOZE zawierac sie w trojce, bo inaczej trojka jest zarowno trojka i para, wiec w kodzie jest blad.

Full?
Piątek 27 Marzec 2009 9:14:01 pm - scanner <scanner_at_poczta.php.pl>

Na pokerze się nie znam, ale czy do wykrycia full'a na pewno wystarczy sprawdzić, czy istnieje para i trójka?

Mentax.pl    NQ.pl- serwery z dodatkiem świętego spokoju...   
O nas | Kontakt | Mapa serwisu
Copyright (c) 2003-2017 php.pl    Wszystkie prawa zastrzeżone    Powered by eZ publish Content Management System eZ publish Content Management System