Start Kontakt

Gra Tysiączek, czyli nowa odsłona gry w kości "Tysiąc"

Wstęp

Na początku trochę informacji dla programistów i tych, których może to zainteresować .

Gra Tysiączek powstała, ponieważ chciałem przetestować trzy ważne dla mnie w danym momencie zagadnienia:

Dla języka C++ istnieje wiele bibliotek służących do tworzenia interfejsów graficznych. Najstarszą i od razu gotową do użycia jest MFC. W MFC sporo kiedyś programowałem. Chciałem jednak sprawdzić, jak programuje się interfejsy przy użyciu publicznie dostępnych bibliotek. Najpierw spróbowałem użyć biblioteki Nana, ale niestety w pewnym momencie poddałem się, ponieważ była ona bardzo słabo udokumentowana, a forum użytkowników też nie spełniało swojej roli. Przyjrzałem się więc dokładniej bibliotece FLTK i postanowiłem ją przetestować podczas tworzenia programu symulującego grę "Tysiąc" w kości.

Wiele osób zna zasady klasycznej gry "Tysiąc" w kości. Ja zaimplementowałem takie reguły, które obowiązywały, gdy w wieku nastoletnim grałem w nią namiętnie z kolegami. W internecie dostępne są różne opisy gier "Tysiąc", czasem z nieco odmiennymi zasadami, ale zawsze chodzi o to, by zdobyć 1000 punktów, przy czym wyróżniającymi się kostkami są jedynki i piątki (dokładny opis reguł jest dostępny w akapicie akapitu Zasady gry w 1000 lub w instrukcji użytkownika zawartej w pliku ZIP z grą (link poniżej)).

W stosunku do oryginału gra została przeze mnie poszerzona o możliwość rozgrywki sieciowej z udziałem wielu graczy, a także sztuczną inteligencję, dzięki czemu w grę można grać z graczami komputerowymi.

Możliwość gry sieciowej wzięła się stąd, że chciałem sprawdzić, jak można zaprogramować komunikację sieciową za pomocą biblioteki boost::asio. Kiedyś na potrzeby gry Emperor of the World napisałem bibliotekę komunikacji sieciowej opartą na mechanizmie Winsock i teoretycznie mógłbym jej użyć w Tysiączku, ale potrzebowałem większego wyzwania! Poza tym zdobyta wiedza mogła mi się przydać, ponieważ biblioteka boost::asio jest wykorzystywana w wielu aplikacjach.

Jeśli chodzi o FLTK, jest to całkiem przyjemna biblioteka, dość dobrze udokumentowana. Za pomocą stosunkowo prostego kodu można utworzyć złożone aplikacje graficzne. Prawdę mówiąc, miałem z nią tylko jeden problem: okazało się, że funkcje modyfikujące grafikę nie działają zbyt sprawnie w oddzielnych wątkach. W grze wykorzystuję taki wątek do animacji rzucania kośćmi. Na niektórych słabszych komputerach może w tym czasie być widoczne migotanie całego lub fragmentu okna. Opcję tę można więc wyłączyć w grze. Trochę poszperałem po internecie i okazało się, że rzeczywiście nie jest zalecane modyfikowanie ekranu w wątkach innych niż główny. Szkoda, bo w MFC czy też w oknach tworzonych za pomocą API Windows takie wątki graficzne działały bezproblemowo.

Po testach boost::asio okazało się, że ta biblioteka nie jest aż tak wysokopoziomowa, jak się wcześniej spodziewałem. Oczywiście, jest nowoczesna i jakieś proste przykłady serwerków sieciowych można za jej pomocą szybko uruchomić. Większość przykładów dostępnych w internecie dotyczy jednak serwerów stron www, a ja chciałem uzyskać dwukierunkową komunikację pomiędzy programami przy użyciu zdefiniowanego protokołu gry. W tym celu musiałem napisać własną bibliotekę, która opakowywała podstawowe klasy i metody biblioteki boost::asio. W wolnej chwili postaram się ją doszlifować i opublikować, włącznie z jakimś prostym przykładem programu wykorzystującego protokół.

Sztuczna inteligencja została wdrożona w sposób klasyczny, tzn. bez wykorzystania uczenia maszynowego. Wynika to stąd, że gra jest bardzo losowa, a poza tym jej zbiór decyzyjny jest niewielki, więc można pokusić się o stworzenie funkcji, których logika odzwierciedla sposób myślenia człowieka. Gdyby gra była bardziej złożona i mniej zależna od przypadku, być może warto byłoby się zastanowić nad implementacją jakiegoś rozwiązania uczenia maszynowego. Więcej o wykorzystanych algorytmach można się dowiedzieć w oddzielnym artykule zatytułowanym Algorytmy sztucznej inteligencji w grze Tysiączek, dostępnym w opcji Programowanie.

 

Pobieranie, instalacja i użycie programu

Grę (w postaci pliku ZIP) można pobrać na swój komputer z Google Drive (informacja dla paranoików: jeśli się obawiacie pobrać aplikację, możecie sami sprawdzić cały adres pliku z grą choćby w darmowym skanerze online Dr. Web). Oczywiście ta gra w tysiąca jest darmowa, nie trzeba za nią nic płacić.

Zapisany plik należy rozpakować do dowolnego katalogu. Instrukcja obsługi gry, a także dokładnie opisane zasady gry w 1000, są dostępne w dokumencie Instrukcja uzytkownika PL.pdf, zawartym w piku ZIP. Z zasadami gry w tysiąca możesz się też zapoznać poniżej w akapicie Zasady gry w 1000.

