Sem vložte zadání Vaší práce.
České vysoké učení technické v Praze Fakulta informačních technologií Katedra softwarového inženýrství Bakalářská práce Herní server strategické tahové hry Sepulcher Václav Starý Vedoucí práce: Ing. Jiří Daněček 16. května 2012
Poděkování Na tomto místě bych rád poděkoval Ing. Jiřímu Daněčkovi za vedení mé bakalářské práce, jeho čas, ochotu a cenné rady.
Prohlášení Prohlašuji, že jsem předloženou práci vypracoval samostatně a že jsem uvedl veškeré použité informační zdroje v souladu s Metodickým pokynem o etické přípravě vysokoškolských závěrečných prací. 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, ve znění pozdějších předpisů, zejména skutečnost, že České vysoké učení technické 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 16. května 2012..................... 7
České vysoké učení technické v Praze Fakulta informačních technologií c 2012 Václav Starý. Všechna práva vyhrazena. Tato práce vznikla jako školní dílo na Českém vysokém učení technickém v Praze, Fakultě informačních technologií. Práce je chráněna právními předpisy a mezinárodními úmluvami o právu autorském a právech souvisejících s právem autorským. K jejímu užití, s výjimkou bezúplatných zákonných licencí, je nezbytný souhlas autora. Odkaz na tuto práci Václav Starý. Herní server strategické tahové hry Sepulcher: Bakalářská práce. Praha: ČVUT v Praze, Fakulta informačních technologií, 2012.
Abstract This thesis focuses on creating a multiplayer server application, used in the turn-based strategy game Sepulcher. Its major part deals with artificial intelligence algorithms for pathfinding in graphs. In order to maintain a smooth flow of the game, the thesis elaborates on the client-server communication. A design of the game logic, which allows to customize and extend the game easily, is also discussed. Keywords C# Sepulcher, Artificial intelligence, Server application, Pathfinding, Abstrakt Práce je zaměřena na tvorbu serverové aplikace umožňující hru více hráčů, pro tahovou strategickou hru Sepulcher. Její velká část je věnována algoritmům umělé inteligence zabývajících se hledáním cest v grafech. Z důvodu plynulosti hry řeší způsob komunikace mezi klientem a serverem. Není opomenut návrh herní logiky, který umožňuje hru jednoduše upravovat a rozšiřovat. Klíčová slova cest, C# Sepulcher, Umělá inteligence, Serverová aplikace, Hledání 9
Obsah Úvod 17 1 Teoretická část 19 1.1 NP problém............................. 19 1.2 Terminologie AI........................... 20 1.3 Základní strategie hledání cest.................. 21 1.4 A* a heuristická funkce...................... 23 1.5 Využití A* v PC hrách....................... 24 1.6 Genetický algoritmus........................ 26 2 Analytická část 31 2.1 Požadavky na serverovou aplikaci................. 31 2.2 Technologie C#,.NET Framework 4............... 32 2.3 Analýza herního prostředí..................... 33 2.4 Analýza A*............................. 34 2.5 Heuristiky - odhad vzdálenosti.................. 36 2.6 Jump Point Search......................... 37 2.7 Lightning-Fast A*......................... 38 2.8 Výběr algoritmu pro hledání cest................. 39 2.9 Vzhled cesty............................. 42 2.10 Komunikační protokol návrh a analýza............. 43 2.11 Návrh komunikace......................... 46 2.12 Domain model - herní logika................... 47 2.13 Domain model - serverová aplikace................ 48 3 Implementační část 51 3.1 Konvence úpravy kódu....................... 51 3.2 Implementace protokolu...................... 52 3.3 Synchroní komunikace....................... 52 3.4 Problém zvýraznění dlaždic.................... 53 11
3.5 Testování.............................. 54 3.6 Základní testování......................... 55 3.7 Testy vstupních hodnot...................... 56 3.8 Zátěžový test............................ 57 Závěr 59 Literatura 61 A Seznam použitých zkratek 63 B Obsah přiloženého CD 65 12
Seznam obrázků 1.1 Schéma agent(7)............................. 20 1.2 Procházení grafu - základní strategie.................. 22 1.3 Prohledávání iterative deeping search................. 23 1.4 Procházení grafu - A* algoritmus................... 24 1.5 Prohledaný prostor v závislosti na algoritmu............. 25 1.6 Hledání cesty v hrách.......................... 26 2.1 Ukázka převodu mapy do grafové repreazentace........... 34 2.2 Křivka rotoucí náročnosti při expanzi jednoho uzlu......... 36 2.3 Prohledaný prostor v závislosti na algoritmu............. 38 2.4 Roustoucí časová náročnost grafu v závisloti na počtu uzlů..... 41 2.5 Vzhled optimální cesty......................... 44 2.6 Kanály mezi klientem a serverem................... 47 2.7 Asynchronní kanál a fronta....................... 47 2.8 Domain model herní logiky....................... 48 2.9 Domain model serverové logiky..................... 49 13
Seznam tabulek 2.1 Složitosti datových struktur...................... 34 2.2 Algoritmy pro hledání cest a jejich optimálnost............ 40 2.3 Naměřená rychlost testovaných algoritmů............... 41 3.1 Velikost nejjednoduššího příkazu.................... 53 3.2 Porovnání velikost objektů v závislosti na formátu.......... 53 15
Úvod Práce byla vytvořena v návaznosti na předmět Počítačové hry a animace. V tomto předmětu nebyl dostatečný prostor pro téma umělé inteligence. Je to rozsáhlá vědecká disciplína, proto bude většina mé práce zaměřena na hledání cest v grafech, To je základní prvek umělé inteligence počítačových her. Práce se bude opírat o design dokument hry Sepulcher, který byl vytvořen i s hrou na předmětu Počítačové hry a animace. Návrh Sepulcher hry počítá s hrou pro více hráčů, která byla v původní verzi umožněna pouze v módu horkého křesla. Tedy v módu, kde se více hráčů střídá u jednoho počítače. Možnost hry více hráčů z různých míst na světě by měla zajistit právě serverová aplikace vytvořená v této práci. Původní hra Sepulcher, pro kterou jsem implementoval herní logiku a umělou inteligenci byla pro hraní na osobním počítači přijatelná. Serverová aplikace bude však vyhodnocovat více příchozích požadavků ve stejném okamžiku. Proto bude muset být většina kódů přepsána, jak z důvodu špatného odstínění herní logiky od prezentační vrstvy, tak špatně napsanými algoritmy pro hledání cest. Práce je rozdělena na tři hlavní úseky. V prvním části je úvod do problematiky související s umělou inteligencí a hledáním cest v grafech. Na konci této části je také uveden vše řešící genetický algoritmus, jako univerzální způsob řešení problémů za pomoci umělé inteligence. Analytická část není klasickou analýzou softwarového inženýrství. Z velké části je zaměřena na prohledávací algoritmy uvedené v části první. Je zde rozebíráno jejich uplatnění při implementaci, měří se jejich kvality a diskutuje se nad možnostmi zlepšení. V poslední části se zabývám několika zajímavými problémy, které bylo nutno řešit při implementaci a nebylo též opomenuto testování. Cílem mé práce je vytvořit serverovou aplikaci, které bude umožňovat hru více hráčů. Rád bych ukázal, jakým způsobem se přistupuje k hledání cest v grafech a hrách. Jaké algoritmy zvolit a jak je optimalizovat v prostředí, ve kterém se hra odehrává. 17
Kapitola 1 Teoretická část 1.1 NP problém NP problémy jsou takové problémy, u kterých není doposud znám algoritmus řešení s polynomiální složitostí. Problematika NP úplných problémů je celkem novou záležitostí, přicházející s moderním věkem výpočetní techniky. Poprvé byl NP úplný problém zmíněn v práci Stephna Cooka roku 1971, kterou následně ukázal na splnitelnosti logických formulích. Jedním z problémů tisíciletí (problém, za jehož vyřešení je vypsána odměna 1 mil. dolarů), je problém P=NP, tedy zda jde NP problém vyřešit na deterministickém turingově stroji v polynomiálním čase. Pokud by se tato rovnost prokázala, znamenalo by to převratné změny především v moderní kryptografii, která své šifrování staví tak, aby nalezení klíče k šifře bylo NP úplným problémem. V dnešní době můžeme pro řešení NP úplných problémů použít heuristiky. Heuristiky neprocházejí celý stavový prostor (neprocházejí všechny možnosti), ale pouze jeho části. To značně urychluje hledání přípustného řešení. Výsledkem heuristiky je pak často řešení sub-optimální. Optimální řešení je takové řešení, které dosahuje nejlepších možných výsledků. Nemusí existovat pouze jedno. Sub-optimální řešení je řešení s hodnotami, které se velice blíží řešení optimálnímu. Občas je dobré přijmou tuto variantu v případech (mimo jiné) kdy: Nalezení optimálního řešení je časově velice náročné. Je obtížné nalézt nějaké řešení (například u tvorby rozdělovníků). Nelze rozhodnou, zda řešení je optimální. 19
1. Teoretická část 1.2 Terminologie AI 1.2.1 Intelligent Agent Intelligent Agent je v umělé inteligenci nazývaná autonomní entita se schopností pozorovat a manipulovat s prostředím(1). Obrázek 1.1: Schéma agent(7). S pomocí vstupních senzorů monitoruje stav prostředí a díky aktuátoru pak tento stav může ovlivňovat. Hlavní otázkou, kterou řeší umělá inteligence, je zpracování vstupů a výstupů tak, aby agent dosáhl určitého cíle. 1.2.2 Agentí prostředí Prostředí, které agent prozkoumává, může mít různé vlastnosti, podle kterých se dělí. 20 Plně nebo částečně prozkoumatelné prostředí Plně prozkoumatelné prostředí ukazuje všechny stavy. V tomto prostředím se hrají například šachy, kde vidíme celou hrací plochu a všechny figurky. Částečně prozkoumatelné prostředí poskytuje pouze informace o některých stavech. Příkladem mohou být karetní hry, kde hráč zná pouze své karty a karty vyložené na stole. O hracích kartách jiných hráčů nemá informace. Deterministické nebo nedeterministické prostředí Deterministické prostředí má jasně daná pravidla pro přechod mezi stavy. Figurky na šachovnici mají jasně daná pravidla pohybů, kterými se můžou pohybovat. Nedeterministické prostředí obsahuje prvky náhody. Nelze tedy jednoznačně rozhodnout, do jakého stavu prostředí se přejde. Jsou to například hry s kostkami.
1.3. Základní strategie hledání cest Diskrétní nebo spojité prostředí Diskrétní prostředí se může nacházet pouze v konečném množství stavů. Spojité prostředí se může nacházet v nekonečném množství stavů. Neutrální nebo cílené prostředí Neutrální prostředí změnou svého stavu nesleduje žádný konkrétní cíl. Cílené prostředí změnou stavu nějaký cíl sleduje. Toto prostředí obsahuje většina her. Statické nebo dynamické prostředí Statické prostředí mění svůj stav pouze v závislosti na akci agenta. Dynamické prostředí může změnit svůj stav bez agentova zásahu. 1.3 Základní strategie hledání cest 1.3.1 Definice problému hledání cest Problém hledání nejkratší cesty v grafu je jeden ze základních problémů teorie grafů. S problémem hledání (nejkratší) cesty v grafu se setkáváme v mnoha různých oborech. Nemalé úsilí je mu věnováno i v počítačových hrách. Umělá inteligence, která tento problém řeší, se pohybuje v agentním prostředí, které je plně prozkoumatelné, diskrétní, deterministické a statické. Všechny dále uvedené algoritmy, které zohledňují ohodnocení hran požadují, aby cena všech hran byla větší nebo rovna 0. Problém se záporným ohodnocením hran řeší například Johnsonův algoritmus, jehož popis je v knize (2). Algoritmy rozdělují uzly prohledávaného grafu do třech tří množin. Fresh uzly, o kterých algoritmus ještě neví. Open uzly, které má algoritmus právě k dispozici. Close uzly, které algoritmus již zpracoval. Tato množina uzlů je zde proto, aby algoritmus nenavštěvoval uzly víc než jedenkrát a tak nedocházelo k redundanci cest. Pokud algoritmus expanduje uzel, pak uzel přesouvá z Open do Close množiny a do Open množiny umisťuje všechny následovníky expandovaného uzlu. 21
1. Teoretická část Obrázek 1.2: Procházení grafu - základní strategie. Čísla v uzlech udávají pořadí, v jakém jsou uzly expandovány pro: A prohledávání do hloubky B prohledávání do šířky C uniform-cost search 1.3.2 Prohledávání do šířky Základní jednoduchý přístup k nalezení cesty spočívá v procházení grafu po jednotlivých úrovních. Algoritmus se dá jednoduše implementovat pomocí FIFO fronty, která reprezentuje množinu Open uzlů. Prohledáváním grafu do šířky se nalezne optimální cesta, pokud hrany nemají ohodnocení, nebo ohodnocení hran je rostoucí funkce závislá na hloubce uzlu(1). Má však vysokou výpočetní a paměťovou náročnost. Proto je tento algoritmus použitelný pouze na problémy s malým počtem uzlů. 1.3.3 Uniform-cost search (Cheapest first) Uniform-cost search nalezne optimální řešení vždy. Bez ohledu na ohodnocení hran (které je 0 nebo kladné). Pokud je ohodnocení na všech hranách stejné nebo žádné, pak se algoritmus chová stejně jako prohledávání do šířky. Uniform-cost při hledání cesty vždy expanduje uzel s nejnižším ohodnocení cesty, která k němu vede z počátečního uzlu. Variantou Uniform-cost je Dijskrův algoritmus(17). 1.3.4 Prohledávání do hloubky Prohledávání do hloubky vždy expanduje nejhlubší uzel. Jednoduchá implementace je pomocí LIFO fronty reprezentující Open množinu. Algoritmus má menší paměťovou náročnost než prohledávání do šířky. V rozsáhlém, téměř nekonečném prostoru, je pro tento algoritmus velice obtížné najít cestu z důvodu stálého sestupování do hloubky. Také toto prohledávání grafu nezaručuje nalezení optimální cesty. Malá paměťová náročnost je 22
1.4. A* a heuristická funkce Obrázek 1.3: Prohledávání iterative deeping search velice atraktivní a proto prohledávání grafu do hloubky je často použito jako stavební kámen složitějších algoritmů. 1.3.5 Iterative deeping search U tohoto algoritmu je postup stejný jako u prohledávání grafu do hloubky s tím, že je nastaven limit na maximální hloubku, do které lze sestoupit. Pokud nebude nalezeno řešení, limit hloubky se navýší a graf je znovu prohledán. Algoritmus má malou paměťovou náročnost stejně jako prohledávání do hloubky a cesta, kterou nalezne, je optimální. 1.4 A* a heuristická funkce 1.4.1 Heuristika Heuristika se využívá v situacích, kdy je stavový prostor pro úplné procházení příliš velký nebo pro daný problém není znám algoritmus řešení. Heuristika nemusí nalézt nejlepší řešení. Často nalezené řešení je pouze přibližné, ale lze je nalézt v rozumném čase. Proto jsou heuristiky zpravidla používané při řešení NP-úplných problémů. 1.4.2 A* algoritmus Spojením Uniform-cost search s vhodnou heuristikou, která do algoritmu vnáší hlubší znalost problému, vzniká velice silný nástroj A*. A* vybírá pro expanzi takový uzel, který má nejmenší hodnotu f(x) = g(x) + h(x), kde g(x) je ohodnocení hrany, h(x) předpokládaná vzdálenost z uzlu k cíli. Funkce f(x) je tedy předpokládaná cena řešení. Na heuristiku h(x) jsou kladeny dva požadavky - přípustnost a konzistenčnost. 23
1. Teoretická část Obrázek 1.4: Procházení grafu - A* algoritmus. Černé číslice udávají cenu hrany. dé číslice udávají hodnotu heuristiky. Při procházení grafu by se po dosažení cíle šedivé uzly neexpandovaly. Heruristika h(x) je přípustná, pokud předpokládaná cena řešení je menší nebo rovna reálné ceně. Tehdy se jedná o optimistickou heuristiku. Konzistence vyžaduje, aby pro každý uzel platilo, že h(x) d(x, y) + h(y), kde d(x, y) je ohodnocení hrany mezi uzly x a y. Platí, že pokud je heuristika konzistentní, je i přípustná(1). Cesta nalezena A* je optimální, pokud jsou tyto dvě podmínky splněny. Při prohledávání stromového grafu je pro nalezení optimální cesty postačující podmínka přípustnosti. 1.4.3 Weighted A* Úpravou funkce f(x) na tvar f(x) = g(x) + εh(x), kde ε > 1 získáváme weighted A* algoritmus. Parametr ε udává jak velkou váhu má heuristika při výběru, jaký uzel bude expandován. Při této úpravě již heuristická funkce není optimistická a nalezená cesta tudíž nemusí být optimální. Za tuto cenu weighted A* nabízí v mnoha případech větší rychlost a menší paměťovou náročnost než klasický A* algoritmus. 1.5 Využití A* v PC hrách Hledání cest je základní a velice důležitý problém v PC hrách. atná realizace hledání cest je velice snadno odhalitelná. Buď nesmyslnou cestou, kterou se pohybující objekt vidá, nebo snížením výkonu z důvodu špatně zvolené strategie hledání, popřípadě špatné implementace. Velice často pro problém hledání cest je využíván A* algoritmus a jeho různé mutace. Hry kladou na hledání cest vysoké nároky na rychlost nalezení cesty a kvalitu cesty, která v některých případech může být i sub-optimální. Prohledávaný prostor je konečný, jeho velikost může být značně velká. Tomu je úměrná i časová náročnost na nalezení cesty. Hledaní cesty tedy musí 24
1.5. Využití A* v PC hrách Obrázek 1.5: Prohledaný prostor v závislosti na algoritmu. A prohledávání do šířky, které nalezne nejkratší cestu B A* algoritmus nalezne nejkratší cestu, prohledá při tom méně prostoru C weighted A*, nalezne sub-optimální cestu s nejmenším nárokem na prostor být rozděleno tak, aby proces nadměrně nezatěžoval výpočetní prostředky procesoru (tím nebrzdil průběh hry) a cesta působila přirozeným dojmem tak, že byla nalezena v jednom okamžiku. Problém můžeme rozdělit na hledání tří cest: rychlé cesty, úplné cesty a spojovací cesty. Rychlá cesta V okamžiku, kdy je po objektu vyžadováno aby se přemístil, není čas na hledání úplné cesty k cíli. Proto se nalezne pouze neúplná krátká cesta tak, aby se objekt vydal přibližným směrem k požadovanému bodu. Rychlá cesta je zde tedy proto, aby nám dala čas k nalezení cesty úplné. Úplná cesta Úplná cesta je cesta, která vede k cíli objektu. Hledání začíná tam, kde končí cesta rychlá. Její hledání je rozděleno do více průchodů herní smyčkou. Rozpracované cesty jsou ukládány do fronty, která může mít různé strategie rozdělování výpočetních prostředků. Ty se mohou rozdělovat rovnoměrně mezi všechna rozpracovaná hledání nebo podle priorit. Priorita je pak často určena dobou strávenou ve frontě. Spojovací cesta Poslední hledanou cestou je cesta spojovací. S jejím hledáním se začíná v okamžiku nalezení úplné cesty. Její počátek je aktuální pozice objektu a cílovým bodem je bod na cestě úplné. Není jasně dáno o který bod se jedná, protože nalezení spojovací cesty musí být co nejrychlejší a zároveň tato cesta musí vypadat co nejpřirozeněji. 25
1. Teoretická část Obrázek 1.6: Hledání cesty v hrách. 1.5.1 Optimalizace Pro rychlejší nalezení cesty je třeba zmenšit prohledávaný prostor. V A* algoritmu využijeme vlastnost iterative deep search. Tedy omezíme hloubku do které se A* může vnořit. Pokud nebude cesta nalezena, omezení zmírníme posunem hranice maximální hloubky a prohledávání opakujeme. Omezení hloubky může být závislé na délce cesty, ceně nebo na využité paměti. Volba správných datových struktur pro open a close množiny výrazně ovlivňuje A* algoritmus. Je důležité zaměřit se na složitost vkládání a odebírání prvků ze seznamů. Pokaždé, když se expanduje uzel, dochází k jeho odebrání prvku z open listu a je přesunut do close listu. Následovníci expandovaného uzlu jsou vloženi do open listu, proto je vhodné mít datovou strukturu se složitostí vkládání O(1). Problém je komplikován faktem, že A* expanduje uzel s nejnižší hodnotou f(n), proto by se mělo jednat o upořádanou strukturu. Řešení toho problému nabízí v knize (6), kdy před open listem je list o velikosti patnácti prvků s řazením vkládáním a obsahuje uzly s nejnižší hodnotou f(n). 1.6 Genetický algoritmus Genetický algoritmus je inspirován Darwinovou teorií evoluce. Byl vymyšlen Johnem Hollandem a kolektivem. Publikován byl roku 1975 v knize Adaption in Natural and Artificial Systems. Algoritmus je velice jednoduchý. Začíná s počáteční množinou řešení, která může být jak náhodně vybraná, tak nalezena pomocí jiných heuristik. Tato množina řešení se nazývá počáteční populace. Pro snažší aplikaci genetických operátorů se jedinci kódují. Takto zakódovaný jedinec se nazývá chromozom. Algoritmus musí být schopen jednotlivá řešení ohodnotit. K tomu účelu definujeme fitness funkci, která pro lepší řešení vrací vyšší hodnoty. Následuje selekce řešení pro křížení s pravidlem: Čím větší fitness hodnota, tím větší pravděpodobnost výběru. 26
1.6. Genetický algoritmus Nad vybranými jedinci začíná proces křížení, tedy proces, který z vybraných jedinců vytváří nová řešení. Následuje mutace, která s malou pravděpodobností pozmění nepatrně chromozom. Mutace brání příliš rychlému zjednotvárnění vlastností v populaci, ztrátě potencionálně užitečného genetického materiálu a předčasné konvergenci populace(4). Poslední krok algoritmu je vytvoření nové počáteční populace. V nové populaci buď budou pouze potomci populace stávající nebo se všechny chromozomy seřadí a do nové populace se zařadí prvních N s nejvyšší hodnotou fitness funkce. V tomto okamžiku se algoritmus vrací na začátek a postup opakuje. Algoritmus je ukončen pokud najde řešení s odpovídající fitness funkcí nebo bylo vytvořeno požadované množství generací. Algoritmus 1 Genetický algoritmus Vytvoř počáteční populaci. Ohodnoť jedince populace. while ukončující podmínka do Vytvoř novou populaci tak, že: { while Populace není kompletní do Vyber 2 jedince z Populace. Zkřiž tyto jedince - zisk minimálně jednoho potomka. Nad potomkem proveď mutaci. Ulož potomka do nové populace. end while } Nahraď starou populaci populací novou. end while 1.6.1 Příklad Najděte maximum funkce f(x) = x 2, x 0, 255. Náhodně vybraná počáteční populace: 5, 23, 71, 96. Zakódování do binárního kódu. N Binární zakódování Fitness hodnota Prav. křížení [%] 5 0000 0101 25 0,4 23 0001 0111 429 3,6 71 0100 0111 4 624 32 96 0110 000 9 216 64 Fintess se stanoví stejným předpisem jako funkce pro níž hledáme maximum, tedy x 2. Tento předpis má vypovídající hodnotu o kvalitě jedince. V případě hledání minima by byla nevyhovující a zvolila by se například 27
1. Teoretická část f(x) = 10 5 x 2. Algoritmus nyní vypočítá pravděpodobnost křížení pomocí kola štěstí. Provede se křížení mezi hodnotou 96 a 71. Bod křížení je náhodně zvolen za čtvrtým bitem. Předek Potomek N Fitness hodnota 0100 0111 0100 0000 64 4 096 0110 0000 0110 0111 103 10 609 Druhé křížení bude mezi hodnotou 96 a 23, bod křížení za třetím bitem. Předek Potomek N Fitness hodnota 011 00000 0111 0111 64 4 096 000 10111 0000 0000 0 0 Předposledním krokem algoritmu je mutace. Tedy s velice malou pravděpodobností si jedinec sám sobě neguje jeden bit. Zde je velice dobře vidět její nutnost. Pouhé křížení nám samo o sobě nikdy nevytvoří jedince, který by měl jedničku na prvním nebo čtvrtém bitu. V posledním kroku se vytvoří z potomků nová počáteční populace a algoritmus se opakuje. Uvedený příklad je pouze ilustrativní a mnohem lépe by se na řešení tohoto problému hodil například horolezecký algoritmus popsaný v (1). Ten by na rozdíl od genetického algoritmu s jistotou našel nejlepší řešení a to s výrazně menším nárokem na výpočetní prostředky. Pro účel seznámení s postupy používané v genetických algoritmech je však velice užitečný. 1.6.2 Kódování Prvním zásadním rozhodnutím při používání genetického algoritmu je volba kódování. Při špatné volbě kódování můžou vznikat redundance, které zvětšují mohutnost prohledávaného prostoru a tím snižují efektivitu algoritmu (4). Tradičně je používáno binární kódování, na které se snadno aplikují klasické genetické operátory. Pro binární reprezentaci čísel je preferován Grayův kód, v kterém se sousedící čísla liší pouze v jednom bitu. Tato vlastnost je pozitivní při mutaci, kdy malá změna chromozomu znamená malou změnu řešení. Mezi další často používané typy kódování patří permutační kódování, maticové kódování, stromové kódování nebo kódování nad abecedou znaků. Kódování je silně závislé na problému. Proto nelze jednoznačně říci, které je nejlepší nebo uvést univerzální kódování. 1.6.3 Fitness funkce Fitness funkce přiřazuje chromozomům takovou hodnotu, která reprezentuje jeho sílu v populaci. V mnoha případech se jako fitness funkce dá použít užitková funkce. Je dobré si uvědomit, že při ohodnocení populace může platit A > B > C > A (například při volbě vhodné herní strategie). Takovou situaci 28
1.6. Genetický algoritmus řeší mimo jiné turnajová fitness funkce, kde ohodnocení chromozomu je počet vyhraných turnajů (porovnání). 1.6.4 Selekce Pro selekci platí podobná pravidla jako pro volbu fitness funkce. Při špatné volbě selekce může docházet k pomalé konvergenci populace a k úpadku do lokálního extrému. Často se používá pro selekci kolo štěstí. nce na výběr pak odpovídá procentnímu zastoupení fitness hodnoty v populaci. Pokud se ale v populaci nachází pár silných jedinců (ti mohou být v lokálním extrému), ostatní jedinci nemají příliš možností pro křížení a algoritmus nalezne pouze lokální extrém. Takové situace lze řešit například ranked selection(4). Ta šanci na křížení přiděluje podle pořadí. Mnoho pravidel platících pro selekci se dá aplikovat na fitness funkci a obráceně. 1.6.5 Křížení Díky křížení se genetický algoritmus posouvá v prohledávání stavového prostoru kupředu. Jeho efektivnost velice záleží na správně zvolené fitness funkci a selekci. Části informací z dvou chromozomů vybraných selekcí jsou spojené a vytvářejí nového jedince. Časté je použití jednobodového křížení (obecně n-bodové křížení). Na křížení je kladen požadavek, aby jedinec takto vzniklý byl přípustným řešením. To v mnoha úlohách není samo o sobě splněno (úlohy plánování, obchodní cestující) a proto se na nepřípustné jedince musí aplikovat opravné algoritmy. Druhou možností je použití codeků (zobrazení), které převádějí chromozomy na přípustného jedince. 1.6.6 Mutace Mutace slouží k udržování genetické variace v populaci a tím brání, aby proces hledání sklouznul do oblasti nějakého lokálního optima. Také přináší do populace nové prvky, které by nemusely být pomocí genetického operátoru křížení v populaci přítomné (viz příklad). Podle (4) by mutace jedince měla nastat s pravděpodobností 0,001 až 0,05. Stejně jako u křížení je u některých problémů potřeba oprava zmutovaného jedince na přípustné řešení. 1.6.7 Elitismus Při vygenerování všech potomků stávající populace umírá a potomci vzniklí křížením vytvoří novou počáteční populaci. Pokud se do této populace dostanou i nejsilnější jedinci z rodičovské populace, je do genetického algoritmu zahrnut elitismus. Ten zaručuje, že doposud nejlepší řešení nebude ztraceno, zároveň však zvyšuje pravděpodobnost uváznutí v lokálním extrému. 29
Kapitola 2 Analytická část 2.1 Požadavky na serverovou aplikaci Požadavky na serverovou aplikaci Sepulcher vycházejí z design documentu (v příloze) a nároky na klientskou aplikaci. Klientská aplikace Na klientskou aplikaci jsou kladeny minimální požadavky. Nepředpokládá se, že by poskytovala jinou než prezentační funkcionalitu. To na server klade větší zatížení jak na přenos dat, tak na výpočetní prostředky. Příkladem toho může být zvýraznění dlaždic zasažené efektem. Přestože klientská aplikace by tuto operaci mohla sama vypočítat, není to po ní vyžadováno a server tuto funkcionalitu musí poskytnout. Tento fakt však brání tomu, aby různí klienti viděli různé, často nesmyslné věci (out-of-syn). Klientská aplikace bude implementována v technologii C# a frameworkem XNA a neklade žádné požadavky na komunikační protokol. Ten se tedy musí v rámci této práce navrhnout s ohledem na technologie používané klientem. Generování map Jedním ze selling-pointů uvedeným v design documentu je náhodná generace map. Mapa musí být generována tak, aby (bez modelů) byla každá místnost a dlaždice dostupná. Některé mapy nemusí být od začátku hry hned zobrazeny a jsou odhaleny až během hry. Mapa je generována pomocí semínka, které, pokud není určeno, je vygenerováno náhodně. Toto náhodné semínko je hráčům nabídnuto, aby v budoucnu mohli vygenerovat stejnou mapu. Softwarové požadavky Pro běh aplikace nejsou definovány žádné softwarové požadavky. Lze je tedy specifikovat v závislosti na použité technologii. 31
2. Analytická část Hardwarové požadavky Stejně jako u softwarových požadavků nejsou definovány. Minimální hardwarové požadavky budou stanoveny na sestavu, na které bude aplikace testována. Jednoduché spuštění hry Hráč by měl mít možnost spustit hru jedním kliknutím. Není vyžadována po hráči žádná registrace, ani logování do systému. Free-To-Play Hra má splňovat model Free-To-Play. Hraní bude zcela zdarma. Také bude mít otevřený zdrojový kód, nebude tedy možné využít žádné placené technologie. Bezpečnost Aplikace nemusí obsahovat žádné bezpečnostní prvky, protože neobsahuje žádná citlivá data o uživatelích. Zatížení Server by měl zvládnout alespoň 100 uživatelů v jednom okamžiku. 2.2 Technologie C#,.NET Framework 4 Volba programovacího jazyka je důležitou součástí analýzy. atná volba může vést k prodloužení práce nebo dokonce úplnou nemožností implementace některých částí programu. Pro tuto serverovou aplikaci byl zvolen jazyk C# veze 4.0, s.net Framework 4.0. Tento jazyk byl zvolen, protože původní Sepulcher hra je v tomto jazyce napsána a bude tedy možno využít při implementaci některé již hotové části kódu. To by ale pro volbu technologie nestačilo. Serverová aplikace bude potřebovat především dobře umět pracovat s vlákny a s komunikačními kanály. C# tyto požadavky velice dobře zvládá, navíc je okolo C# velká komunita a kvalitně zpracovaná dokumentace na Microsoft Developer Network. Pro čtenáře, který se se C# ještě nesetkal, je zde výčet základních vlastností tohoto programovacího jazyka. 32 C# je objektově orientovaný jazyk, ve kterém je vše objektem (potomkem třídy object) a to včetně primitivních datových typů. Jazyk je inspirován jazykem Java a přebírá (stejně jako Java) syntaxi jazyka C. Automatická správa paměti v podobě garbage collector. Detekce hranic polí a neinicializovaných proměnných.
2.3. Analýza herního prostředí Neexistuje zde vícenásobné dědění, které používá jazyk C++. Náhradou jsou zde interface, který předepisuje rozhraní třídy. Jednou ze zásad softwarového inženýrství je zapouzdření. Zde C# nabízí property, které se chovají jako klasický datový atribut, vně se jedná o funkce get a set. C#, stejně jako C++, má ukazatele na funkce. Jistou nevýhodou je závislost.net Frameworku 4 na platformě systému Windows. Podporované verze Windows jsou Windows XP a novější. Protože však z požadavků na aplikaci nevyplývá požadavek na platformní nezávislost nebo na konkrétné operační systém, není použití této technologie problém. Z této volby plyne požadavek na softwarové vybavení počítače, na kterém serverová aplikace poběží. 2.3 Analýza herního prostředí Pro správnou implementaci a volbu algoritmů je nutné nejprve analyzovat prostor, ve kterém se hra odehrává. Podkladem této analýzy je design document. Herní plocha se skládá ze čtvercových místností. Těch může být v jedné hře maximálně 49 49 = 2 401. Herní plocha je takto záměrně omezena, aby byla umožněna jednoduchá reprezentace dvourozměrným polem s fixní velikostí. Počet místností je tak dostatečně velký a nebude proto omezovat hráče. Tento závěr je odvozen ze stolní hry Castle Ravenfoft (touto hrou je Sepulcher inspirován), která má stejné herní prostředí. Zde se většina her odehrává na prostoru menším než 25 místností. Na každou místnost je dále kladen požadavek, aby měla alespoň jeden vstup. Také nesmí být rozdělena tak, aby se vytvořilo více místností. Je tedy jednoznačně dané z jakého směru se do místnosti dá vstoupit a také odejít. Každou místnost tvoří 4 4 = 16 čtvercových dlaždic. Každá dlaždice se může nacházet ve třech stavech. Prvním je dlaždice prázdná - tedy taková, která neobsahuje žádný model a na kterou lze vstoupit a zůstat na ní stát. Druhý typ je dlaždice s modelem. Je neprůchozí, nelze na ni vstoupit, ani umístit další jiný model. Model na této dlaždici má ale možnost z dlaždice odejít. Poslední typ je dlaždice obsahující zeď - na takovou dlaždici nelze umístit model ani dlaždicí projít. Po dlaždicích se mohou modely pohybovat do 8 směrů a to za cenu jednoho kroku. Za tuto cenu je tedy možný i pohyb po úhlopříčce, který je v reálném světě delší ( 2). Pohyb po úhlopříčce je stanoven pravidlem: Pohyb po úhlopříčce o jednu dlaždici je umožněn, pouze pokud této dlaždice lze dosáhnout dvěma kroky bez cesty po úhlopříčce. 33
2. Analytická část Obrázek 2.1: Ukázka převodu mapy do grafové repreazentace. Operace Hash tabulka Strom Priority queue φ volání 1 Insert Θ(1) O(log 2 n) O(log 2 n) 3,5 Remove Θ(1) O(log 2 n) O(log 2 n) 1 IsElement Θ(1) O(log 2 n) O(log 2 n) 3,5 FindMinimum O(n) O(log 2 n) O(1) 1 Tabulka 2.1: Složitosti datových struktur. Z uvedeného vyplývá, že herní mapa je graf, kde z nebo do každého uzlu vede maximálně 8 hran. Do uzlu reprezentující zeď nevede žádná hrana. 2.4 Analýza A* Jak už bylo popsáno v teoretické části je A* jedením ze základních algoritmů pro hledání cest v počítačových hrách. V pseudokódu jsou používány dvě datové struktury pro fresh uzly (open list) a pro již prohledané uzly (close list). Výběr struktur je velice důležitý, protože má dopad jak na rychlost hledání, tak na využité paměti. Pro tyto dvě struktury připadají v úvahu tyto možnosti uvedené v tabulce 2.1. První hledanou strukturou bude struktura pro open list. Hash tabulka má pro Inser, Remove a IsElement konstantní časovou složitost (záleží ale také na vnitřní implementaci tabulky), pro nalezení minima má však složitost O(n). Protože pro strom a priority queue budou platit stejná pravidla, budeme nadále uvažovat pouze priority queue, která má konstantní náročnost 1 Aritmetický průměr počtu volání při expanzi jednoho uzlu. 34
2.4. Analýza A* Algoritmus 2 A* algoritmus P = počáteční uzel. Výpočet ceny heuristiky a F pro P. Přidání P do open listu. while Open list není prázdný do B = uzel s nejmenší hodnotou F z open listu. if B == cíl then return B end if Přesunutí uzlu B z open listu do close listu. for all C z potomků B do Výpočet F = G + H pro uzel C. if C je v close listu then continue end if if C je v open listu s hodnotou F je větší než právě vypočítanou then Aktualizuj rodiče a cenu F. else Přidej C do open listu. end if end for end while return Cesta neexistuje. na nalezení minima. Porovnáme tedy pro jaké N je výhodnější použít hash tabulku, před prioritní frontou. Při expanzi jednoho uzlu se provede v průměrném množství uvedeném v tabulce 2.1. Tedy při expanzi hash tabulka provede 8 + n operací, zatímco priority queue 8 log 2 n + 1. Na grafu 2.4 jsou tyto funkce znázorněny. Ani jedna ze složitostí není větší než složitost lineární, což je přijatelné. Prioritní fronta dokonce dosahuje logaritmické složitosti, to z ní dělá velice silného kandidáta pro využití. Pokud se však na aplikaci prioritní fronty podíváme důkladněji zjistíme, že složitost u operace IsElement v tomto případně nebude log n ale bude lineární, tedy n. To je způsobeno faktem, že fronta je řazena pomocí hodnoty funkce F. Pokud je hledán konkrétní prvek, musíme množinu procházet až do okamžiku nalezení. Též nastává problém v okamžiku, kdy upravujeme cenu uzlu, který je již v open listu zařazen. Nejen že tento prvek musí být nalezen a upraven, ale také musí být přesunut na správné pořadí ve frontě. Snížením ceny klesne hodnota F a tím stoupne priorita tohoto uzlu. Z těchto důvodů bude pro open list vybrána hash tabulka. U close listu je situace výrazně jednodušší. Používají se zde pouze funkce IsElement a Insert. Proto i zde bude použita hash tabulka, nabízející konstantní složitost pro tyto operace. 35
2. Analytická část Obrázek 2.2: Křivka rotoucí náročnosti při expanzi jednoho uzlu. 2.5 Heuristiky - odhad vzdálenosti Dalším důležitým faktorem u A* je použitá heuristika. Ta musí být konzistentní a optimistická. Nejčastěji se v odborné literatuře lze setkat s heuristikou Manhattan distance, která je definována H = X uzel X cíl + Y uzel Y cíl. Ten ale v případě Sepulcheru nelze použít pro nalezení optimální cesty, protože se nejedná o optimistickou heuristiku. Na rozdíl od reálného světa v prostředí Sepulcher má pohyb po úhlopříčce stejnou hodnotu jako pohyb po hraně (přestože úhlopříčka má délku 2). Další používanou heuristikou je eklidovská metrika definována vzorcem H = (X uzel X cíl ) 2 + (Y uzel Y cíl ) 2. I přestože se v reálném světě jedná o heuristiku optimistickou, v našem prostředí pro ni platí stejný závěr jako u Manhattan distance. Navíc heuristika nevrací celá čísla, což by se mohlo jevit jako další nedostatek. Optimistickou a konzistentní heuristikou v prostředí Sepulcher je diagonal distance, H = max( X uzel X cíl, Y uzel Y cíl ). Zde platí stejné pravidlo a to takové, že pohyb po diagonále je stejně drahý jako pohyb do ostatních směrů. Protože je vyžadováno, aby bylo vždy nalezeno optimální řešení, bude A* používat právě tuto techniku. Díky faktu, že heuristika je optimistická, je možné některá prohledávání omezená hloubkou rovnou prohlásit za neřešitelná. Tato kontrola bude použita v okamžiku, kdy od hráče přijde požadavek na pohyb, který je omezen počtem zbývajících kroků (tedy je omezen hloubkou). V případě, že heuristika bude 36
2.6. Jump Point Search mít větší hodnotu než je počet kroků, požadavek bude zamítnut a nebude se zbytečně plýtvat prostředky serveru. 2.5.1 Více informovaná heuristika Vytvořit a navrhnout vlastní více informovanou heuristiku není triviální úkol. Heuristika, jak už několikrát bylo zmíněno, musí být optimistická a konzistentní. Navíc její výpočet musí být rychlý, aby nezatěžoval výpočetní prostředky. V úvaze nad více informovanou heuristikou byla využita skutečnost prostředí Sepulcher, že jednotlivé dlaždice jsou součástí místností (viz analýza prostředí). Místnosti tedy tvoří graf o maximální velikosti 49 49 = 2 401 uzlů (dlaždic v tomto grafu je 16 49 2 = 38 416). Tento graf by se na začátku algoritmu prohledal do šířky a posléze by hodnota heuristiky odpovídala vzdálenosti dané místnosti od místnosti s cílovou dlaždicí. Při testovací implementaci se ukázalo, že heuristika je optimistická i konzistentní, ale hodnoty, které vrací, jsou příliš malé a A* expanduje více uzlů než s použitím diagonal distance. Výsledkem analýzy A* a heuristik je rozhodnutí, že pro datové kontejnery budou použity hashovací tabulky a zvolenou heuristikou bude diagonal distance. 2.6 Jump Point Search V prostředí, ve kterém se Sepulcher odehrává a které je popsáno v předešlé části, je prostředí známé a často ve hrách používané. Jedinou jeho odchylkou je nižší cena při pohybu po úhlopříčce. Je známo, že v mnoha hrách se používá prostředí známé jako mřížka a vyhledávací algoritmus A*. Optimalizací tohoto vyhledávajícího algoritmu v prostředí mřížky se zabývá Daniel Harabor. V práci (19) využívá vlastnosti Manhattanské metriky, tedy, že cesta mezi dvěma body v mřížce je vždy stejně dlouhá, ať se vybere jakákoliv posloupnost hran mířících k bodu B. Mapa je poté rozdělena na čtverce (v případě Sepulcheru by šlo využít místnosti) a hledání vně čtverců se v problému nezabýváme. Tento způsob však lze použít pouze pokud je pohyb omezen na čtyři základní směry a proto je toto řešení pro Sepulcher nevhodné. Druhou variantou a pro Sepulcher mnohem zajímavější, je práce (20) zabývající se prořezáváním sousedních uzlů a nazvaná Jump Point Search, který upravuje A*. Prvním krokem v algoritmu Jump Point Search (JPS) je ořezávání. Z uzlu vyloučíme všechny následovníky, do kterých se lze dostat z rodiče za stejnou nebo levnější cenu. U diagonálního pohybu se jedná pouze o levnější cestu. Sousedi, kteří po tomto kroku zbudou, však ještě nejsou finální. Následuje krok nazvaný Jump (odtud jump point), při kterém ve směru potomka přeskočíme všechny uzly, které by po ořezání měly maximálně jednoho souseda. 37
2. Analytická část Obrázek 2.3: Prohledaný prostor v závislosti na algoritmu. Délka optimální cesty je 16 kroků. A JPS, expanduje 7 uzlů, což je méně než délka optimální cesty. B A*, expanduje 73 uzlů. První uzel, který nelze přeskočit, je zařazen do finální množiny potomků právě zkoumaného uzlu. V práci (20) jsou dobře popsané používané algoritmy a podmínky pro JSP a je zde uveden důkaz, že cesta nalezená JPS při optimistické heuristice, bude optimální. Na začátku práce o JPS je dán požadavek na prostředí. Vyžaduje prostředí mřížky s pohybem do 8 směrů s cenou jednoho kroku do základních směrů a 2 kroku při posunu po úhlopříčce. Fakt, že délka úhlopříčky v Sepulcheru je menší, JPS v nalezení optimální cesty neovlivní. Pouze bude expandováno více uzlů (s délkou úhlopříčky 1 lze prořezat více sousedů). Při implementaci však bude použit nezměněný algoritmus. Nalezená cesta vypadá mnohem přirozeněji a to díky tomuto lehkému znevýhodnění pohybu po úhlopříčce. 2.7 Lightning-Fast A* Jedním z algoritmů, který bude zvažován pro použití v aplikaci, bude kombinace doposud nabytých znalostí. Pro implementaci A* a použitých struktur budou využity rady z knihy (6) uvedené také v teoretické části. Bude zvolena optimistická heuristika a práce se sousedy bude podle pravidel Jump Point Search uvedené v předchozí kapitole. Tento algoritmus bude označen 38