Univerzita Karlova v Praze Matematicko-fyzikální fakulta
BAKALÁŘSKÁ PRÁCE
Michal Bilanský Obecná karetní hra s uživatelsky definovanými pravidly Katedra teoretické informatiky a matematické logiky
Vedoucí bakalářské práce: Mgr. Martin Babka Studijní program: Informatika Studijní obor: programování
Praha 2013
Děkuji Mgr. Martinu Babkovi za odborné vedení mé práce, podnětné rady a čas, který mi během vypracovávání této práce věnoval. Dále bych chtěl poděkovat svým rodičům za podporu v mém dosavadním studiu.
Prohlašuji, že jsem tuto bakalářskou práci vypracoval samostatně a výhradně s použitím citovaných pramenů, literatury a dalších odborných zdrojů. Beru na vědomí, že se na moji práci vztahují práva a povinnosti vyplývající ze zákona č. 121/2000 Sb., autorského zákona v platném znění, zejména skutečnost, že Univerzita Karlova v Praze má právo na uzavření licenční smlouvy o užití této práce jako školního díla podle §60 odst. 1 autorského zákona. V Praze dne 31. července 2013
Podpis autora
Název práce: Obecná karetní hra s uživatelsky definovanými pravidly Autor: Michal Bilanský Katedra: Katedra teoretické informatiky a matematické logiky Vedoucí bakalářské práce: Mgr. Martin Babka, Katedra teoretické informatiky a matematické logiky Abstrakt: Cílem práce je navrhnout a implementovat obecnou karetní hru pro více hráčů. Pravidla hry jsou definována uživatelem pomocí dostupné sady pravidel a příkazů. Hra probíhá po síti, součástí je i jednoduchá umělá inteligence umožňující lidský faktor nahradit počítačem. Program bude vytvořen pro .NET na platformě Windows v programovacím jazyku C#. Klíčová slova: obecná karetní hra, síťová hra, vlastní pravidla
Title: Generic card game with user-defined rules Author: Michal Bilanský Department: Department of Theoretical Computer Science and Mathematical Logic Supervisor: Mgr. Martin Babka, Department of Theoretical Computer Science and Mathematical Logic Abstract: The goal of this thesis is to design and implement a generic card game for multiple users. The rules of the game are defined by user using an available set of rules and commands. The game is playable on the network and also includes a simple artificial intelligence allowing to replace a human factor by the computer. The program is created for .NET on Windows platform in the programming language C#. Keywords: generic card game, network game, custom rules
Obsah Úvod
3
1 Analýza problému 1.1 Požadavky . . . . . . . . . . . . . . 1.2 Existující řešení . . . . . . . . . . . 1.3 Návrh vlastního řešení . . . . . . . 1.3.1 Barva karty . . . . . . . . . 1.3.2 Hodnota karty . . . . . . . . 1.3.3 Karty . . . . . . . . . . . . 1.3.4 Základní nastavení . . . . . 1.3.5 Proměnné . . . . . . . . . . 1.3.6 Nerovnice . . . . . . . . . . 1.3.7 Karetní nerovnice . . . . . . 1.3.8 Testování přítomnosti karty 1.3.9 Podmínka . . . . . . . . . . 1.3.10 Akce . . . . . . . . . . . . . 1.3.11 Pravidlo . . . . . . . . . . . 1.3.12 Test konce hry . . . . . . . 1.3.13 Zjištění splněných pravidel . 1.3.14 Kontrola herních tahů . . . 1.3.15 Boti . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
4 4 4 5 5 5 5 7 7 7 8 10 10 11 11 11 12 12 13
. . . . . . . . . . . . . . . . .
14 14 14 14 15 15 15 16 16 16 16 17 17 20 20 22 22 22
3 Programátorská dokumentace 3.1 Použité technologie . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Použité technologie .NET . . . . . . . . . . . . . . . . . . . . . . 3.3 Knihovna CardGame . . . . . . . . . . . . . . . . . . . . . . . . . .
23 23 23 23
2 Uživatelská dokumentace 2.1 Obsah přiloženého CD . . . 2.2 Systémové požadavky . . . . 2.3 Instalace . . . . . . . . . . . 2.4 Editor her . . . . . . . . . . 2.4.1 Základní . . . . . . . 2.4.2 Karty . . . . . . . . 2.4.3 Průběh hry . . . . . 2.4.4 Normální hra . . . . 2.4.5 Hra se štychy . . . . 2.4.6 Konec hry . . . . . . 2.4.7 Pravidla . . . . . . . 2.5 Server . . . . . . . . . . . . 2.6 Klient . . . . . . . . . . . . 2.6.1 Připojení k serveru . 2.6.2 Hra . . . . . . . . . . 2.6.3 Chat . . . . . . . . . 2.6.4 Odpojení od serveru
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
1
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.4 3.5
3.6 3.7 3.8 3.9 3.10 3.11
3.3.1 Herní nastavení . . . . . . . . . 3.3.2 Herní entity . . . . . . . . . . . Komunikace mezi serverem a klientem Knihovna Common . . . . . . . . . . . . 3.5.1 Třída CallBack . . . . . . . . . 3.5.2 Rozhraní IServerService . . . 3.5.3 Rozhraní IGameService . . . . 3.5.4 Třída Connection . . . . . . . 3.5.5 Rozhraní IBot . . . . . . . . . . Boti . . . . . . . . . . . . . . . . . . . 3.6.1 CardGameBot . . . . . . . . . . 3.6.2 CardGameBotHeuristics . . . . Knihovna WPFControlLibrary . . . . . 3.7.1 Grafiké komponenty . . . . . . 3.7.2 Konvertory . . . . . . . . . . . Lokalizace . . . . . . . . . . . . . . . . Server WpfGameServer . . . . . . . . . 3.9.1 Serverová a herní služba . . . . 3.9.2 Skóre hráčů . . . . . . . . . . . Klient WpfGameClient . . . . . . . . . 3.10.1 Grafické komponenty . . . . . . Editor her WpfGameEditor . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
23 25 25 28 28 28 28 28 29 29 30 30 30 30 31 31 32 32 32 32 33 34
Závěr
35
Seznam použité literatury
36
Seznam tabulek
37
Seznam obrázků
38
Seznam použitých zkratek
39
Přílohy
40
2
Úvod Karetní hry lze zjednodušeně rozdělit na klasické a moderní. Moderní karetní hry, mezi které patří například Bang! či Magic: The Gathering, mají pravidla specifická pro vlastnosti udávané na kartách a obvykle s takovými kartami nelze hrát jiná hra. Naproti tomu klasické karetní hry se v našich končinách hrají nejčastěji s kartami francouzského (žolíkového) nebo německého (mariášového) typu, pomocí nichž lze hrát široká škála her a herních variací. Na podmnožinu klasických karetních her se zaměříme v této práci. Výhodou klasických karet je, že nejsou napevno svázány s pravidly. Každý si tak může vymyslet svou vlastní hru, stejně tak může i pozměnit pravidla hry již existující. Podívejme se na variace jedné z nejznámějších karetních her v České republice – Prší. Hra vznikla jako varianta původní hry Mau Mau, liší se kupříkladu v typu karet nebo v možnosti přebíjet (přenést hrozící penalizaci na následujícího hráče vhozením k tomu určené karty) esa a sedmičky. Další známá varianta je rychlé Prší, kdy lze vhodit několik karet stejné hodnoty najednou. Na Slovensku je velice populární varianta na rychlé Prší zvaná Faraón. Liší se mimo jiné tím, že při zahrání sedmičky následující hráč bere tři karty namísto dvou, a zelený spodek lze zahrát na jakoukoliv kartu. Tyto variace her se velice snadno adaptují ve skutečném světě. Ovšem s adaptací her na počítači to bývá složitější. Programátor často vytvoří hru „šitou na míruÿ pravidlům jedné konkrétní variace, vetšinou té, která je mu osobně blízká či je populární v jeho zemi. Zakomponování všech variant je obtížné nejen z hlediska zpracování, ale už jenom z toho důvodu, že autor hry tyto varianty v době tvorby programu ani nezná či nemohl znát. Hráč se tedy musí buď přizpůsobit, anebo se poohlédnout po kýžené variantě jinde, možná i v cizím jazyce. Naskýtá se tedy otázka, jak dosáhnout toho, abychom uspokojili i ty hráče, kteří si přejí hrát různé variace her, případně si vytvářet vlastní? Právě na tuto otázku se pokusíme odpovědět. Zavedeme si systém pravidel, které zajistí správný průběh hry. Pravidla jsou vlastně množina podmínek a akcí typu „pokud jsou splněny zadané podmínky, proveď následující herní úkonyÿ. Systém následně implementujeme v programu umožňujícím hrát vzniklé hry po síti anebo proti počítači. V první kapitole zanalyzujeme některá existující řešení a následně si popíšeme vlastní zvolený systém pravidel, umožňující vyhodnotit stav hry a patřičně na ně reagovat. Ve druhé kapitole si popíšeme, jak se se vzniklým programem zachází. Ve třetí kapitole se pak budeme zabývat programovou implementací systému. V závěru shrneme výsledky této práce.
3
1. Analýza problému V této kapitole si ujistíme, čeho chceme v této práci dosáhnout, poté zanalyzujeme některá již existující řešení nastíněného problému, nakonec předvedeme vlastní návrh řešení.
1.1
Požadavky
Budeme se zabývat klasickými karetními hrami pro více hráčů. Pro zjednodušení problému se omezíme na hry, kde není potřeba vykládat karty na stůl a tím vytvářet vlastní kupičky, jako je tomu třeba u Kanasty či Žolíků. Hra tedy obsahuje následující: • hráči mající karty v ruce • štych (každý hráč postupně vhodí jednu kartu na stůl, tyto karty tvoří štych) • talon (karty, které jsou otočené lícem dolů) • odhozené karty (karty, které jsou otočené lícem nahoru) • akce hráče • lízne si (hráč si vezme do ruky jednu kartu z talonu) • vezme odhozenou kartu (hráč si vezme do ruky kartu z odhozených karet) • odhodí kartu • dá kartu do štychu Požadujeme, aby pravidla nebyla fixně nastavena na jednu hru, ale aby bylo možné hru modifikovat. Poslední požadavek je, aby bylo možné hrát hru po síti, případně hráče nahradit počítačem.
1.2
Existující řešení
Ačkoliv existuje nespočetně programů na hraní karet, žádný z nich nesplňuje všechna naše kritéria. Většina programů obsahuje napevno zadaná pravidla jednotlivých her a není možné je modifikovat, případně pouze v minimálním rozsahu pomocí přepínačů. Některé programy obsahují možnost naprogramovat další karetní hry jako modul, který následně program načte a je možné začít vlastní hru hrát, jako je tomu například u hry Generic Card Game Server [1]. Tato varianta je ale pro běžného uživatele neznalého programování nedostupná, a tak se problém opět redukuje na to, co je ochoten napsat programátor. Další možností, jak se k danému problému postavit, je nechat uživatele dělat cokoliv a ověřování pravidel nechat na poctivosti hrajících, jako je tomu ve skutečném světě. Tento postup zvolili například autoři programu Virtual Deck [2]. 4
Tento přístup plně splňuje kritérium modifikovatelnosti her. Bohužel ale nelze jednoduše využít možnosti botů (počítačem řízený hráč). Boti mohou být dvojího ražení. Buď mají pevně daný algoritmus, podle kterého se řídí. Jelikož se ale pravidla u těchto volných her testují pouze pomyslně, nemá se takový bot dle čeho řídit. Druhý typ botů jsou tzv. učenliví boti. Ti sledují průběh hry a zaznamenávají si, jaké jednotliví hráči provádějí tahy. Při dalších hrách se poté snaží naučené tahy napodobit. Ovšem ani takovýto postup nezaručí, že bot bude hrát vždy podle pravidel. Kombinace všech karet na scéně a možných tahů je příliš velká na to, aby se mohl bot naučit kompletní pravidla. Bot by musel odehrát obrovské množství her, aby byl alespoň trochu použitelný. Otázkou ale je, kdo by se uvolil hrát tolik her s hráčem, který (ač neuvědoměle) podvádí?
1.3
Návrh vlastního řešení
Nezbývá, než přijít s vlastním řešením. Pro každou hru si vytvoříme soubor s nastavením. Tento soubor obsahuje základní potřebné informace o hře, karty ve hře, podmínky testující konec hry a v neposlední řadě sekvence herních akcí, které lze zahrát v závislosti na splněných podmínkách. Budeme tak moci pomocí soustav rovnic a nerovnic nechat otestovat hru a podle toho se nám povolí nebo zakáží nějaké posloupnosti herních tahů. To nám umožní psát pravidla typu: „Pokud je karta na stole sedmička a hráč má sedmičku v ruce, musí ji tam hodit.ÿ Hry tak budou snadno modifikovatelné a díky jasně daným pravidlům bude možné do hry zapojit i boty. Nyní si popíšeme celý systém nastavení hezky od základů. Pro představu o všech částech systému poslouží UML diagram 1.1.
1.3.1
Barva karty
V nastavení lze zvolit, jaké barvy karet budeme chtít používat. Standardně se jedná o barvy „červenéÿ, „zelenéÿ, „žaludyÿ, „kuleÿ, pokud používáme německý typ karet, anebo „srdceÿ, „pikyÿ, „trefyÿ, „káryÿ, pokud používáme francouzský typ. Každá barva má svou herní číselnou hodnotu, pomocí které se na ni můžeme odkazovat.
1.3.2
Hodnota karty
Rovněž lze nastavit, jaké hodnoty karet budeme používat. Standardně se jedná o hodnoty „sedmÿ, „osmÿ, „devětÿ, „desetÿ, „spodekÿ, „svršekÿ, „králÿ, „esoÿ pokud používáme německý typ karet, anebo „Aÿ, „2ÿ, „3ÿ, „4ÿ, „5ÿ, „6ÿ, „7ÿ, „8ÿ, „9ÿ, „10ÿ, „Jÿ, „Qÿ, „Kÿ, pokud používáme francouzský typ. Stejně jako u barev i každá hodnota karty má svou herní číselnou hodnotu, pomocí které se na ni můžeme odkazovat.
1.3.3
Karty
Co se karet týče, můžeme zvolit buď německý nebo francouzský typ karet, který zajistí správné obrázky pro karty. Karta se skládá z barvy a hodnoty a má rovněž i svou herní číselnou hodnotu. Navíc má každá karta nastaven počet bodů, které 5
Nastavení hry +název hry: string +popis hry: string +minimální poet hrá : int +maximální poet hrá : int +typ hry: enum +kdo zaíná hru: enum +trumfy: enum +poet karet, které dostane hrá: int +poet karet na volení: int +možnost dát renonc: bool +penalizace za renonc: int +doplnit talon z odhozených karet: bool +kdo bere štych: enum +ctít barvu: bool +ctít hodnotu: bool
Karta karty +herní hodnota: int 0..* +hodnota pi sítání: int
barva 1
hodnota 1
Barva
Hodnota
+herní hodnota: int
+herní hodnota: int
+otestuj konec hry(stav hry:Hra) +zjisti splnná pravidla(stav hry:Hra)
karta 1
Nerovnice
Pravidlo
pravidla +priorita: int 0..*
+specifická hodnota levé strany: int konec hry nerovnice +levá strana: enum 0..* 0..* +operátor: enum +pravá strana: enum Podmínka +specifická hodnota levé strany: enum podmínka +vyhodno(stav hry:Hra): bool 1 +negace: bool +vyhodno(stav hry:Hra): bool barva hodnota 1 1 akce testy 0..* 0..*
Akce +tah: enum
Test p ítomnosti karty Karetní nerovnice
+negace: bool +kde: enum
+vyhodno(stav hry:Hra,karta:Karta): bool karta kritéria karty 0..* 0..*
+vyhodno(stav hry:Hra): bool
Obrázek 1.1: UML diagram systému
6
za ni dostaneme při sčítání skóre. Takto si sestavíme náš vlastní balíček karet a určíme bodování. Lze rovněž nastavit, zda se karty míchají po každé hře.
1.3.4
Základní nastavení
Pro správné spuštění a chod hry, jsou k dispozici následující nastavení: • název a popis hry • minimální a maximální počet hráčů • typ hry (normální s odhozenými kartami nebo na štychy) • kdo začíná hru (následující hráč v pořadí, vítěz nebo poražený) • trumfy (žádné, konkrétní barva, hodnota anebo karta) • počet karet, které dostane každý hráč • počet karet, ze kterých se volí trumfy • možnost dát renonc (chybný tah) • penalizace za renonc • zda se má talon po vyprázdnění doplnit z odhozených karet • kdo bere štych (hráč, který hodil do štychu největší/nejmenší barvu, hodnotu či kartu) • zda se má ctít barva či hodnota (hodit stejnou barvu respektive hodnotu, pokud ji má) ve hře se štychy (tato možnost lze zajistit i pomocí podmínek a akcí)
1.3.5
Proměnné
Klíčovým prvkem našeho řešení jsou proměnné. Hodnota proměnné se zjistí ze stavu hry. Můžeme se tak odkazovat například na počet hráčů či nejvyšší kartu ve štychu. Seznam předdefinovaných proměnných je uveden v tabulce 1.1 a pro hru se štychy navíc v tabulce 1.2.
1.3.6
Nerovnice
Proměnné můžeme používat v nerovnicích. Nerovnice se skládá z levé strany, operátoru a pravé strany. Jsou tedy ve tvaru „(levá strana) operátor (pravá strana)ÿ. K dispozici máme klasické matematické operátory, viz tabulku 1.3. Na každé straně lze nastavit specifickou hodnotu či některou z předdefinovaných proměnných. Vyhodnocení nerovnice probíhá podle následujícího algoritmu: function nerovnice.vyhodnoť(hra) zjisti levou a pravou stranu ze stavu hry return vyhodnoť „(levá strana) operátor (pravá strana)ÿ end function 7
Proměnná Any Specific DeckCount ThrownCount HandCount PlayersCount PlayersHavingCardsCount PlayersNotHavingCardsCount ActivePlayers InactivePlayers TrumpColor TrumpValue Trump ThrownCardColor ThrownCardValue ThrownCard ThrownCardAge
Popis cokoliv specifická hodnota počet karet v balíčku počet odhozených karet počet karet v ruce aktuálního hráče počet hráčů počet hráčů s kartami počet hráčů bez karet počet aktivních hráčů počet neaktivních hráčů (nemohou provést žádné tahy) barva trumfů hodnota trumfů herní hodnota trumfové karty barva poslední odhozené karty hodnota poslední odhozené karty herní hodnota poslední odhozené karty jak dlouho je poslední odhozená karta na vrchu
Tabulka 1.1: Předdefinované proměnné Seznam (též sekvence) nerovnic se vyhodnotí kladně, právě když se všechny nerovnice vyhodnotí kladně. function nerovnice[].vyhodnoť(hra) for all nerov ∈ nerovnice[] do if nerov.vyhodnoť(hra) = false then return false end if end for return true end function
1.3.7
Karetní nerovnice
Rozšířením nerovnice je karetní nerovnice. Obsahuje tři nerovnice – pro barvu, hodnotu a kartu. Na levou stranu nerovnice se vždy dosadí číselná hodnota barvy resp. hodnoty resp. karty vyhodnocované karty. Operátory a pravé strany získáme z nastavení hry. Zjistíme tak, jestli testovaná karta splňuje nějaká kritéria. Vyhodnocení probíhá dle tohoto algoritmu: function (karetní nerovnice).vyhodnoť(hra, karta) zjisti číselné hodnoty barvy, hodnoty a karty a doplň je na levé strany nerovnic return barva.vyhodnoť(hra) and hodnota.vyhodnoť(hra) and karta.vyhodnoť(hra) end function
8
Proměnná TrickOrder FirstTrickCardColor FirstTrickCardValue FirstTrickCard LastTrickCardColor LastTrickCardValue LastTrickCard BestTrickCardColor BestTrickCardValue BestTrickCard HighestCardValueWithFirstTrickCardColor HighestCardWithFirstTrickCardColor HighestCardColorWithFirstTrickCardValue HighestCardWithFirstTrickCardValue LowestCardValueWithFirstTrickCardColor LowestCardWithFirstTrickCardColor LowestCardColorWithFirstTrickCardValue LowestCardWithFirstTrickCardValue
Popis pořadí hráče u aktuálního štychu barva první karty ve štychu hodnota první karty ve štychu herní hodnota první karty ve štychu barva poslední karty ve štychu hodnota poslední karty ve štychu herní hodnota poslední karty ve štychu barva prozatím vítězné karty ve štychu hodnota prozatím vítězné karty ve štychu herní hodnota prozatím vítězné karty ve štychu nejvyšší hodnota karet ve štychu s barvou jako první karta nejvyšší herní hodnota karet ve štychu s barvou jako první karta nejvyšší barva karet ve štychu s hodnotou jako první karta nejvyšší herní hodnota karet ve štychu s barvou jako první karta nejnižší hodnota karet ve štychu s barvou jako první karta nejnižší herní hodnota karet ve štychu s barvou jako první karta nejnižší barva karet ve štychu s hodnotou jako první karta nejnižší herní hodnota karet ve štychu s barvou jako první karta
Tabulka 1.2: Předdefinované proměnné pro štych Operátor EQ NEQ LT LTE GT GTE
Popis = 6 = < ≤ > ≥ Tabulka 1.3: Operátory u podmínek 9
Místo Trick Hand
Popis štych ruka aktuálního hráče Tabulka 1.4: Místa s kartami
Seznam (též sekvence) karetních nerovnic se vyhodnotí kladně, právě když se všechny karetní nerovnice vyhodnotí kladně. function karetní nerovnice[].vyhodnoť(hra, karta) for all nerov ∈ karetní nerovnice[] do if nerov.vyhodnoť(hra, karta) = false then return false end if end for return true end function
1.3.8
Testování přítomnosti karty
Test přítomnosti karty se skládá z balíčku, kde chceme kartu hledat, a z karetních nerovnic, které musí hledaná karta splňovat. Test probíhá tak, že se projde každá karta v balíčku a zjistí se, jestli vyhovuje stanoveným kritériím. Seznam balíčků je uveden v tabulce 1.4. Test umožňuje i negaci, tedy testování nepřítomnosti karty. Algoritmus je následující: function (test přítomnosti karty).vyhodnoť(hra) for all karta ∈ balíček karet v kde do if (kritéria karty).vyhodnoť(hra, karta) = true then return true xor negace end if end for return false xor negace end function Sekvence testů přítomnosti karty se vyhodnotí kladně, právě když se všechny testy přítomnosti karty vyhodnotí kladně. function (test přítomnosti karty[]).vyhodnoť(hra) for all test ∈ test přítomnosti karty[] do if test.vyhodnoť(hra) = false then return false end if end for return true end function
1.3.9
Podmínka
Sekvence nerovnic spolu se sekvencí testů přítomnosti karty tvoří podmínku. Aby byla podmínka splněna, musí být každá nerovnice i test přítomnosti karty splněn(a). Podmínka umožňuje i negaci. Příkladem podmínky je třeba: „Hráč má 10
Akce Card Talon Thrown ThrownAllSameColor ThrownAllSameValue Skip
Popis hráč hodí kartu (do odhozených karet nebo do štychu, dle typu hry) hráč vezme kartu z talonu hráč vezme kartu z odhozených karet hráč vezme všechny karty z vrchu odhozených karet se stejnou barvou hráč vezme všechny karty z vrchu odhozených karet se stejnou hodnotou hráč ukončí tah Tabulka 1.5: Akce
jednu kartu a má v ruce červenou sedmu.ÿ Algoritmus vyhodnocení je velice jednoduchý: function podmínka.vyhodnoť(hra) return (nerovnice.vyhodnoť(hra) and testy.vyhodnoť(hra)) xor negace end function
1.3.10
Akce
Akce udává, jaký tah hráč může udělat. Akce se skládá z typu tahu a karetních nerovnic. Všechny herní tahy jsou uvedeny v tabulce 1.5. Pokud je herní tah Card, tak vhozená karta ještě musí splňovat všechny karetní nerovnice, aby byla akce platná. Příkladem akce je třeba: „Vhoď červenou sedmu.ÿ
1.3.11
Pravidlo
Pravidlo je tvořeno podmínkou a sekvencí akcí. V podstatě říká: „Pokud je splněna tato podmínka, pak lze provést tyto akce.ÿ Každé pravidlo má ještě číslo určující prioritu pravidla. Pokud se u sebe sejde více pravidel, tak se mohou použít pouze ta s minimální prioritou. Sejde-li se více pravidel se stejnou prioritou, může si hráč vybrat.
1.3.12
Test konce hry
Nedílnou součástí konfigurace hry jsou i podmínky pro ukončení hry. Pokud je alespoň jedna podmínka splněna, tak je hra ukončena. Konec hry se vyhodnotí tímto algoritmem: function (nastavení hry).otestuj konec hry(hra) for all podmínka ∈ konec hry do if podmínka.vyhodnoť(hra) = true then return true end if end for return false end function
11
1.3.13
Zjištění splněných pravidel
Splněná pravidla získáme tak, že se projde každé pravidlo hry a zjistí se, jestli je podmínka tohoto pravidla splněna. Když ano, přidáme jej do splněných. function (nastavení hry).zjisti splněná pravidla(hra) splněná pravidla ← ∅ for all pravidlo ∈ pravidla do if pravidlo.podmínka.vyhodnoť(hra) = true then splněná pravidla.přidej(pravidlo) end if end for return splněná pravidla end function
1.3.14
Kontrola herních tahů
Pokaždé, když se změní hráč, tak se nejdříve otestuje, zda jsou splněny podmínky konce hry. Pokud ano, ukončíme hru. Pokud ne, tak se zjistí pravidla, která jsou splněna. Každé pravidlo obsahuje sekvenci akcí, které lze vykonat. function změna hráče hráč ← následující hráč if (nastavení hry).otestuj konec hry(hra) = true then ukonči hru else splněná pravidla = (nastavení hry).zjisti splněná pravidla(hra) end if end function Příklad. Máme pravidla: „Pokud je odhozená karta sedmička, lízni si dvě karty,ÿ a „Pokud je odhozená karta červená, lízni si tři karty a odhoď jednu kartu.ÿ Hra se nachází ve stavu, kdy je odhozená karta zelená sedmička. Zjistí se, že obě tato pravidla jsou splněna. Potom hráč může provést buď sekvenci akcí: „Lízni, lízni, předej tah.ÿ anebo „Lízni, lízni, lízni, odhoď kartu, předej tah.ÿ Když hráč provede tah, musí se zkontrolovat, jestli je tah v pořádku. To se zajistí tak, že si udržujeme číslo aktuálního tahu n. Poté zjistíme, jestli se akce na n-tém místě v nějaké sekvenci akcí shoduje s tahem, který provedl hráč. Pokud nějakou takovou akci najdeme, tak je tah v pořádku, jinak je tah neplatný a hráč musí hrát znovu. Příklad. Využijeme nastolený příklad a řekněme, že se nacházíme ve třetím tahu a uživatel provedl tah: „Vezmi kartu z odhozených karet.ÿ Systém projde všechna splněná pravidla a podívá se, jestli se na třetím místě nachází akce: „Vezmi kartu z odhozených karet.ÿ Dle pravidel ale jako třetí tah může zahrát pouze: „Předej tah.ÿ nebo „Lízni.ÿ Systém tedy zahlásí, že tah je neplatný, a hráč musí hrát znovu. Opět se nacházíme ve třetím tahu a řekněme, že hráč nyní zahraje: „Lízni.ÿ Systém projde všechna pravidla, zjistí, že tato akce je v souladu s pravidlem: „Lízni, lízni, lízni, odhoď kartu, předej tah,ÿ a provede tah. function validní tah(tah, číslo tahu) for all pravidlo ∈ splněná pravidla do if tah odpovídá pravidlo.akce[číslo tahu] then 12
return true end if end for return false end function Při provedení tahu poté musíme smazat pravidla, která nejsou souladu s tímto tahem. function proveď tah(tah, číslo tahu) for all pravidlo ∈ splněná pravidla do if tah neodpovídá pravidlo.akce[číslo tahu] then splněná pravidla.odeber(pravidlo) end if end for proveď tah ve hře end function Příklad. V nastoleném příkladu tedy ze splněných pravidel vymažeme pravidlo: „Lízni, lízni, předej tah.ÿ
1.3.15
Boti
Popsaný systém pravidel výrazně usnadňuje práci botům. Bot nemusí sám zjišťovat, které sekvence tahů jsou přípustné a které ne. Bot si jednoduše vyžádá seznam splněných pravidel, poté z nich může splnit kteroukoliv sekvenci akcí. Výběr takové sekvence je již na programátorovi daného bota. Bot se může specializovat na některé hry, stejně tak může být univerzální pro všechny hry a vybírat třeba první sekvenci akcí, která mu přijde pod ruku.
13
2. Uživatelská dokumentace V této kapitole seznámíme uživatele s programem a detailně popíšeme jeho ovládání. Nejprve provedeme uživatele jednoduchou instalací. Nainstalovaný program se skládá ze tří podprogramů. Jedná se o editor her, který slouží k vytváření a editaci vlastních her. Dále si popíšeme program na ovládání serveru, pomocí kterého lze založit server a přidat do hry boty. Nakonec se podíváme na klientský program, kde se odehrává grafická část hry. Editor her i ovládání serveru lze spustit přímo z klientského programu pomocí příslušných položek menu.
2.1
Obsah přiloženého CD
Na CD se nacházejí následující soubory a složky: • text.pdf – obsahuje text této práce • doc.chm – obsahuje programátorskou dokumentaci • dotNetFx40 Client x86 x64.exe – spustí instalaci Microsoft .NET Framework 4 Client Profile [3] • setup.exe – spustí instalační program pro platformu Microsoft Windows • setup.msi – spustí instalační balíček pro platformu Microsoft Windows • src – v této složce se nachází zdrojové kódy programu, dynamické knihovny třetích stran potřebné ke spuštění programu a tři hry vytvořené pomocí editoru her (lízaný Mariáš, Prší, Srdce)
2.2
Systémové požadavky
Pro správný chod programu je potřeba mít nainstalovaný Microsoft .NET Framework 4 Client Profile [3]. Ten je oficiálně dostupný pouze pro operační systémy: • Microsoft Windows XP • Microsoft Windows Vista • Microsoft Windows 7
2.3
Instalace
Instalace se provede spuštěním přiloženého instalátoru setup.exe. Instalační program detekuje přítomnost Microsoft .NET Framework 4 Client Profile v systému a případně nabídne jeho instalaci. Po instalaci se v cílové složce vytvoří následující složky. Ve složce Games jsou umístěny vytvořené hry, ve složce Bots jsou soubory s boty, složka Score slouží na ukládání databází se skóre hráčů.
14
2.4
Editor her
Editor spustíme pomocí programu WpfGameEditor.exe. V horní části se nachází menu s položkami: • Hra – slouží pro manipulaci s hrou • Nová – vytvoří novou hru • Načíst. . . – načte již existující hru ze souboru *.gam (binární) nebo *.gml (XML) • Uložit. . . – uloží existující hru do souboru *.gam (binární) nebo *.gml (XML) • Konec – ukončí editor • Jazyk – umožňuje přepnout jazyk programu, změny se projeví po restartu programu • Jazyk (system) – nastaví systémový jazyk • Čeština – nastaví jako jazyk češtinu • English – nastaví jako jazyk angličtinu • Nápověda • O programu. . . – zobrazí okno s informacemi o programu Pod menu je pás s kartami, mezi kterými lze přepínat. Na každé kartě lze editovat rozličná nastavení.
2.4.1
Základní
Na kartě Základní můžeme nastavit základní informace o hře, jako název hry, minimální počet hráčů, maximální počet hráčů, typ hry a popis.
2.4.2
Karty
Na kartě Karty lze nastavit, jaké karty budou ve hře použity. Lze nastavit, jestli se mají karty na začátku každé hry zamíchat. Dále lze vybrat, zda chceme hrát s kartami německého (German) nebo francouzského (French) typu. Zvolí se, jaké barvy a hodnoty chceme ve hře mít. Tlačítkem Přidat přidáme novou prázdnou položku, tlačítkem Odstranit odstraníme vybranou položku, tlačítkem Nahoru respektive Dolu posuneme vybranou položku nahoru respektive dolu. Editace se provádí pomocí textových polí pod seznamem položek. U názvu položky v seznamu je uvedena její hodnota, pomocí které se lze odkazovat v pravidlech a akcích. Dole se zobrazí seznam karet ve hře. U každé karty lze editovat její hodnotu ve hře a hodnotu při sčítání (kolik bodů se za tuto kartu přičte, máme-li ji v sebraném štychu, případně odečte, zůstala-li nám v ruce). Dále lze u karty nastavit kód, pomocí kterého se určí, jaký bude mít karta obrázek. První trojčíslí značí barvu obrázku, druhé trojčíslí značí hodnotu obrázku. Specifické hodnoty udává tabulka 2.1. 15
Číslo 0 1 2 3 4 5 6 7 8 9 10 11 12
Německý typ Barva Hodnota červené sedm zelené osm žaludy devět kule deset spodek svršek král eso -
Francouzský typ Barva Hodnota srdce A piky 2 trefy 3 káry 4 5 6 7 8 9 10 J Q K
Tabulka 2.1: Tabulka s kódy barev a hodnot
2.4.3
Průběh hry
Na kartě Průběh hry lze nastavit, kdo začíná další hru, počet karet, které dostane každý hráč, počet karet, ze kterých se volí trumfy, možnost dát renonc (chybný tah) a penalizace za renonc. V neposlední řadě lze zvolit, co bude považováno za trumf, zda-li barva, hodnota či jedna konkrétní karta.
2.4.4
Normální hra
Na kartě Normální hra lze nastavit vlastnosti specifické pro normální hru. Konkrétně se jedná o vyprázdnění balíčku karet, který můžeme případně doplnit o karty již odhozené.
2.4.5
Hra se štychy
Na kartě Hra se štychy lze nastavit vlastnosti specifické pro hru se štychy. Můžeme zvolit, jestli má hráč ctít barvu či hodnotu, tedy hodit stejnou barvu respektive hodnotu, pokud ji má. Také se zde nastaví, kdo vezme štych.
2.4.6
Konec hry
Na kartě Konec hry se specifikuje, kdy se má hra ukončit. V horním seznamu se nacházejí podmínky, přičemž pokud je alespoň jedna z nich splněna, tak se hra ukončí. Jak se zachází se seznamem je vysvětleno již v kapitole 2.4.2. U každé podmínky lze nastavit jméno, a zda-li se podmínka má znegovat. Dále je zde seznam nerovnic a seznam na testování přítomnosti karty, přičemž každá položka z těchto seznamů musí být splněna. Nerovnice se skládá z levé strany, operátoru a pravé strany. Na každé straně lze nastavit specifickou hodnotu či některou z předdefinovaných proměnných, které se získají ze hry. Seznam operátorů je vyjmenován v tabulce 1.3, seznam 16
předdefinovaných proměnných je v tabulce 1.1 a pro hru se štychy navíc v tabulce 1.2. Pokud je proměnná nedefinovaná, například je použita proměnná pro hru se štychy ale probíhá normální hra, je její hodnota −1. Testování přítomnosti karty se skládá z místa, kde se má karta nacházet, a z nerovnic, které musí karta splňovat. Testuje se, jestli se na daném místě nachází karta, která splňuje dané nerovnice kladené na barvu, hodnotu a herní hodnotu. Seznam míst je uveden v tabulce 1.4.
2.4.7
Pravidla
Na kartě Pravidla se nastavují pravidla. Každé pravidlo má název, prioritu, podmínku a akce. Když hráč přijde na řadu, zjistí se, která pravidla splňují podmínku stejně jako v kapitole 2.4.6, a vezmou se pouze ty s nejmenší prioritou. Hráč poté může provést jednu vybranou sekvenci akcí z těchto pravidel. Seznam akcí je uveden v tabulce 1.5. Pro ilustraci poslouží obrázky pravidel hry Prší 2.1 a 2.2.
2.5
Server
Server lze spustit pomocí programu WpfRemoteServer.exe. Zobrazí se obrazovka podobná jako na obrázku 2.3. V horní části se nachází menu s položkami: • Server – slouží pro manipulaci se serverem • Přidat bota – přidá do hry bota • Konec – ukončí server • Jazyk – umožňuje přepnout jazyk programu, změny se projeví po restartu programu • Jazyk (system) – nastaví systémový jazyk • Čeština – nastaví jako jazyk češtinu • English – nastaví jako jazyk angličtinu • Nápověda • O programu. . . – zobrazí okno s informacemi o programu V kolonce Port je číslo portu a v kolonce IP adresa je seznam síťových adres, na kterých bude server spuštěn. V závislosti na umístění klientského počítače může být některá z těchto adres použita k připojení k serveru. Před spuštěním je nutné vybrat soubor s pravidly hry buď v binárním formátu (přípona *.gam) nebo ve formátu XML (přípona *.gml). Předpřipravené jsou tři hry – lízaný Mariáš, Prší a Srdce. Volitelně lze vybrat i databázi se skóre hráčů. K dispozici jsou připraveni i dva boti. Při prvním startu serveru se klasicky zobrazí výzva firewallu o udělení výjimky, kterou je třeba potvrdit. Pokud se výzva nezobrazí, je třeba udělit výjimku pro server v nastavení firewallu.
17
Obrázek 2.1: Ukázka pravidel pro hru Prší
18
Obrázek 2.2: Ukázka akcí pro hru Prší
Obrázek 2.3: Server
19
2.6
Klient
Server lze spustit pomocí programu WpfRemoteClient.exe. V horní části se nachází menu s položkami: • Hra – slouží pro manipulaci s hrou • Nový server. . . – spustí server • Editor her. . . – spustí editor her • Připojit se. . . – zobrazí dialog na připojení k serveru • Odpojit se – odpojí se od serveru • Konec – ukončí klienta • Řazení karet – slouží pro řazení karet v ruce • Vzestupně – seřadí karty vzestupně podle barvy, karty stejné barvy seřadí podle hodnoty • Vzestupně podle hodnot – seřadí karty vzestupně podle hodnoty, karty stejné hodnoty seřadí podle barvy • Sestupně – seřadí karty sestupně podle barvy, karty stejné barvy seřadí podle hodnoty • Sestupně podle hodnot – seřadí karty sestupně podle hodnoty, karty stejné hodnoty seřadí podle barvy • Žádné – řadí karty podle toho, jak přijdou do ruky • Jazyk – umožňuje přepnout jazyk programu, změny se projeví po restartu programu • Jazyk (system) – nastaví systémový jazyk • Čeština – nastaví jako jazyk češtinu • English – nastaví jako jazyk angličtinu • Nápověda • O programu. . . – zobrazí okno s informacemi o programu
2.6.1
Připojení k serveru
Dialog na připojení k serveru vyžaduje IP adresu a port serveru, jméno hráče a případně i osobní heslo k účtu na serveru. Sejdou-li se hráči se stejným jménem, pomocí hesla mohou rozlišit jejich účty se skóre. Při prvním připojení se klasicky zobrazí výzva firewallu o udělení výjimky, kterou je třeba potvrdit. Pokud se výzva nezobrazí, je třeba udělit výjimku pro klienta v nastavení firewallu.
20
Obrázek 2.4: Klient
21
2.6.2
Hra
Po připojení minimálního počtu hráčů k serveru se prvnímu hráči zobrazí výzva k startu hry. Jakmile hra začne, nově příchozí hráči budou serverem odmítnuti. Na začátku každé hry se zobrazí tabulka s výsledkem předchozí hry a hráč je dotázán, zda chce hrát nové kolo. Další akce se provádějí kliknutím na karty či příslušná tlačítka. Pro odhození karty hráč musí kliknout na kartu v dolním pásu reprezentující hráčovu ruku. Na předání tahu slouží speciální tlačítko pod balíčkem a odhozenými kartami. Vpravo se zobrazují informace o hře – tabulka hráčů se skóre, aktuální hráč, trumf a pravidla hry. Pokud chce hráč vzít kartu z balíčku respektive z odhozených karet, klikne v hlavní části okna na kartu lícem dolu respektive nahoru. Po obvodu hlavní části okna jsou vypsána jména hráčů a v závorce u jména je uveden počet karet v ruce. Hraje-li se hra se štychy, jsou u jmen zobrazeny také karty, které jednotliví hráči dali do štychu.
2.6.3
Chat
Pro zpestření hry si mohou hráči posílat zprávy přes chat, který se skrývá v dolní části obrazovky. Po napsání příspěvku do textového pole a následném potvrzení tlačítkem na odeslání se zpráva odešle všem ostatním hráčům.
2.6.4
Odpojení od serveru
Odpojení je možné provést kdykoliv v menu pomocí volby Odpojit se, zavřením okna, anebo odmítnutím další hry při vyúčtování. Ostatní hráči budou poté odpojeni a server se vypne.
22
3. Programátorská dokumentace 3.1
Použité technologie
Program je postaven na technologii Microsoft .NET Framework 4 [4], přičemž ke spuštění je potřeba minimálně Microsoft .NET Framework 4 Client Profile [3]. Dle oficiálních informací jsou podporované operační systémy: • Microsoft Windows XP • Microsoft Windows Vista • Microsoft Windows 7 (otestováno) Pro ukládání skóre hráčů do databáze je použita technologie Microsoft SQL Server Compact 4.0 [5]. Díky tomu je celá databáze umístěna v jednom souboru a není potřeba mít spuštěný databázový server. Knihovny jsou umístěné v projektu WpfRemoteServer, jedná se o soubor System.Data.SqlServerCe.dll a složky x86, amd64 a CS.
3.2
Použité technologie .NET
Uživatelské prostředí používá technologii Windows Presentation Foundation [7] (WPF), která umožňuje efektivně oddělit grafickou a logickou vrstvu aplikace – tzv. Code-Behind. Zatímco logické prvky máme v jazyce C# v jednom souboru, vzhled komponent je zapsán v jazyce XAML [8] v jiném souboru. Technologie .NET nabízí pro komunikace mezi dvěma programy řadu možností. Jejich přehled je uveden na stránkách MSDN [9]. My budeme potřebovat, aby server založil službu na určité adrese a portu, klient se na tuto adresu napojí, čímž mezi sebou vytvoří informační kanál. Jako vhodní kandidáti se jevily Windows Presentation Foundation [10] (WCF) a .NET Remoting [11]. Oba umožňují připojit se k objektu na druhé straně, čímž se vytvoří proxy, a můžeme na objektu volat funkce stejně, jako by byl lokální. WCF je novější technologie a je mnohem komplexnější. Nicméně k našim účelům nám bude stačit jednodušší .NET Remoting, a ten jsme nakonec zvolili. Veškeré zdrojové kódy jsou psané v programovacím jazyku C# a hojně se využívá i jeho rozšíření LINQ [6].
3.3
Knihovna CardGame
Knihovna CardGame slouží pro reprezentaci hry (Entity) a herního nastavení (Settings). Všechny třídy jsou serializovatelné, aby bylo možné je jednoduše načítat ze souboru a přenášet při komunikaci mezi serverem a klientem.
3.3.1
Herní nastavení
Herní nastavení je přímou implementací navrženého systému v kapitole 1.3. Viz obrázek 3.1. 23
Obrázek 3.1: Diagram nastavení
24
3.3.2
Herní entity
Herní entity reprezentují programátorský ekvivalent prvků z reálného světa. Pro představu o struktuře hry poslouží diagram 3.2. Hlavním prvkem je třída Game, která reprezentuje celou hru. Chová se jako stavový automat. Nejprve načte nastavení, které určí, jak se hra bude chovat. Hra se vždy nachází v nějakém stavu, který je reprezentován výčtovým typem GameState. Přechody mezi stavy ilustruje obrázek 3.3. Nejprve je ve stavu GameState.Init, což znamená, že se do hry mohou přidávat hráči pomocí metody AddPlayer. Když už jsou do hry všichni hráči přidáni, je třeba zavolat metodu Init, která provede základní inicializaci. Hra se přesune do stavu GameState.End, tedy že právě skončila hra. Pro odstartování nové hry je třeba zavolat metodu NewRound, která připraví novou hru, rozdá hráčům karty a určí, kdo bude začínajícím hráčem. Pokud se nevolí trumfy, hra se přesune do stavu GameState.Move. Pokud se mají volit trumfy, hra se bude nacházet ve stavu GameState.Trump. V tom případě je ještě potřeba zvolit trumfy pomocí metody SetTrump a hra se přesune do stavu GameState.Move. Stav GameState.Move znamená, že se od aktuálního hráče (CurrentPlayer) očekává nějaký tah. Je možné si pomocí funkce IsValidMove otestovat, zda určitý herní tah odpovídá pravidlům. Testování pravidel probíhá tak, jak již bylo popsáno v kapitole 1.3. Když je vše v pořádku, může hráč provést tah pomocí funkce PerformMove. Aktuální hráč takto provádí tahy až do té doby, než provede tah GameMove.Skip, což působení aktuálního hráče ukončí a přejde se k dalšímu hráči. Pokud je hra typu GameType.Trick a všichni hráči již odhodili kartu do štychu, přejde hra do stavu GameState.Trick, což vyžaduje sebrání štychu pomocí funkce CollectTrick. Při každé změně hráče se navíc testuje, jestli jsou splněny podmínky konce hry. V případě, že ano, spočítá skóre hráčů a hra přejde hra do stavu GameState.End.
3.4
Komunikace mezi serverem a klientem
Ke komunikaci je použita technologie .NET Remoting. Pro ilustraci poslouží schéma komunikace 3.4. Na serveru je vytvořena serverová služba odvozená od MarshalByRefObject, která je zveřejněna pod určeným jménem, a také je vytvořen TCP kanál pro připojení na server. Klient si také vytvoří TCP kanál pro komunikaci se serverem a připojí se na serverovou službu. Klient tak získá instanci serverové služby, která je ve skutečnosti proxy na skutečnou serverovou službu. Může tak používat serverovou službu jako každý jiný objekt, který se nachází u klienta. Serverová služba může obsahovat i metody, u kterých chceme, aby k nim měl přístup pouze server. Proto serverová služba implementuje servisní rozhraní IServerService, který určuje, jaké metody klient může používat. Toto rozhraní se sdílí mezi serverem a klientem. Skutečná implementace je tak známa pouze serveru. Pokud je hráč připojen, vytvoří se pro klienta nová herní služba, která zprostředkovává komunikaci mezi klientem a hrou. Služba je opět odvozena od MarshalByRefObject a na straně klienta je vytvořeno proxy. Opět, chceme klientovi povolit používat pouze některé metody herního serveru, a tak je použito herní rozhraní IGameService. V herní službě je uložen CallBack. To je objekt odvozen od MarshalByRefObject, u kterého si klient zaregistruje funkce, které 25
Obrázek 3.2: Diagram herních entit
26
Obrázek 3.3: Stavový diagram hry 1)
*)
2)
SERVER ServerService ServerService
ServerService
GameService
CallBack
IServerService
GameService
CallBack
IServerService IGameService
KLIENT
Obrázek 3.4: Komunikace mezi serverem a klientem 27
se mají spustit při událostech. Je vytvořen na straně klienta, takže server získá proxy, přes kterou u klienta události vyvolává. Hra probíhá tak, že server zašle klientům pomocí CallBacku pokyny typu: „Pošli mi kartu.ÿ nebo „Zvol trumf.ÿ Na straně klienta je událost zpracována a je spuštěna příslušná zaregistrovaná funkce. U klienta to zpravidla bývá zpřístupnění ovládacích prvků hry a čeká se, až uživatel na něco klikne. U bota to bývá nějaký algoritmus, který poté pošle serveru odpověď. Až klient vykoná požadavek, pošle se odpověď serveru přes herní službu. Ten odpověď zpracuje a opět zašle klientům pokyny. Pokud některý klient již nechce hrát novou hru, nebo se odpojí, server se vypne a oznámí to všem klientům.
3.5
Knihovna Common
Knihovna Common sdružuje společné funkční komponenty potřebné hlavně pro komunikaci mezi serverem a klientem. Třída ChannelProvider je jednoduchá třída s jednou statickou funkcí, která vrátí nově vytvořený TCP kanál pro daný port. Třída Client sdružuje informace o klientovi, tedy CallBack, jméno, heslo a zda je bot.
3.5.1
Třída CallBack
CallBack je třída, která obsahuje události a uživatel si do ní zaregistruje funkce, které mají být spuštěny při jednotlivých událostech. Když pak server od uživatele požaduje nějakou akci, zavolá událost na CallBacku a tato událost spustí u klienta příslušnou zaregistrovanou funkci.
3.5.2
Rozhraní IServerService
Rozhraní IServerService definuje metody, které musí server poskytovat. Klient se tak může dozvědět aktuální, minimální a maximální počet hráčů. Dále definuje možnost připojení na tento server. Funkce Connect bere jako parametr instanci třídy Client a při úspěšném připojení vrátí herní službu typu IGameService.
3.5.3
Rozhraní IGameService
Rozhraní IServerService definuje metody, které musí poskytovat herní server. Pomocí těchto metod klient komunikuje směrem k serveru. U herní služby je uložen CallBack na klienta, takže server pak může odpovídat zpátky. Dále si pamatuje údaje o klientovi a umožňuje klientovi získat hru (instanci Game). Když server po klientovi vyžaduje například zvolení trumfů či herní tah, klient pošle odpověď přes herní server pomocí příslušné funkce.
3.5.4
Třída Connection
Třída Connection zjednodušuje klientovi komunikaci se serverem. Umožňuje nastavit informace o uživateli (CallBack, jméno, heslo, zda je bot) a o serveru (IP adresa, port). Lze se pomocí ní připojit k serveru a odpojit se od serveru.
28
Také obsahuje delegáta, kde lze zaregistrovat funkci, která se spustí při chybě připojení. Připojení probíhá tak, že si nejdříve vytvoří TCP kanál pro komunikaci, poté se připojí k serveru a získá proxy serverové služby, která implementuje IServerService. Přes serverovou službu se pak pokusí připojit do hry, a pokud bude připojení úspěšné, získá proxy na herní službu implementující IGameService. Herní službu poté používá klient při komunikaci s herním serverem. Heslo se serveru posílá zahešované pomocí MD5.
3.5.5
Rozhraní IBot
Rozhraní IBot musí implementovat bot, aby bylo možné jej použít ve hře. Jedná se o jednu funkci Connect, která bere jako parametr IP adresu a port serveru. Správná implementace by měla bota připojit k serveru a vrátit, zda byla operace úspěšná.
3.6
Boti
Každý bot musí implementovat rozhraní IBot. Když je zavolána funkce Connect, bot si nejprve v CallBacku nastaví funkce, které se mají provést při událostech, a pak se připojí k serveru. Pokud je bot požádán o provedení tahu, nejprve získá seznam možných sekvencí tahů a podle toho si vybere. Při každé odpovědi serveru vytvoříme nové vlákno, aby se nebrzdila hra, poté počkáme dvě sekundy, aby člověk stihl okem zaregistrovat, jaké byly provedeny tahy, a pošleme serveru odpověď přes herní službu. Výsledkem kompilace bota je dynamická knihovna, kterou je možné načíst při přidávání botů na serveru. Programátor bota může chtít, aby byl bot specializován na konkrétní hru a vybíral tahy podle sofistikovaného algoritmu. Stejně tak může být bot použitelný ve všech hrách. Otázkou je, podle čeho si tahy vybírat? Prohledávání stavového prostoru nám moc nepomůže, jelikož stavů je příliš mnoho, nemáme úplnou informaci a navíc stavy nelze jednoduše ohodnotit, protože každá hra, ba i hráč, má jinou taktiku. Může například vybrat hned první možný tah, na který narazí, či použít nějakou rozumnou heuristiku. Ale různé hry mají různé přístupy. Například v Mariáši se snaží získat nejvyšší karty, ale ve hře Srdce to je spíš naopak, tam je snaha podlézat. Takže kdyby se jako heuristika pro vhození karty použila nejvyšší herní hodnota, sice by to možná vylepšilo inteligenci v Mariáši, ale ubralo by to inteligenci ve hře Srdce. Implementovali jsme tedy dva diskutované přístupy. CardGameBot bere první možnost tahu a CardGameBotHeuristics používá heuristiku. Jak již bylo řečeno, heuristika není vhodná pro úplně všechny hry. Lepší řešení se zdá být použití první možnosti tahu s tím, že se pravidla hry ekvivalentně upraví tak, aby respektovala nejlepší strategii. Budeme dávat pravidla od nejvýhodnějších až po ty nevýhodné. Úpravu pravidel si demonstrujeme na hře Srdce. Původní nastavení hry je v souboru Srdce.gml. Jsou tu pouze dvě pravidla: 0 „mám stejnou barvu => dám stejnou barvuÿ 1 „ jinak => hoď libovolnou kartuÿ
29
Pro úpravu si musíme uvědomit taktiku hry. Pokud je ve štychu špatná karta (ohodnocená záporně), tak štych nechceme vzít a budeme radši podlézat (hodíme nižší kartu, abychom štych nevzali). Pokud nemůžeme ctít barvu, můžeme tam vhodit libovolnou kartu. Nejvýhodnější je vhodit tam špatnou kartu, pokud možno nejdříve pikové Q, pak červené. Upravené nastavení hry je v souboru SrdceBot.gml: 0 „mám stejnou barvu, ve štychu je špatná karta (záporná herní hodnota), můžu podlézt => dám stejnou barvu, budu podlézatÿ 0 „mám stejnou barvu => dám stejnou barvuÿ 1 „mám pikové Q (herní hodnota -13) => dám pikové Q (herní hodnota -13)ÿ 1 „mám červené (herní hodnota -1) => dám červené (herní hodnota -1)ÿ 1 „ jinak => hoď libovolnou kartuÿ Pravidla jsou ekvivalentní a bot CardGameBot si vybere tu nejvýhodnější. Tato varianta umožňuje tvůrci pravidel ovlivňovat chování bota ve hře a tím zajistit jeho nejlepší chování, aniž by bylo nutné naprogramovat specifického bota pro danou hru. Takovýto přístup se jeví jako nejlepší možný.
3.6.1
CardGameBot
Bot CardGameBot se při volení trumfů snaží zvolit tak, aby trumfů měl co nejvíce. Při provádění tahů vybere první možný tah. Při odhození karty se vezme první možná karta.
3.6.2
CardGameBotHeuristics
Bot CardGameBotHeuristics se při volení trumfů snaží zvolit tak, aby trumfů měl co nejvíce. Při provádění tahů se snaží nejdříve zbavit co nejvíc karet, až poté se uchýlí k předání tahu a přijímání karet. Při odhození karty se vezme karta s nejvyšší herní hodnotou.
3.7
Knihovna WPFControlLibrary
Knihovna WPFControlLibrary reprezentuje společné grafické komponenty pro editor, server i klienta.
3.7.1
Grafiké komponenty
• AboutWindow se používá na zobrazení informací o programu. Stačí jí nastavit příslušné informace o sestavení a nechat okno zobrazit. • BalloonText rozšiřuje klasický popisek tím, že je žlutý a má zakulacené rohy.
30
• ButtonMsgBox je v podstatě dialogové okno, kterému se dá nastavit text, zprávy a text na tlačítku. Také obsahuje informaci o tom, jestli bylo dialogové okno pomocí tlačítka potvrzeno. • IconRadioButton je klasické přepínací tlačítko, na jehož levé straně je zobrazen nastavený obrázek. • SelectWindow je okno, které dostane při konstrukci seznam objektů a umožní výběr jedné položky.
3.7.2
Konvertory
Součástí knihovny jsou i konvertory. Abychom nemuseli konvertovat objekty v kódu a pak je pomocí kódu nastavovat do grafického prostředí, poslouží nám právě konvertory. V XAMLu u klasického Bindingu nastavíme jako parametr zvolený konvertor a o zbytek konverze se postará WPF. • EnumToBoolConverter zjistí, jestli je konvertovaný enum shodný s enumem v parametru. • FormattedEnumToImageSourceConverter vezme název enumu, aplikuje na něj formát z parametru a z výsledného názvu souboru vytvoří obrázek. • FormattedStringToImageSourceConverter vezme řetězec, aplikuje na něj formát z parametru a z výsledného názvu souboru vytvoří obrázek. • IntIncrementConverter inkrementuje číslo. Vhodné například pokud indexujeme od nuly, ale vypisovat chceme od jedničky. • ItemToArrayConverter z objektu vytvoří pole s tímto objektem. • StringToBoolConverter zjistí, jestli je řetězec shodný s řetězcem v parametru.
3.8
Lokalizace
Lokalizace programů je řešena přes tzv. Resource Files. To jsou soubory, které obsahují různé obrázky, zvuky či texty. Mohou mít i různé jazykové mutace podle systémového jazyka či jazyka dle výběru uživatele. Má-li hlavní soubor název file.resx, pak jazykové mutace mohou být file.cs-CZ.resx pro češtinu či file.en-US.resx pro angličtinu. Naše soubory se jmenují Language.resx s implicitním českým obsahem, respektive Language.en-US.resx s anglickým obsahem, a jsou umístěny ve složce Languages. Při spuštění programu se automaticky vybere příslušný soubor se systémovým jazykem, a pokud tento soubor neexistuje nebo v něm některá slova chybí, jsou nahrazena slovy z implicitního souboru. Chceme-li si sami zvolit jazykovou mutaci, je možné toho docílit před nahráním grafických komponent, v našem případě v konstruktoru okna, nastavením Languages.Language.Culture. Možnost vybrání jazyka je u klienta, serveru i editoru zakomponována do menu a pro provedení změn je potřeba restartovat program. Zvolený jazyk se u každého programu uchovává v jeho nastavení – Settings.Default.Language. 31
3.9
Server WpfGameServer
Server obsahuje serverovou službu, herní službu a jednoduché prostředí.
3.9.1
Serverová a herní služba
Serverová služba se skrývá pod názvem ServerService a implementuje rozhraní IServerService popsané v sekci 3.5.2. Při startu serveru se vytvoří TCP kanál pro komunikaci a nová instance ServerService, které předáme nastavení hry načtené ze souboru a soubor s databází skóre. Dále můžeme nastavit funkce, které se spustí při chybové události či zastavení serveru. Služba si otevře soubor s databází, vytvoří novou instanci Game a předá jí získané nastavení. Služba se poté zveřejní pomocí RemotingServices.Marshal a čeká na klienty. Následně probíhá komunikace jako na obrázku 3.4. Když se klient připojí a předá svůj CallBack, zjistí se, jestli není server plný. Pokud ne, načteme skóre klienta z databáze a vrátíme mu instanci GameService implementující IGameService. Od této chvíle server bude posílat požadavky klientovi přes CallBack a klient na ně bude odpovídat přes IGameService. Do hry můžeme nechat připojit i bota. Podmínkou je, že bot musí implementovat rozhraní IBot. Soubor s botem načteme v novém vlákně jako Assembly a necháme ho připojit k serveru pomocí metody Connect. Dále je již práce bota v jeho režii. Až se naplní minimální kapacita hry, vyšle server prvnímu hráči požadavek na odstartování hry. Klient může klidně čekat, než se připojí ještě další hráči. Když server obdrží požadavek na start hry, ukončí přidávání hráčů do hry a na hře zavolá metodu Init. Nyní podle toho, v jakém stavu se hra nachází, bude k hráčům vysílat požadavky (na trumfy, tah, sebrání štychu a podobně). Na výběr správného požadavku se použije metoda SelectNextAction. Při odpovědi klienta na požadavek vždy zkontroluje, jestli je klient opravdu na řadě, a provede akci ve hře. Jakmile se hra dostane do stavu GameState.End, tedy že hra skončila, pošle klientům dotaz, zda chtějí hrát nové kolo. Pokud odpoví všichni kladně, spustí nové kolo. Jinak uloží skóre, odpojí všechny uživatele a vypne server. Server lze vypnout také ručně pomocí metody serverové služby Stop.
3.9.2
Skóre hráčů
Pro načítání a ukládání skóre hráčů je použit soubor s databází Microsoft SQL Server Compact 4.0. O práci s databází se postará externí knihovna. Schéma tabulky v databázi je uloženo ve třídě Score a manažer skóre, poskytující metody na získání a uložení skóre, je ve třídě ServerService.
3.10
Klient WpfGameClient
Při spuštění klienta se vytvoří CallBack, do kterého zaregistrujeme funkce, které se mají spustit při jednotlivých událostech, které server vyvolává. Typy událostí přibližuje tabulka 3.1.
32
Událost server vyžaduje provedení tahu
server vyžaduje potvrzení nové hry server vyžaduje, aby hráč sebral štych aktualizace hry
Zaregistrovaná funkce na stole aktivuje všechny karty a tlačítka a přiřadí jim událost při kliknutí, jež vyšle přes herní službu odpověď serveru zobrazí okno s výsledky hry a možností zvolit ano/ne zobrazí okno s potvrzením sebrání štychu nechá aktualizovat grafické prvky zobrazující hru pomocí události PropertyChanged
Tabulka 3.1: Události Klient implementuje rozhraní INotifyPropertyChanged, které umožňuje oznámit grafickým komponentám, aby se aktualizovaly, neboť mají změněný obsah. Toho se využívá při aktualizaci hry. Klient vyvolá změnu u nabindovaných vlastností, které závisejí na hře, a grafické komponenty se aktualizují. Pokud se chceme připojit k serveru, je nutné nejdříve spustit dialog na připojení k serveru. Vyplněné informace se uloží do instance Connection a pomocí ní se v metodě pro připojení vytvoří proxy k serveru typu IServerService. Poté probíhá připojování. Jelikož chceme, aby během připojování reagovalo grafické prostředí, musíme tuto akci provést v novém vláknu. Navíc chceme mít možnost připojování kdykoliv zrušit. Musíme si tedy pomoci zámkem, který zajistí, že se nezačneme odpojovat, zatímco už dojde k připojení. Chceme, aby se tyto akce navzájem vylučovaly, což dosáhneme právě zámkem. Jakmile dojde k připojení, tak se dialogové okno zavře. Klient tak získá instanci IGameService, pomocí které může komunikovat směrem k serveru. Nyní již klient akorát čeká, až u něj server vyvolá nějakou událost. Na událost zareaguje zaregistrovanou funkcí v CallBacku. Tato funkce se vykoná a určí, jakým způsobem budeme serveru odpovídat (např kliknutím na kartu). Hlavní panel s kartami je vykreslován podle počtu hráčů. Jména a karty hráčů se zobrazí na obvodu pomyslného kruhu.
3.10.1
Grafické komponenty
Pro grafické prostředí je vytvořeno několik nových komponent. Jsou to: • CardButton – tlačítko s obrázkem karty • ConnectWindow – dialogové okno pro připojení k serveru • ResultTable – tabulka se skóre hráčů • ResultWindow – okno s vyhodnocením hry
33
3.11
Editor her WpfGameEditor
Editor her slouží pro editaci nastavení hry. Umožňuje načíst či uložit serializované nastavení hry buď binárně (přípona *.gam) nebo ve formátu XML (přípona *.gml). Má na první pohled nelehký úkol – zobrazit celý strom nastavení od pravidel až po proměnné a operátory. Pro ulehčení je pro každou úroveň vytvořena nová komponenta. • MainWindow – hlavní okno editoru • CardEditor – editace barev, hodnot a karet na záložce Karty • GameEndEditor – editace podmínek pro ukončení hry na záložce Konec hry • ConditionEditor – editace podmínky (zde se editují nerovnice a testy na přítomnost karty) • ConditionCardEditor – editace karetních nerovnic u testů na přítomnost karty • RulesEditor – editace pravidel hry na záložce Pravidla • ConditionEditor – editace podmínky (zde se editují nerovnice a testy na přítomnost karty) • ConditionCardEditor – editace karetních nerovnic u testů na přítomnost karty • ConditionActionEditor – editace akcí • ConditionCardEditor – editace karetních nerovnic u kritérií kladených na vhozenou kartu U komponent, kde se nachází seznam, se pro manipulaci (přidat, odstranit, posunout nahoru či dolu) používá třída ListBoxHandler, která obsahuje metody pro tyto účely. Nespornou výhodou je použití Bindingu na vybranou položku seznamu. Když uživatel vybere položku seznamu, pak se tato položka rovnou zobrazí v editační komponentě nižší úrovně, aniž by bylo nutné vše nastavovat ručně z kódu. Jednotlivé nastavitelné položky lze opět nastavit pomocí Bindingu. U položek je používán obousměrný Binding, tak se změny rovnou projeví v objektu pro nastavení hry, aniž by bylo třeba psát speciální kontrolní a konverzní funkce. Samotná potřeba kódu se tak omezí na minimum, o většinu se postará WPF.
34
Závěr V této práci jsme zanalyzovali problém modifikovatelnosti klasických karetních her na počítači. Pokusili jsme se najít již existující programy, které dovolují běžnému uživateli přizpůsobovat a editovat pravidla hry, a zároveň je lze hrát proti počítači či po síti s ostatními hráči. Leč žádný takový program se nám nepodařilo najít. Buď jsou možnosti modifikovatelnosti značně omezené, nebo se pravidla nekontrolují vůbec, což znemožňuje nasazení počítačově řízeného protivníka. Vymysleli jsme tedy vlastní systém pravidel. Naše řešení umožňuje uživateli, aby si vytvořil vlastní hru s pravidly. Pravidlo se skládá z podmínky a sekvence akcí, které může hráč zahrát, pokud je podmínka splněna. V podmínkách můžeme používat rovnice a nerovnice s proměnnými, které se zjistí ze hry. Systém tak přesně ví, jaké tahy může hráč provést a celou dobu jej kontroluje. Takové řešení umožňuje i nasazení počítačově řízeného protivníka, který se systému dotáže, jaké tahy může provést, a počítač si dle vlastního algoritmu vybere. Tento systém jsme implementovali v aplikaci, která se skládá z editoru pravidel, serveru, klienta a dvou botů (počítačových protivníků). Porovnali jsme bota vybírajícího první dostupnou možnost s botem používajícím heuristiku a ukázali jsme, že první varianta je ve skutečnosti vhodnější. Systém pravidel nám totiž umožňuje pomocí ekvivalentních úprav pravidel a jejich pořadí určovat, které tahy jsou nejvýhodnější. Dále jsme pomocí našeho systému vytvořili tři hry – lízaný Mariáš, Prší a Srdce. Aplikace umožňuje hraní vlastních klasických karetních her jak po síti, tak proti počítači, čímž jsme splnili kladené požadavky. Pro pokrytí větší množiny her by bylo vhodné zakomponovat možnost tvorby vlastních hromádek na stole, díky návrhu systému nečiní rozšíření o tuto funkčnost problém.
35
Seznam použité literatury [1] BrainKandy.org: Generic Card Game Server http://www.brainkandy.org/apps/gcgs/ [2] RL Software: Virtual Deck http://www.rlsoftwares.com/baralhovirtual/ [3] Microsoft: .NET Framework 4 Client Profile http://www.microsoft.com/cs-cz/download/details.aspx?id=24872 [4] Microsoft: .NET Framework 4 http://msdn.microsoft.com/en-us/library/vstudio/w0x726c2(v=vs.100).aspx [5] Microsoft: SQL Server Compact Books Online Home Page http://msdn.microsoft.com/en-us/library/bb734673.aspx [6] Microsoft: LINQ (Language-Integrated Query) http://msdn.microsoft.com/en-us/library/vstudio/bb397926(v=vs.100).aspx [7] Microsoft: Windows Presentation Foundation http://msdn.microsoft.com/en-us/library/ms754130(v=vs.100).aspx [8] Microsoft: XAML Overview (WPF) http://msdn.microsoft.com/en-us/library/ms752059(v=vs.100).aspx [9] Microsoft: Choosing Communication Options in .NET http://msdn.microsoft.com/en-us/library/8119f66k [10] Microsoft: Windows Communication Foundation http://msdn.microsoft.com/en-us/library/dd456779(v=vs.100).aspx [11] Microsoft: .NET Remoting http://msdn.microsoft.com/en-us/library/72x4h507(v=vs.100).aspx
36
Seznam tabulek 1.1 1.2 1.3 1.4 1.5
Předdefinované proměnné . . . . . Předdefinované proměnné pro štych Operátory u podmínek . . . . . . . Místa s kartami . . . . . . . . . . . Akce . . . . . . . . . . . . . . . . .
. . . . .
8 9 9 10 11
2.1 Tabulka s kódy barev a hodnot . . . . . . . . . . . . . . . . . . .
16
3.1 Události . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
33
37
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
Seznam obrázků 1.1 UML diagram systému . . . . . . . . . . . . . . . . . . . . . . . . 2.1 2.2 2.3 2.4
Ukázka Ukázka Server Klient
pravidel akcí pro . . . . . . . . . .
pro hru Prší . hru Prší . . . . . . . . . . . . . . . . . . .
3.1 3.2 3.3 3.4
Diagram nastavení . . . . Diagram herních entit . . Stavový diagram hry . . . Komunikace mezi serverem
. . . a
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
18 19 19 21
. . . . . . . . . . . . . . . . . . klientem
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
24 26 27 27
38
. . . .
. . . .
. . . .
6
Seznam použitých zkratek IP LINQ MD5 MSDN TCP UML WCF WPF XAML XML
Internet Protocol Language Integrated Query Message-Digest algorithm Microsoft Developer Network Transmission Control Protocol Unified Modeling Language Windows Communication Foundation Windows Presentation Foundation Extensible Application Markup Language Extensible Markup Language
39
Přílohy Instalační CD Přílohou této práce je CD, na kterém se nacházejí následující soubory: • text.pdf – obsahuje text této práce • doc.chm – obsahuje programátorskou dokumentaci • dotNetFx40 Client x86 x64.exe – spustí instalaci Microsoft .NET Framework 4 Client Profile [3] • setup.exe – spustí instalační program pro platformu Microsoft Windows • setup.msi – spustí instalační balíček pro platformu Microsoft Windows • src – v této složce se nachází zdrojové kódy programu, dynamické knihovny třetích stran potřebné ke spuštění programu a tři hry vytvořené pomocí editoru her (lízaný Mariáš, Prší, Srdce)
40