PHP triky

Weblog o elegantním programování v PHP pro mírně pokročilé

Hra pro děti: Monopoly

Školení, která pořádám

ukázka ze hryS dětmi jsem naprogramoval další hru: Monopoly. Hra je podle pravidel verze, kterou jsme dostali někde v sámošce. Herní plán byl strašně malý, figurky z něj padaly a písmena byla sotva čitelná. Proto jsme se rozhodli hru přesunout na počítač, čímž jsme navíc oproštěni od monotónních činností jako házení kostkou, posouvání figurky nebo počítání částek. Můžeme se tak soustředit na to, co je na hře nejzábavnější – uvážené investování (hlavně v menším počtu hráčů nelze kupovat vše, protože vám dojdou peníze a nezbude na nákup nemovitostí, které políčka zhodnocují) a obchody mezi sebou. Pokud už jsou navíc „karty rozdány“ a nikdo nechce nic prodávat, tak jde hru jednoduše dosimulovat a zjistit, kdo vyhraje. Pravidla jsou oproti stolní verzi trochu upravená, někdy záměrně, jindy z lenosti:

Ke zdrojovým kódům mám několik postřehů:

  1. Samotného mě překvapilo, jak volně se prolínají data a funkce. Např. pole karet s náhodami je definované takto: [goToNearestRail, goTo.bind(this, 11), function get50FromEveryone(player) { /* ... */ }, ...]. Hodnoty v poli jsou funkce, které se při vytažení karty prostě zavolají. V jiném jazyce bych asi zvolil jiné řešení, ale v JavaScriptu mi to přišlo přirozené.
  2. Dost mi chyběla normální dědičnost, která je k dispozici až v ES6. Zazdít staré prohlížeče nebo používat emulaci se mi nechtělo, tak jsem to nakonec vyřešil definicí globální funkce a jejím zavoláním např. pomocí visitRailOrService.call(this, this.amounts, player), ale dvakrát nadšený z toho nejsem.
  3. Animaci pohybu figurek jsem vyřešil triviálním transition: top 1s, left 1s. Ještě jsem chtěl udělat, aby figurka v případě zahájení dalšího tahu skočila na cílové místo a znovu se začla animovat tam odtud, to se mi ale nepovedlo.
  4. V kódu hodně používám bind, což si vynucuje pořadí parametrů funkcí. Např. funkci earn bych měl radši se signaturou earn(player, amount). Ale vzhledem k tomu, že ji často používám jako earn.bind(this, 50), což pak volám s argumentem player, tak musí být pořadí parametrů obráceně.

Hru si můžete zahrát online. Ještě se chystám udělat Dostihy a sázky, které se hrají velmi podobně, což by mohlo pomoci s oddělením herního enginu od herních dat. Dodělal jsem také Dostihy a sázky.

Jakub Vrána, Výuka, 21.4.2017, diskuse: 11 (nové: 11)

Adminer 4.3.0

Po delší době vydávám novou verzi Admineru, která přidává novou funkčnost a ne jen opravuje bezpečnostní chyby nebo doplňuje překlady. Vyřídil jsem všechny pull-requesty a bugy (pokud se netýkaly ovladačů, které sám nepoužívám), některé novinky jsem dodělal sám. Přehled změn:

Adminer můžete stáhnout na domácí stránce nebo na GitHubu.

Jakub Vrána, Adminer, 14.3.2017, diskuse: 10 (nové: 10)

Google Cloud Spanner

Google nedávno veřejně nabídl databázi Spanner, kterou masivně používá i interně. Co mě na tomto úložišti zaujalo:

Jakub Vrána, Seznámení s oblastí, 16.2.2017, diskuse: 2 (nové: 2)

Hry pro děti

Moje děti si občas chtějí zahrát nějakou hru. Tak už jsem jim několik her naprogramoval, občas i s jejich pomocí. Nešlo o to vytvořit něco dokonalého, ale rychle spíchnout něco, co bychom si mohli hned zahrát a snadno tomu přidávat další vlastnosti. Jednou jsme třeba někam letěli, tak jsem bez dostupnosti Internetu naprogramoval hru, kterou jsme se po zbytek letu bavili. Docela mě překvapilo, že celkem hratelná hra se vejde třeba i do 100 řádek JavaScriptu. Naštěstí jsem nemusel řešit, aby hry fungovaly ve všech prohlížečích, ale v dnešní době i tak nejspíš budou.


ukázka ze hryPrší

Klasická hra pro dva a více hráčů. Každý hráč hraje na svém zařízení, aby si vzájemně nekoukali do karet. Hry spolu komunikují pomocí WebSocket, což zajišťuje okamžitou viditelnost událostí na všech zařízeních bez nutnosti neustálého dotazování se serveru. Tím pádem je hra i trochu složitější na zprovoznění – nejprve je potřeba spustit server, pak ve zdrojáku nastavit, na jaké běží adrese a pak teprve otevřít hry v prohlížeči. Pro WebSockets jsem použil knihovnu Ratchet.