Jeśli po uruchomieniu programu pojawi się błąd "The code execution cannot proceed because MSCVP140.dll was not found", należy ze strony firmy Microsoft wybrać tak zwany Pakiet redystrybucyjny programu Microsoft Visual C++ 2015, kliknąć przycisk "Pobierz", następnie zaznaczyć opcję "vc_redist.x86.exe", pobrać plik, zainstalować go, a dopiero potem uruchomić grę.

Gra po uruchomieniu może działać w trzech trybach:

Oto przykładowy zrzut ekranu z działającej gry. Zauważmy, że grają ze sobą jedynie gracze komputerowi - po dwóch w wersji 2 i 3.

gra Tysiąc

Opcje gry pozwalają na wybranie i zapamiętanie używanego języka (do dyspozycji są języki polski, angielski i niemiecki, ale sprytni użytkownicy mogą wpaść na pomysł, jak można poszerzyć program o kolejny język - dla ułatwienia podam, że nie wymaga to wcale modyfikacji kodu i rekompilacji programu). Można na przykład dodać nawet język japoński czy jakiś inny oparty na "krzaczkach", ponieważ program obsługuje treści w formacie UTF-8.

Przykładowo, oto zrzut fragmentu ekranu gry po cześciowym zaimplementowaniu języka japońskiego:

gra Tysiąc po japońsku

 

Zasady gry w 1000

Gra komputerowa jest oparta na tradycyjnej grze w kości „w tysiąca”. W stosunku do oryginału została poszerzona o możliwość rozgrywki sieciowej z udziałem wielu graczy, a także sztuczną inteligencję, dzięki czemu w grę można grać z graczami komputerowymi.

Wygrywa ten, kto pierwszy zdobędzie 1000 lub więcej punktów. Gracze mają do dyspozycji maksymalnie 6 kości. Liczba ta zmniejsza się automatycznie po odłożeniu wybranych kostek. Główną zasadą jest to, że po rzucie trzeba odłożyć co najmniej jedną poprawną kostkę.

Poprawne kostki to jedynka (ma wartość 10) i piątka (ma wartość 5). Inne wartości są poprawne pod warunkiem, że w danym rzucie występują co najmniej trzy takie same kości (patrz niżej).

Po rzucie można oczywiście odłożyć więcej kostek, jeśli wyrzucono kilka jedynek i piątek. Inne kostki się nie liczą, ale wyjątkiem jest sytuacja, gdy gracz uzyska w danym rzucie 3 lub więcej takich samych kostek. Gdy są 3, wówczas wartość kostki mnożymy przez 10. Na przykład wyrzuciliśmy trzy czwórki, a więc 4*10 =40 punktów. W przypadku jeszcze większej liczby takich samych kostek wzór wyliczeniowy trochę się komplikuje, ponieważ mnożymy pośredni wynik uzyskany z trzech kości odpowiednią liczbę razy przez dwa.

Jaka jest punktacja, najlepiej pokazać na przykładach:

  1. Wyrzuciliśmy pięć kości, każda o wartości 3:
    Trzy kości dają 3*10=30, cztery kości 30*2=60, pięć kości 60*2=120 punktów (wynik = 3*10*2*2).
  2. Wyrzuciliśmy cztery piątki:
    Trzy piątki mają wartość 5*10=50, cztery to poprzedni wynik pomnożony przez 2 czyli 100 punktów.
  3. Wyrzuciliśmy sześć jedynek:
    Trzy jedynki dają 10*10=100, cztery to 100*2=200, pięć to 200*2=400, sześć to 400*2=800 punktów (jest to nawiasem mówiąc najwyższy wynik, jaki można uzyskać za jednym rzutem).

Gra składa się z 3 etapów. Pierwszy jest etapem początkowym. Aby go ukończyć, trzeba w jednej kolejce zdobyć co najmniej 105 punktów. W drugim etapie, który trwa do przekroczenia 900 punktów, wystarczy rzucić za 30 punktów i można już je zaliczyć (czyli dodać do bieżącego wyniku sumarycznego). W trzecim etapie należy w jednej kolejce zdobyć 100 punktów. Oczywiście można też „przeskoczyć” dany etap – wystarczy po prostu w jednej kolejce uzyskać odpowiednią liczbę punktów. Na przykład gracz ma 890 punktów (czyli jest na etapie 2.) i w jednej kolejce wyrzuca 130 punktów. Oznacza to, że jego sumaryczny wynik będzie równy 890+130=1020, a więc zakończył już grę „przeskakując” etap 3.

Punkty się sumują, a jednocześnie zmniejsza się liczba kostek, którymi rzucamy. Jeśli dojdzie do takiej sytuacji, że nie zostanie już żadna kostka, wówczas ponownie będziemy mogli rzucać sześcioma.

Dodatkową zasadą gry jest to, że w przypadku, gdy któryś gracz uzyska w danej kolejce tyle punktów, iż jego sumaryczny wynik przewyższy punktację innego gracza, wówczas temu graczowi odejmuje się 100 punktów.

Na przykład gracz A miał 450 punktów, a gracz B 490 punktów. Gracz A w jednej kolejce wyrzucił 55 punktów, przez co uzyskał wynik 505 punktów i przekroczył wynik gracza B. Spowodowało to odjęcie 100 punktów graczowi B, więc finalnie wyniki są następujące: gracz A - 505 punktów, gracz B - 390 punktów.

Takie odejmowanie punktów ma także miejsce, gdy gracz przechodzi z etapu pierwszego do drugiego. Wtedy „przeskakuje” graczy, którzy mają mniej punktów od niego.

Na przykład gracz A miał 80 punktów, gracz B 115 punktów, a gracz C był na pierwszym etapie (początkowym), jednak w jednej kolejce uzyskał 120 punktów i przeszedł do etapu drugiego. W wyniku tego zarówno gracz A stracił 100 punktów i ma ich -20, jak również gracz B stracił 100 punktów i ma ich obecnie 15.