ukázka ze hryČlověče, nezlob se

Hra pro čtyři hráče. Pamatuji si, že se část stavu hry načítá přímo z DOMu, což je velmi krkolomné. Když bych hru programoval znovu, tak si veškerý stav uložím ve vlastních strukturách a do DOMu ho jenom promítám, nejspíš pomocí Reactu.


ukázka ze hryHad

Jednoduchá hra pro tři hráče. Po obrazovce lezou hadi, kteří žerou jídlo. Každý had může prolézt sám sebou, ale nesmí narazit do jiného hada, jinak chcípne a promění se v jídlo. Had může zrychlit, což ho zkracuje.

VlevoVpravoZrychlení
Hráč 1
Hráč 2ZXA
Hráč 3NMB

Hadi jsou tvořeni jednotlivými body, což může vést k trhanosti v pozdější fázi hry, kdy jsou hadi dlouzí. Při programování jsem musel oprášit velice pokročilou matematiku (goniometrické funkce):

snake.x += Math.cos(snake.angle / 180 * Math.PI) * snake.speed;
snake.y += Math.sin(snake.angle / 180 * Math.PI) * snake.speed;

ukázka ze hryMotokáry

Hra pro dva kráče. Po trati jezdí motokára. Když narazí do svodidel, tak zastaví. Vzájemně sebou motokáry můžou projíždět, aby se hráči neblokovali. Jde o to zajet nejrychlejší kolo.

Na čtverečkovaný papír jsem nakreslil trať, kterou jsem pak ručně přeťukal do kódu. Funkce arcTo, která se k tomu především používá, má dost nepříjemné API – přijímá bod, ke kterému má vyrazit rovná čára a druhý bod, ke kterému má čára zatočit. Obrázek v dokumentaci to hezky znázorňuje – ani jeden z bodů přijímaných funkcí na výsledné čáře není. Mnohem intuitivnější by bylo API, které by přijímalo cílový bod a poloměr křivky. Trať jsem kreslil pomocí API proto, že jsem si myslel, že křivky následně využiji pro detekci kolizí. Tu jsem ale nakonec vyřešil pomocí barvy bodů, takže jsem trať mohl hře předat i formou obrázku.

VlevoVpravoZrychleníBrzda/couvání
Hráč 1
Hráč 2ZCSX

Hra kontroluje, jestli motokára projela checkpointem v polovině trati, ale jinak lze trať projet oběma směry. Rychlost motokáry je pečlivě nastavena tak, aby se celá trať dala projet na plný plyn. Detekce kolizí: ctx.getImageData(x, y, 1, 1).data[0] != 255.


Monopoly

Viz samostatný článek.

Jakub Vrána, Výuka, 24.10.2016, diskuse: 2 (nové: 2)

Ukládání dat do cookie

Na mobilním telefonu se běžně stává, že vypadne připojení k Internetu, a při ukládání dat na server bychom s tím měli počítat. Pokud data ukládáme běžným formulářem obnovujícím stránku, tak by se o to měl postarat prohlížeč – zobrazí informaci o nedostupnosti připojení a při jejím obnovení se data pošlou znovu. Pokud ale data odesíláme AJAXem, tak se o to musíme postarat sami.

Možné řešení spočívá v tom, že si data na klientu uložíme do cookie, která se při dalším úspěšném požadavku automaticky pošle na server. S knihovnou js-cookie můžeme cookie nastavit pomocí Cookies.set('save', data). Po uložení dat v cookie ji na straně serveru smažeme: setcookie("save", ""). Do cookie lze typicky uložit jen 4 KB dat, takže pro větší objemy dat se toto řešení bohužel nehodí.

Pokud je uživatel k aplikaci přihlášen a chceme, aby se změna dat projevila na všech jeho zařízeních (případně jde o změnu, která ovlivňuje i ostatní uživatele), tak je vhodné na server ihned odeslat požadavek pro uložení dat v cookie. Jen pokud se nepovede, tak data v cookie zůstanou a uloží se při dalším požadavku na server. Uživatele je v tom případě vhodné informovat o tom, že data ještě nejsou na straně serveru uložena.

Pokud se uložení dat má projevit jen v současném prohlížeči uživatele a nikde jinde (např. košík nepřihlášeného uživatele), tak extra požadavek na server ani posílat nemusíme a stačí počkat na přenos dat při dalším normálním požadavku.

A pokud stránku sestavujeme až na klientu, tak data na server ani nemusíme posílat a místo do cookie je můžeme uložit do localStorage.

Jakub Vrána, Řešení problému, 29.6.2016, diskuse: 4 (nové: 4)

Starší články naleznete v archivu.

avatar © 2005-2017 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.