Viliam Búr

24. 8. 2008

Plošinovka za štyri dni

Pozvali ma do poroty programátorskej súťaže Špongia. Súťaží sa v programovaní počítačových hier za niekoľko dní. Aby som sa lepšie vcítil do kože súťažiacich, vyzval som organizátora Dana Lováska na programátorský súboj. Jednotlivec proti jednotlivcovi, časový limit 4 dni, presnejšie 3½, téma "plošinovka", programovací jazyk podľa vlastného výberu. Zatiaľ sme nezohnali nezávislú porotu na vyhodnotenie výsledkov, ale na tom až tak nezáleží. Rád som si to vyskúšal, a tu sú moje dojmy a skúsenosti.

Najprv pár slov o súťaži. Špongia znamená "Športové programovanie oči neodpudzujúcich grafických interaktívnych aplikácií", čiže programovanie počítačových hier s dôrazom na nápad, hrateľnosť a estetický dojem. Súťaž vznikla v roku 2006 z iniciatívy študentov Gymnázia pre mimoriadne nadané deti v Bratislave. V roku 2007 sa zapojili aj študenti Gymnázia Jura Hronca a Gymnázia Grösslingová. Ak máte záujem pridať sa aj vy (študenti stredných alebo základných škôl), ozvite sa organizátorom, alebo v komentári k tomuto článku.

Bežné programátorské súťaže sú zamerané na písanie efektívnych algoritmov a pre laika sú nezáživné až nepochopiteľné. V Špongii je algoritmus iba prostriedkom na dosiahnutie cieľa, a hodnotia sa vlastnosti zrozumiteľné laickej (hráčskej) verejnosti. To však neznamená, že programátor má menej práce. Naopak, musí navyše spracovávať vstup z klávesnice a myši, a zobrazovať peknú animovanú grafiku. Špongie sa zúčastňujú skupiny do 6 členov. Väčšia skupina nemusí byť vždy výhodou, ťažšie sa koordinuje spolupráca. Grafikou a hudbou prispievajú aj neprogramátori. V minulom ročníku mali účastníci na vytvorenie hry 18 dní, presné zadanie sa uverejňuje na začiatku súťaže, v októbri. Témy predchádzajúcich ročníkov boli "voda" a "puzzle".

Mojou výhodou oproti účastníkom súťaže je 10 rokov profesionálnej programátorskej praxe, a predtým ďalších takmer desať rokov amatérskeho programovania. Na druhej strane, počítačovú hru som naposledy programoval tuším pred dvanástimi rokmi, v programovacom jazyku Turbo Pascal v operačnom systéme MS DOS. Odvtedy som písal prevažne skripty, knižnice, a webové rozhrania. Naštudovať si nejaké grafické rozhranie nie je veľký problém, ale času je málo a práce veľa. A vedel som, že približne deň stratím nejakými povinnosťami mimo počítača; nechcem sa však vyhovárať, účastníci súťaže zase počas nej musia chodiť do školy.

Vybral som si programovací jazyk Java. Chcel som písať v jazyku, ktorý dôkladne poznám, aby ma technické detaily nebrzdili pri písaní. Do úvahy pripadala Java alebo Perl, ale v Perli som zatiaľ nič grafické interaktívne nepísal, a Java má výborné vývojové prostredie Eclipse. V Jave neviem pracovať so zvukom, ale keďže neviem skladať hudbu, a nechcelo sa mi strácať čas hľadaním vhodne znejúcej voľne použiteľnej skladby na internete, rozhodol som sa, že hra bude bez zvuku. Síce to znižuje celkový dojem, ale snáď to vyvážim niečím iným.

Ako prvý krok som si vytvoril pracovnú plochu 800×600 pixelov, ktorá sa dá spustiť v okne aplikácie alebo na webovej stránke, a vykreslí farebný obdĺžnik. Pre dobrý pocit, že už mám prvý malý tromf proti súperovi píšucemu v C++, a pretože v Jave sa to programuje pomerne jednoducho.

Potom som si v grafickom editore MS Paint nakreslil jednoduchého panáčika: kružnica ako hlava, elipsa ako telo, čiary ako ruky a nohy. V hre som panáčika načítal zo súboru a vykreslil na obrazovku. Pridal som ovládanie pomocou šípok, takže sa pomaly vznášal vo vzduchu. Obrazovka sa aj s panáčikom prekresľovala 50× za sekundu; panáčik pritom blikal, takže som dorobil virtuálnu obrazovku, na ktorej sa vždy vymazal a nakreslil o kúsok vedľa, a až potom sa výsledok prekreslil na skutočnú obrazovku. Aby obrazovka nebola prázdna, vykreslil som na pozadí modrozelený obrázok, a niekoľko tehlových múrov poskladaných zo štvorčekových dlaždíc. Hráč sa stále voľne vznášal aj ponad múry.

Pred kreslením ďalších obrázkov som si chcel premyslieť, aký veľký má byť v hre panáčik, a ako rýchlo sa má pohybovať. Toto rozhodnutie sa dá neskôr v programe ľahko zmeniť, ale bude treba nakresliť nové obrázky a nové miestnosti, čo je strata času, a bol som už zhruba v polovici druhého dňa zo štyroch. Keď som programoval hry pred dvanástimi rokmi, posúval som vždy panáčika o jeden obrazový bod za päťdesiatinu sekundy, čím vznikal krásny plynulý pohyb. Lenže ak by mal takto prejsť 800 pixelov, trvalo by mu to 800÷50= 16 sekúnd, a to mi pripadalo priveľa. Ale keď som vyskúšal rýchlosť väčšiu ako jeden obrazový bod, ten pohyb vyzeral ako pod stroboskopom. Čo s tým?

(C) 2008 Viliam Búr viliam-bur.blogspot.com

Ak sa majú figúrky rýchlo pohybovať, asi ich nemôžem kresliť po jednotlivých bodoch. Použil som teda vektorový editor Inkscape, poskladal som si panáčika z kriviek, a nakoniec som ho exportoval ako bitmapu. A hoci som opäť nakreslil úplne jednoduché tvary, vyzeral omnoho kvalitnejšie ako predchádzajúci model. Dokonca mal na okrajoch kriviek polopriesvitné body, takže nepôsobil kockato na žiadnom pozadí. Bolo mi jasné, že budem takto kresliť všetky obrázky.

Veľkosť panáčika som si určil ako 40×80 pixelov, a terén som poskladal ako mozaiku z dlaždíc veľkosti 40×40 pixelov, čím sa plocha 800×600 pixelov akoby rozdelila na mriežku 15×20 častí. Pri vodorovnom pohybe som nechal panáčika plynulo zrýchľovať až po 7 bodov za päťdesiatinu sekundy, to mi pripadalo dosť rýchle a ešte pomerne plynulé. Do výšky skákal rýchlosťou 16 bodov, aby pri gravitačnom zrýchlení 1 bod zvládol vyskočiť o tri riadky mriežky. K týmto hodnotám som došiel skúšaním, dokiaľ mi nepripadalo, že pohyb panáčika vyzerá správne.

Okrem múrov som dorobil trávnatú zem. Musel som kvôli nej úplne prerobiť algoritmus zrážok. Múr je totiž pre panáčika nepriepustný; nemôže sa cezeň prepadnúť, prejsť ním zboku, ani ho preraziť hlavou. Trávnatá plošina je polopriepustná; ak je panáčik nad ňou, nemôže sa prepadnúť, ale ak stojí nižšie, môže chodiť popred ňu, alebo na ňu zdola vyskočiť. Múr tesne nad hlavou je ako strop, cez ktorý sa nedá prejsť a možno ho iba obísť; trávnatá plošina tesne nad hlavou je ako priečka rebríka, na ktorú sa dá vyskočiť.

Bol som príjemne prekvapený, že sa celá obrazovka stíha prekresliť päťdesiatkrát za sekundu. Môj počítač by zvládol až 65 záberov za sekundu, a to som si kúpil pred dvoma rokmi jeden z tých lacnejších (1 GB pamäte, 1,6 GHz procesor). Vedel som, že Java je pomalý jazyk, a mal som v úmysle prerobiť program, aby sa každú päťdesiatinu sekundy prekresľovali iba tie časti obrazovky, kde sa niečo pohlo; ale odkladal som túto úpravu na neskôr, a bol som prekvapený, že to ide aj bez nej. Neskôr som program aj tak upravil, len kvôli dobrému pocitu, že zbytočne neplytvám silami počítača. Až po skončení programátorského súboja a odoslaní výsledku som zistil, že som v tej úprave urobil logickú chybu, takže sa naďalej prekresľovala celá obrazovka. Nuž čo, hlavne, že to funguje. ;-)

Dorobil som nepriateľského slimáka, ktorý sa pohybuje rovnakým spôsobom ako hráč, iba o trochu pomalšie, a nemôže skákať. Dorobil som jablká, ktoré hráč môže zbierať. Mal som v úmysle neskôr dorobiť viacero nepriateľov a viacero zberateľných predmetov, ale už som nestihol. Dorobil som vznášadlo, ktoré funguje ako trávnatá zem, ale sa vodorovne pohybuje, takže panáčik po ňom musí opatrne kráčať, aby nespadol.

V posledný deň sa mi veľmi nedarilo, lebo som sa bál blížiaceho termínu, nechcelo sa mi už programovať, a tešil som sa, až to skončí a oddýchnem si. Rozšíril som obrazovku na 820×660 pixelov; na vnútornej časti 800×600 pixelov prebiehal dej hry, okolo bol rám a spodná časť, v ktorej sa mali zobrazovať pozbierané kľúče a iné špeciálne predmety. Ak panáčik pozbieral všetky jablká, dostal sa do ďalšej miestnosti; ak sa ho dotkol slimák, musel miestnosť opakovať. Dorobil som ešte úvodnú a záverečnú obrazovku. Zabralo mi to niekoľko hodín, a potom už mi nezostal čas na naprogramovanie naplánovaných kľúčov a odomykateľných dverí, takže niektoré úpravy som z hľadiska súťaže robil celkom zbytočne. Texty na začiatku a na konci hry sú uložené ako obrázky; je to plytvanie miestom na disku, ale tak sa to dalo naprogramovať najrýchlejšie. Pôvodne mali byť po slovensky a po anglicky, ale keď Inkscape nevedel správne zobraziť slovenské písmená, nechal som iba anglický text, nemal som čas hľadať riešenie.

Prišla noc, tak som narýchlo nadizajnoval päť miestností; v prvých štyroch postupne predstavujem jednotlivé herné prvky, a posledná piata je náročná, aby som hráča na rozlúčku potrápil, nech mu hra nepripadá príliš krátka. Miestnosti sú naeditované priamo v zdrojovom kóde; nemal som čas písať editor. Výsledok som zabalil a odoslal cez e-mail.

Programátorský súboj teda skončil, ale snáď si niekedy ešte nájdem čas na dokončenie tejto hry, a prípadne na nejaké ďalšie hry. Keď zvážim, čo som sa za tie roky naučil, a aké sú dnes počítače výkonné, je to veľká výzva. (Dnes by sa dala v Jave bez problémov napísať napríklad hra ako Warcraft 2.) A teším sa na Špongiu 2008, na ktorej sa síce nemôžem zúčastniť oficiálne, ale pokúsim sa ukecať nejakého grafika, aby mi pomohol s neoficiálnym mimosúťažným príspevkom.

Myslím si, že niečo podobné by si mal vyskúšať každý človek alebo tím, ktorý sa pokúša vytvoriť nejakú hru. Na internete je mnoho herných projektov, aj od slovenských autorov, ktoré začali vytvorením webovej stránky, napísaním príbehu a nakreslením ukážkových obrázkov, po ktorých nasledovali už len správy typu "pracujeme na tom, vydržte chvíľku" a prípadne o niekoľko rokov záverečná správa "momentálne na projekte nepracujeme, vošli nám do toho nejaké povinnosti, projekt sa odkladá na neurčito". Takýto športový štýl programovania neľútostne preverí vaše tímové schopnosti, nájde vaše slabé miesta, a ukáže vám, aký výsledok môžete očakávať v akom čase. Bez ohľadu na to, aký typ hry idete robiť, skúste najprv za týždeň vyrobiť kompletné mikrodemo. Oplatí sa obetovať jeden týždeň, aby ste zistili, či sa nasledujúce roky nebudete namáhať zbytočne.

Hru "Platform 2008" (lepší názov som nevymyslel) si môžete stiahnuť zo serveru SourceForge.net pod licenciou GNU GPLv3 aj so zdrojovým kódom.

11. 8. 2008

Vodná mapa pre Wesnoth, vylepšená

V predchádzajúcich dvoch článkoch sme si vytvorili scenár na súboj dvoch hráčov s názvom "2p - Pool", kde jeden hráč mohol verbovať iba morských ľudí a druhý hráč iba morských hadov a saurov. Scenár sme uverejnili na serveri Wesnothu. V poslednom článku presunieme výber medzi morskými ľuďmi a hadmi dovnútra scenára. Každý hráč si vyberie svoj tím presunutím veliterľa do jednej z dvoch začiatočných pevností; budú teda možné aj bitky morských ľudí proti morským ľuďom alebo hadov proti hadom.

Poznámka: Bolo by "prirodzenejšie" vytvoriť dve frakcie morských ľudí a hadov, a potom éru, ktorá by obsahovala tieto frakcie. Pri spustení hry pre viacerých hráčov by mohol hostiteľ vybrať túto (alebo hocijakú inú) éru pre tento (alebo hocijaký iný) scenár. Ale my to urobíme inak, pretože chceme použiť tieto frakcie iba pre tento scenár, a pretože toto je návod na programovanie udalostí v scenári.

Logika scenára sa zadáva pomocou udalostí. Udalosti sú príkazy v tvare: "keď sa stane toto, urob tamto". Na wiki stránkach Wesnothu (po anglicky) nájdete zoznam možných príkazov; v tomto článku vysvetlím iba tie, ktoré teraz použijeme.

Začnime tým, že na jednotlivé pevnosti umiestnime nápisy ako "toto je pevnosť morských ľudí" a "toto je pevnosť hadov". Použijeme konfiguračný súbor scenára "2p_Pool.cfg" z predchádzajúceho článku, a na jeho koniec pridáme udalosti.

[multiplayer]

 # ...tu su veci z predchadzajuceho clanku...

 [event]
  name=prestart
  [label]
   x,y=4,5
   text=_"Morski ludia"
  [/label]
  [label]
   x,y=4,9
   text=_"Hadi"
  [/label]
  [label]
   x,y=30,5
   text=_"Morski ludia"
  [/label]
  [label]
   x,y=30,9
   text=_"Hadi"
  [/label]
 [/event]

[/multiplayer]

Značky "[event]" a "[/event]" znamenajú "toto je začiatok / koniec popisu udalosti". Parameter "name" určuje, kedy nastane táto udalosť; hodnota "prestart" znamená "pred štartom scenára, skôr ako sa čokoľvek ukáže na obrazovke". To je správne miesto na záverečné úpravy mapy. Nasledujúce značky sú príkazy, ktoré sa majú vykonať, keď sa spustí udalosť; čiže pred začiatkom scenára.

Značky "[label]" a "[/label]" znamenajú príkaz "daj na dané políčko mapy nápis". Parametre "x" a "y" sú súradnice políčka. (Súradnice sa zobrazujú v pravom hornom rohu okna, v editore máp aj v hre.) Parameter "text" je text nápisu. Pred textom je znamienko "_", aby ho bolo možné neskôr preložiť do iných jazykov.

Toto je najjednoduchší spôsob ako napísať skript. Ale nie je ho takto jednoduché udržiavať, najmä ak bude dlhší. Nie je zrejmé, že "4,5" sú súradnice pevnosti morských ľudí hráča číslo 1; a keby sa dizajnér mapy rozhodol presunúť pevnosť na iné miesto, nebolo by jasné, ktoré časti skriptu treba aktualizovať. To sa dá vyriešiť vytvorením makra s názvom "PEVNOST_H1M", ktoré bude predstavovať umiestnenie pevnosti morských ľudí hráča číslo 1, ale stačí ho definovať na jednom mieste a môžeme ho používať viackrát. Makrá môžeme definovať v hociktorej časti skriptu, len ich musíme definovať skôr ako ich použijeme, takže toto makro môžeme dať pred udalosti, alebo na začiatok súboru. Verzia s makrami bude vyzerať asi takto:

#define PEVNOST_H1M
x,y=4,5
#enddef

#define PEVNOST_H1H
x,y=4,9
#enddef

#define PEVNOST_H2M
x,y=30,5
#enddef

#define PEVNOST_H2H
x,y=30,9
#enddef

[multiplayer]

 # ...tu su veci z predchadzajuceho clanku...

 [event]
  name=prestart
  [label]
   {PEVNOST_H1M}
   text=_"Morski ludia"
  [/label]
  [label]
   {PEVNOST_H1H}
   text=_"Hadi"
  [/label]
  [label]
   {PEVNOST_H2M}
   text=_"Morski ludia"
  [/label]
  [label]
   {PEVNOST_H2H}
   text=_"Hadi"
  [/label]
 [/event]

[/multiplayer]

Záber spustenia scenára

Ďalšia udalosť bude výber pevnosti. Napíšme kód, ako si hráč číslo 1 vyberie pevnosť morských ľudí. Keď jednotka, ktorá patrí hráčovi číslo 1 vstúpi na pevnosť morských ľudí hráča číslo 1, hráč číslo 1 dostane možnosť verbovať jednotky morských ľudí, a pevnosť hadov hráča číslo 1 sa zničí.

[multiplayer]

 # ...tu su predchadzajuce veci...

 [event]
  name=moveto
  [filter]
   side=1
   x,y=4,5
   [filter_location] 
    terrain=Khw
   [/filter_location]
  [/filter]
  first_time_only=yes

  [set_recruit]
   side=1
   recruit=Merman Fighter,Merman Hunter,Mermaid Initiate
  [/set_recruit]
  [label]
   x,y=4,9
   text=""
  [/label]
  [terrain]
   x= 3, 3, 4, 4, 4, 5, 5
   y= 9,10, 8, 9,10, 9,10
   terrain=Wo
  [/terrain]
  [terrain]
   x,y=4,7
   terrain=Ww
  [/terrain]
 [/event]

[/multiplayer]

Udalosť typu "moveto" nastane vtedy, keď sa jednotka presunie do situácie popísanej v značke "[filter]". (Táto jednotka musí prísť na to políčko, nestačí keď cezeň iba prechádza.) Pretože takáto situácia môže nastať mnohokrát, parameter "first_time_only" určuje, či sa udalosť vykoná iba keď situácia nastane prvýkrát, alebo pri každej takejto situácii. My chceme vybrať pevnosť iba raz.

Značky "[filter]" a "[/filter]" ohraničujú popis situácie, pri ktorej má nastať udalosť. Parameter "side" určuje, že to bude po pohybe jednotky patriacej hráčovi číslo 1. Parametre "x,y" určujú, že to bude, keď sa táto jednotka presunie na políčko 4,5 (pevnosť morských ľudí hráča číslo 1). Značka "[filter_location]" určuje ďalšie podmienky ohľadom terénu, na ktorý sa jednotka posunie; musí to byť terén "Khw", čo je kód pre vodnú pevnosť. (Prečo testujeme, či je na políčku vodná pevnosť, keď už vieme, že je to políčko 4,5? Týmto trikom zabránime výberu už zničenej pevnosti. Ak si hráč číslo 1 vyberie pevnosť hadov, pevnosť morských ľudí sa zničí; ak neskôr nejaká jednotka príde na políčko 4,5, nebude tam terén vodnej pevnosti, takže sa táto udalosť nespustí.)

Značky "[set_recruit]" a "[/set_recruit]" znamenajú príkaz nastaviť zoznam verbovateľných typov jednotiek; hráč číslo 1 bude môcť verbovať iba jednotky morských ľudí. Značky "[label]" a "[/label]" umiestnia nápis na políčko mapy; v tomto prípade nahradia už existujúci nápis ("Hadi" na pevnosti hadov hráča číslo 1) prázdnym textom, čiže vlastne vymažú nápis.

Značky "[terrain]" a "[/terrain]" znamenajú príkaz na zmenu terénu mapy; "x,y" je pozícia políčka, ktoré sa má zmeniť a "terrain" je kód nového terénu. Môžeme zadať viacero políčok naraz, ak uvedieme zoznam x-ových súradníc a zoznam zodpovedajúcich y-nových súradníc; čiže políčka v uvedenom príklade sú 3,9, 3,10, 4,8, 4,9, 4,10, 5,9, a 5,10. Kód terénu "Wo" je hlboká voda, chceme druhý hrad poriadne potopiť. Kód terénu "Ww" je plytká voda, chceme iba zrušiť most na štarte.

Aj tento kus programu by sa jednoduchšie udržiaval, keby využíval makrá:

[multiplayer]

 # ...tu su predchadzajuce veci...

 [event]
  name=moveto
  [filter]
   side=1
   {PEVNOST_H1M}
   [filter_location] 
    terrain={TEREN_PEVNOST}
   [/filter_location]
  [/filter]
  first_time_only=yes

  [set_recruit]
   side=1
   recruit={JEDNOTKY_MORSKE}
  [/set_recruit]
  [label]
   {PEVNOST_H1H}
   text=""
  [/label]
  [terrain]
   {HRAD_H1H}
   terrain={TEREN_VODA_HLBOKA}
  [/terrain]
  [terrain]
   {START_H1}
   terrain={TEREN_VODA}
  [/terrain]
 [/event]

[/multiplayer]

Potrebujeme 4 takéto udalosti: hráč číslo 1 si vyberá pevnosť morských ľudí, hráč číslo 1 si vyberá pevnosť hadov, hráč číslo 2 si vyberá pevnosť morských ľudí, hráč číslo 2 si vyberá pevnosť hadov. Mohli by sme urobiť 4 kópie tejto udalosti a zmeniť, čo treba. Alebo môžeme vytvoriť makro (s názvom VYBER_TIM) využívajúce parametre. Makrá stačí napísať raz a môžeme ich použiť viackrát. Parametre sú tie časti makra, ktoré sa menia.

V tomto príklade sú premenlivé časti makra: číslo hráča, 1 alebo 2 (nazvime ho HRAC); súradnice zvolenej pevnosti (nazvime ju PEVNOST); zoznam jednotiek na verbovanie (JEDNOTKY); súradnice tej druhej pevnosti, odkiaľ vymažeme nápis (INA_PEVNOST); súradnice toho druhého hradu vrátane pevnosti, ktoré potopíme (INY_HRAD); a štartová pozícia daného hráča (START). Takže makro na túto udalosť bude vyzerať takto:

#define VYBER_TIM HRAC PEVNOST JEDNOTKY INA_PEVNOST INY_HRAD START
 [event]
  name=moveto
  [filter]
   side={HRAC}
   {PEVNOST}
   [filter_location] 
    terrain={TEREN_PEVNOST}
   [/filter_location]
  [/filter]
  first_time_only=yes

  [set_recruit]
   side={HRAC}
   recruit={JEDNOTKY}
  [/set_recruit]
  [label]
   {INA_PEVNOST}
   text=""
  [/label]
  [terrain]
   {INY_HRAD}
   terrain={TEREN_VODA_HLBOKA}
  [/terrain]
  [terrain]
   {START}
   terrain={TEREN_VODA}
  [/terrain]
 [/event]
#enddef

Makro použijeme takto:

#define JEDNOTKY_MORSKE
Merman Fighter,Merman Hunter,Mermaid Initiate#enddef

#define JEDNOTKY_HADIE
Naga Fighter,Saurian Augur,Saurian Skirmisher#enddef

#define TEREN_PEVNOST
Khw#enddef

#define TEREN_VODA
Ww#enddef

#define TEREN_VODA_HLBOKA
Wo#enddef

#define START_H1
x,y=4,7
#enddef

#define START_H2
x,y=30,7
#enddef

#define PEVNOST_H1M
x,y=4,5
#enddef

# ...tu su ostatne pevnosti...

#define HRAD_H1M
x= 3, 3, 4, 4, 4, 5, 5
y= 5, 6, 4, 5, 6, 5, 6
#enddef

#define HRAD_H1H
x= 3, 3, 4, 4, 4, 5, 5
y= 9,10, 8, 9,10, 9,10
#enddef

#define HRAD_H2M
x=29,29,30,30,30,31,31
y= 5, 6, 4, 5, 6, 5, 6
#enddef

#define HRAD_H2H
x=29,29,30,30,30,31,31
y= 9,10, 8, 9,10, 9,10
#enddef

[multiplayer]

 # ...tu su predchadzajuce veci...

#define VYBER_TIM HRAC PEVNOST JEDNOTKY INA_PEVNOST INY_HRAD START
 # ...viete co...
#enddef

{VYBER_TIM 1 {PEVNOST_H1M} {JEDNOTKY_MORSKE} {PEVNOST_H1H} {HRAD_H1H} {START_H1}}
{VYBER_TIM 1 {PEVNOST_H1H} {JEDNOTKY_HADIE}  {PEVNOST_H1M} {HRAD_H1M} {START_H1}}
{VYBER_TIM 2 {PEVNOST_H2M} {JEDNOTKY_MORSKE} {PEVNOST_H2H} {HRAD_H2H} {START_H2}}
{VYBER_TIM 2 {PEVNOST_H2H} {JEDNOTKY_HADIE}  {PEVNOST_H2M} {HRAD_H2M} {START_H2}}

[/multiplayer]

(C) 2008 Viliam Búr viliam-bur.blogspot.com

Ak ste sa stratili v tých značkách "...", na konci článku nájdete napísaný celý kód scenára. (Len sa snažím tento článok skrátiť, aj keď mi to veľmi nejde.) Pointa je, že makro bez parametrov sa volá takto: "{MOJE_MAKRO}", a makro s parametrami sa volá takto: "{MOJE_MAKRO PARAMETER1 PARAMETER2}". Ako parameter makra môžeme použiť aj iné makro a vyzerá to takto: "{MOJE_MAKRO {INE_MAKRO}}".

Niektoré makrá majú "#enddef" napísané tesne za textom. Ak to napíšeme takto, nedostane sa do makra znak "koniec riadku". Robím to pri makrách, ktoré tvoria iba časť riadku v skripte; mohol by som potom dať do jedného riadku viacero takýchto makier, napríklad "recruit={JEDNOTKY_MORSKE},{JEDNOTKY_HADIE}". Ak makro obsahuje celé riadky skriptu, nezáleží na tom, či je tam nejaký "koniec riadku" navyše, lebo prázdne riadky sa ignorujú.

Záber zvolenia pevnosti morských ľudí

V tejto chvíli je scenár hotový. Jediný problém je, že AI (umelá inteligencia, čiže "Počítačový hráč" vo Wesnothe) nášmu systému nerozumie, takže z jeho pohľadu sú naše pevnosti iba obyčajné navzájom rovnocenné pevnosti; a z nejakých technických dôvodov si teda vždy vyberie tú severnejšiu. Pridáme teda nejakú logiku pre AI.

Nemôžeme AI vysvetliť náš systém a očakávať, že pochopí, čo z toho vyplýva, a vymyslí si príslušnú stratégiu. (Ak vie niekto naprogramovať takú chytrú AI, nech prosím kontaktuje programátorov Wesnothu.) Namiesto toho použijeme trik: vyberieme si na začiatku ťahu niektorú pevnosť a zničíme tú druhú, takže AI si potom prirodzene "vyberie" tú zostávajúcu (čím spustí horeuvedenú udalosť).

Algoritmus na výber je takýto: vyberieme si náhodnú pevnosť. Čože, očakávali ste niečo múdrejšie? No tak dobre: ak je počítačový hráč hráčom číslo 1, vyberie si náhodne. Ak je počítačový hráč hráčom číslo 2, vyberie si opak toho, čo si vybral hráč číslo 1. Ale ak si hráč číslo 1 počas prvého ťahu nevybral žiadnu pevnosť (čo je čudné, ale možné), vyberie si aj počítačový hráč číslo 2 náhodne. Povedzme to trochu inými slovami: Počítačový hráč si vyberie pevnosť náhodne, ale počítačový hráč číslo 2 si vyberie opak toho, čo si vybral hráč číslo 1, ak si hráč číslo 1 niečo vybral.

Budeme potrebovať udalosť "ai turn", ktorá sa spúšťa na začiatku ťahu počítačového hráča. Budeme však používať náhodné čísla, a z nejakých technických dôvodov je zakázané generovať náhodné čísla počas udalosti "ai turn", takže využijeme ešte udalosť "side turn", ktorá sa spúšťa na začiatku ťahu každého hráča (skôr ako "ai turn"). V udalosti "side turn" vygenerujeme náhodné číslo a uložíme ho do premennej; v udalosti "ai turn" toto číslo použijeme, alebo ho budeme ignorovať, ak je na ťahu človek.

Náhodné číslo uložíme do premennej s názvom "vyber"; bude to číslo 1 alebo 2, ale aby sa nám skript ľahšie čítal, vytvoríme si makrá VYBER_MORSKE a VYBER_HADIE s hodnotami 1 a 2, takže si nemusíme pamätať, ktoré číslo znamená výber ktorých jednotiek.

Použijeme nové značky "[set_variable]" na uloženie hodnoty do premennej; "[if]" a "[then]" na rozhodnovanie (ak, tak); "[variable]" na testovanie, či dané premenná obsahuje danú hodnotu; "[have_location]" na testovanie, či sa na danom políčku nachádza (alebo nenachádza, ak použijeme znamienko "!") daný terén. Použijeme aj premenné udalosti "turn_number" na zistenie, či je to prvý ťah, a "side_number" na zistenie, či je to hráč číslo 2.

V iných programovacích jazykoch by náš algoritmus vyzeral nejako takto. (Toto je iba vymyslený pseudokód na základe nejakých jazykov.)

keď ŤaháHráč (ťah) {
 ak ťah == 1 {
  výber := náhodné(1..2)
 }
}

keď ŤaháAI (ťah, hráč) {
 ak ťah == 1 {

  ak  hráč == 2  a  terén(PEVNOST_H1M) != TEREN_PEVNOST  {
   výber := VYBER_MORSKE
  }
  ak  hráč == 2  a  terén(PEVNOST_H1H) != TEREN_PEVNOST  {
   výber := VYBER_HADIE
  }

  znič_pevnosť(hráč, nie výber)
 }
}

A toto je hotový skript scenára:

#define JEDNOTKY_MORSKE
Merman Fighter,Merman Hunter,Mermaid Initiate#enddef

#define JEDNOTKY_HADIE
Naga Fighter,Saurian Augur,Saurian Skirmisher#enddef

#define TEREN_PEVNOST
Khw#enddef

#define TEREN_VODA
Ww#enddef

#define TEREN_VODA_HLBOKA
Wo#enddef

#define START_H1
x,y=4,7
#enddef

#define START_H2
x,y=30,7
#enddef

#define PEVNOST_H1M
x,y=4,5
#enddef

#define PEVNOST_H1H
x,y=4,9
#enddef

#define PEVNOST_H2M
x,y=30,5
#enddef

#define PEVNOST_H2H
x,y=30,9
#enddef

#define HRAD_H1M
x= 3, 3, 4, 4, 4, 5, 5
y= 5, 6, 4, 5, 6, 5, 6
#enddef

#define HRAD_H1H
x= 3, 3, 4, 4, 4, 5, 5
y= 9,10, 8, 9,10, 9,10
#enddef

#define HRAD_H2M
x=29,29,30,30,30,31,31
y= 5, 6, 4, 5, 6, 5, 6
#enddef

#define HRAD_H2H
x=29,29,30,30,30,31,31
y= 9,10, 8, 9,10, 9,10
#enddef

#define VYBER_MORSKE
1#enddef

#define VYBER_HADIE
2#enddef

[multiplayer]

 id=multiplayer_2p_Pool
 name= _ "(2) Pool"
 description= _ "Vodna bitka medzi vodnymi ludmi a hadmi."
 map_data="{@campaigns/Examples_by_Viliam/maps/multiplayer/2p_Pool.map}"

 {DEFAULT_SCHEDULE}
 {DEFAULT_MUSIC_PLAYLIST}

 [side]
  side=1
  controller=human
  canrecruit=yes

  team_name=zapad
  user_team_name= _ "team^Zapad"
 [/side]

 [side]
  side=2
  controller=human
  canrecruit=yes

  team_name=vychod
  user_team_name= _ "team^Vychod"
 [/side]

 [event]
  name=prestart
  [label]
   {PEVNOST_H1M}
   text=_"Morski ludia"
  [/label]
  [label]
   {PEVNOST_H1H}
   text=_"Hadi"
  [/label]
  [label]
   {PEVNOST_H2M}
   text=_"Morski ludia"
  [/label]
  [label]
   {PEVNOST_H2H}
   text=_"Hadi"
  [/label]
 [/event]

#define VYBER_TIM HRAC PEVNOST JEDNOTKY INA_PEVNOST INY_HRAD START
 [event]
  name=moveto
  [filter]
   side={HRAC}
   {PEVNOST}
   [filter_location] 
    terrain={TEREN_PEVNOST}
   [/filter_location]
  [/filter]
  first_time_only=yes

  [set_recruit]
   side={HRAC}
   recruit={JEDNOTKY}
  [/set_recruit]
  [label]
   {INA_PEVNOST}
   text=""
  [/label]
  [terrain]
   {INY_HRAD}
   terrain={TEREN_VODA_HLBOKA}
  [/terrain]
  [terrain]
   {START}
   terrain={TEREN_VODA}
  [/terrain]
 [/event]
#enddef

{VYBER_TIM 1 {PEVNOST_H1M} {JEDNOTKY_MORSKE} {PEVNOST_H1H} {HRAD_H1H} {START_H1}}
{VYBER_TIM 1 {PEVNOST_H1H} {JEDNOTKY_HADIE}  {PEVNOST_H1M} {HRAD_H1M} {START_H1}}
{VYBER_TIM 2 {PEVNOST_H2M} {JEDNOTKY_MORSKE} {PEVNOST_H2H} {HRAD_H2H} {START_H2}}
{VYBER_TIM 2 {PEVNOST_H2H} {JEDNOTKY_HADIE}  {PEVNOST_H2M} {HRAD_H2M} {START_H2}}

 [event]
  name=side turn
  first_time_only=no

  [if]
   [variable]
    name=turn_number
    numerical_equals=1
   [/variable]
   [then]
    [set_variable]
     name=vyber
     rand=1..2
    [/set_variable]
   [/then]
  [/if]
 [/event]

 [event]
  name=ai turn
  first_time_only=no
  [if]
   [variable]
    name=turn_number
    numerical_equals=1
   [/variable]
   [then]

    [if]
     [variable]
      name=side_number
      numerical_equals=2
     [/variable]
     [have_location]
      {PEVNOST_H1M}
      terrain=!,{TEREN_PEVNOST}
     [/have_location]
     [then]
      [set_variable]
       name=vyber
       value={VYBER_MORSKE}
      [/set_variable]
     [/then]
    [/if]

    [if]
     [variable]
      name=side_number
      numerical_equals=2
     [/variable]
     [have_location]
      {PEVNOST_H1H}
      terrain=!,{TEREN_PEVNOST}
     [/have_location]
     [then]
      [set_variable]
       name=vyber
       value={VYBER_HADIE}
      [/set_variable]
     [/then]
    [/if]

#define PREDVYBER_TIM HRAC VYBER INA_PEVNOST
    [if]
     [variable]
      name=side_number
      numerical_equals={HRAC}
     [/variable]
     [variable]
      name=vyber
      numerical_equals={VYBER}
     [/variable]
     [then]
      [terrain]
       {INA_PEVNOST}
       terrain={TEREN_VODA}
      [/terrain]
     [/then]
    [/if]
#enddef

{PREDVYBER_TIM 1 {VYBER_MORSKE} {PEVNOST_H1H}}
{PREDVYBER_TIM 1 {VYBER_HADIE}   {PEVNOST_H1M}}
{PREDVYBER_TIM 2 {VYBER_MORSKE} {PEVNOST_H2H}}
{PREDVYBER_TIM 2 {VYBER_HADIE}   {PEVNOST_H2M}}

   [/then]
  [/if]
 [/event]

[/multiplayer]

Tento scenár (po anglicky, s nejakými vylepšeniami, ktoré v článkoch nespomínam) si môžete stiahnuť ako doplnok k Wesnothu "Examples by Viliam".

Wesnoth 1.4 žiaľ obsahuje chybu v programe, kvôli ktorej má AI problém verbovať jednotky z určitých netypických zoznamov, ako sú napríklad tieto. Počítačový hráč potom nenaverbuje žiadne jednotky, alebo ich naverbuje príliš málo. Nenechajte sa tým odradiť, berte to ako pripomienku, že hra sa stále vyvíja (napísal som autorom chybové hlásenie a snáď to jedného dňa vo verzii 1.6 opravia), a že ak robíte niečo nezvyčajné, možno budete potrebovať kontaktovať autorov hry. Čím viac sa vaše scenáre budú podobať na scenáre z hry, tým menšia je šanca narazenia na chybu. A tento scenár aspoň funguje poriadne, keď hrá človek proti človeku.

Súvisiace články:

5. 8. 2008

Reálne čísla v jazyku Java

V jazyku Java máme dva primitívne typy určené na prácu s reálnymi číslami: float a double. Ich veľkosť je 4 alebo 8 bajtov, z toho je 1 bit znamienko, 8 alebo 11 bitov exponent, a 23 alebo 52 bitov mantisa. Reálne čísla sa zaokrúhľujú na určitý počet platných číslic, čiže čím väčšie číslo, tým väčšie zaokrúhlenie. Ak teda hovoríme o ich rozsahu, máme na mysli dve nezávislé veci: na akú presnosť sa hodnoty zaokrúhľujú, a aké sú najväčšie a najmenšie možné hodnoty.

Týmto primitívnym typom zodpovedajú dva objektové typy: java.lang.Float a java.lang.Double. Ich statické premenné "MIN_VALUE" a "MAX_VALUE" určujú najmenšie a najväčšie konečné kladné reálne čísla vyjadriteľné pomocou daného typu.

System.out.println("Float " + Float.MIN_VALUE + " " + Float.MAX_VALUE);
// Float 1.4E-45 3.4028235E38
System.out.println("Double " + Double.MIN_VALUE + " " + Double.MAX_VALUE);
// Double 4.9E-324 1.7976931348623157E308

Zápis "1.4E-45" znamená "1.4 × 10-45", čiže 0.000 000 000 000 000 000 000 000 000 000 000 000 000 000 001 4. Zápis "3.4028235E38" znamená "3.4028235 × 1038", čiže 340 282 350 000 000 000 000 000 000 000 000 000 000; záverečné nuly znamenajú, že číslice na týchto miestach sa zaokrúhľujú. Podobne zápisy "4.9E-324" a "1.7976931348623157E308" znamenajú "4.9 × 10-324" a "1.7976931348623157 × 10308", čo nebudem rozpisovať, pretože výsledné čísla majú vyše 300 číslic.

Čo sa stane, ak je výsledok výpočtu príliš veľké alebo príliš malé číslo, a nezmestí sa do daného rozsahu? Oba reálne typy majú pomocné konštanty, ktoré umožňujú vrátiť aspoň nejaké výsledky aj za hranicami rozsahu výpočtu. Príliš veľké kladné a záporné čísla vyjadrujeme pomocou konštánt "POSITIVE_INFINITY" a "NEGATIVE_INFINITY", doslova "kladné nekonečno" a "záporné nekonečno". Čísla príliš blízko nule sa zaokrúhľujú na nulu (existuje aj záporná nula "-0.0f", ktorá sa rovná obyčajnej nule, okrem špeciálnych situácií ako napríklad delenie nulou). Ak o výsledku nevieme povedať vôbec nič, napríklad nekonečno mínus nekonečno alebo nula delená nulou, vyjadrujeme to pomocou konštanty "NaN". (Na rozdiel od objektovej konštanty "null" sú tieto konštanty typu "float" alebo "double", takže môžu byť uložené aj do premennej primitívneho typu.)

Ak chceme zistiť, či náš výpočet vrátil jednu z takýchto hodnôt, použijeme metódy "isInfinite" a "isNaN".

Prečo sa pri reálnych číslach používajú takéto špeciálne konštanty, keď pri celých číslach nastane za podobných okolností obyčajné pretečenie alebo "ArithmeticException"? Jedným dôvodom je zaokrúhľovanie reálnych čísel. Ak pri výpočte s celými číslami delíme nulou, zvyčajne to znamená, že programátor zabudol ošetriť nejaký špeciálny prípad; je dobré, ak ho program na túto situáciu upozorní. Ak pri výpočte s reálnymi číslami delíme nulou, často to znamená, že sme chceli deliť nejakou nenulovou hodnotou, ktorá sa pri medzivýpočte zaokrúhlila na nulu. Ošetrovanie všetkých takýchto situácií by bolo náročné, a v podstate zbytočné. Dobrý programátor predsa vie, že pri práci s reálnymi číslami sa zaokrúhľuje, takže výsledok výpočtu je vždy do istej miery nespoľahlivý; záleží od konkrétneho vzorca, nakoľko môže zaokrúhľovacia chyba narásť. Druhým dôvodom je, že spôsob uloženia reálnych čísel v pamäti umožňuje medzi ne "prirodzeným" spôsobom doplniť tieto konštanty; v prípade celých čísel by bol podobný postup "umelý", a okrem iného by extrémne spomaľoval všetky programy.

(C) 2008 Viliam Búr viliam-bur.blogspot.com

System.out.println(Float.MAX_VALUE * 2);  // Infinity
System.out.println(Float.MIN_VALUE / 2);  // 0.0
System.out.println(0.0f /  0.0f);  // NaN
System.out.println(1.0f /  0.0f);  // Infinity
System.out.println(1.0f / -0.0f);  // -Infinity
System.out.println(1.0f / Float.POSITIVE_INFINITY);  //  0.0
System.out.println(1.0f / Float.NEGATIVE_INFINITY);  // -0.0

Základné matematické operácie robíme znamienkami "+" sčítanie, "-" odčítanie, "*" násobenie, "/" delenie. Ak pri výpočtoch používame celé aj reálne čísla, znamienko "/" medzi dvoma celými číslami znamená celočíselné delenie, medzi dvoma reálnymi alebo reálnym a celým znamená reálne delenie.

System.out.println(1    / 3   );  // 0
System.out.println(1    / 3.0f);  // 0.333...
System.out.println(1.0f / 3   );  // 0.333...
System.out.println(1.0f / 3.0f);  // 0.333...

Čísla môžeme porovnávať pomocou operátorov "==" rovná sa, "!=" nerovná sa, "<" je menšie, ">" je väčšie, "<=" je menšie alebo rovné, ">=" je väčšie alebo rovné. Počas výpočtu sa reálne čísla zaokrúhľujú, takže výsledok operátora "==" môže závisieť od zaokrúhľovania. Pri dvoch reálnych číslach zvyčajne nezisťujeme ich rovnosť, ale či je ich rozdiel v danej tolerancii.

float a = 123456789f;
float b = 123000123f;
float c = 987987987f;
float x = a * b * c;
float y = c * b * a;
System.out.println(x);  // 1.5002796E25
System.out.println(y);  // 1.5002795E25

Trieda java.lang.Math ponúka množstvo metód na výpočty s celými aj reálnymi číslami: absolútna hodnota "abs" a znamienko "signum"; väčšie alebo menšie z dvoch čísel "min" a "max"; zaokrúhľovanie "floor", "ceil", "round"; mocniny "exp" a "pow", odmocniny "sqrt" a "cbrt", a logaritmy "log" a "log10"; premena uhlov zo stupňov na radiány "toRadians" a naopak "toDegrees"; goniometrické funkcie "sin", "cos", "tan", "asin", "acos", "atan", "atan2" a hyperbolické funkcie "sinh", "cosh", "tanh". Poskytuje aj matematické konštanty "E" a "PI".

Existuje aj trieda java.lang.StrictMath, ktorá ponúka rovnaké metódy, ale navyše so zárukou, že na rôznych platformách dá rovnaký výpočet vždy celkom rovnaký výsledok.

Náhodné čísla získame pomocou objektu java.util.Random. Metódy "nextFloat" a "nextDouble" vracajú náhodné reálne čísla rovnomerne rozložené medzi 0 a 1. Metóda "nextGaussian" vracia náhodné reálne čísla podľa Gaussovho normálneho rozloženia s priemerom 0 a smerodajnou odchýlkou 1; to znamená, že približne 68% hodnôt nebude od priemeru ďalej (ľubovoľným smerom) ako smerodajná odchýlka, 95% hodnôt nebude ďalej ako dve smerodajné odchýlky, a 99.7% hodnôt nebude ďalej ako tri smerodajné odchýlky. Napríklad pri IQ testoch je priemer 100 a smerodajná odchýlka 15, takže tu je simulátor výsledkov IQ testov náhodných jedincov v populácii:

java.util.Random r = new java.util.Random();
for (int i = 0; i < 100; i++) {
  System.out.println(Math.round(100 + 15 * r.nextGaussian()));
}

Súvisiace články:

Menovky:

2. 8. 2008

Celé čísla v jazyku Java

V jazyku Java máme štyri primitívne typy určené na prácu s celými číslami: byte, short, int, a long. Sú to celočíselné typy so znamienkom, s veľkosťou 1, 2, 4, 8 bajtov, čiže 8, 16, 32, 64 bitov. (Aj typ char určený na prácu s písmenami je v podstate 16-bitové celé číslo bez znamienka, a možno s ním robiť matematické operácie, ale kvôli prehľadnosti programu to neodporúčam robiť bezdôvodne.)

Základné matematické operácie robíme znamienkami "+" sčítanie, "-" odčítanie, "*" násobenie, "/" celočíselné delenie, "%" zvyšok po celočíselnom delení.

int a = 100;
int b = 6;

System.out.println(a + b);  // 106
System.out.println(a - b);  //  94
System.out.println(a * b);  // 600
System.out.println(a / b);  //  16
System.out.println(a % b);  //   4

Znamienko "+" okrem sčítania znamená aj spojenie reťazcov, a má rovnakú prioritu ako matematické "+" a "-". Ak pri vypisovaní výsledku výpočtu pripájame aj textový popis, pozor na prioritu znamienok; radšej okolo výpočtu pridajme zátvorky.

int x = 2;
int y = 3;

System.out.println("Spolu " + x + y);    // Spolu 23
System.out.println("Spolu " + (x + y));  // Spolu 5

Čísla môžeme porovnávať pomocou operátorov "==" rovná sa, "!=" nerovná sa, "<" je menšie, ">" je väčšie, "<=" je menšie alebo rovné, ">=" je väčšie alebo rovné.

int x = 2;
int y = 3;

if (x == y) {
 System.out.println("X sa rovna Y");   
}
if (x != y) {
 System.out.println("X sa nerovna Y");   
}
if (x < y) {
 System.out.println("X je mensie ako Y");   
}
if (x > y) {
 System.out.println("X je vacsie ako Y");   
}
if (x <= y) {
 System.out.println("X je mensie alebo rovne Y");   
}
if (x >= y) {
 System.out.println("X je vacsie alebo rovne Y");   
}

Každému z týchto primitívnych typov zodpovedá objektový typ: java.lang.Byte, java.lang.Short, java.lang.Integer, java.lang.Long; tieto typy sú odvodené od abstraktného typu java.lang.Number. Vďaka objektovým typom môžeme celé čísla používať tam, kde sa vyžadujú objekty, napríklad pri množinách a zoznamoch. Objektové typy poskytujú aj niekoľko užitočných statických metód. Z primitívnej hodnoty vyrobíme objekt pomocou statickej metódy valueOf; z objektu vyrobíme primitívnu hodnotu pomocou metód byteValue, shortValue, intValue, longValue.

int i = 5;
Integer j = Integer.valueOf(i);
int k = j.intValue();

System.out.println(i);  // 5
System.out.println(j);  // 5
System.out.println(k);  // 5

Jazyk Java umožňuje takzvaný autoboxing, čiže automatické dopĺňanie metód na premenu primitívnej hodnoty na objektovú a naopak. Predchádzajúci príklad by sa dal napísať aj jednoduchšie, ale znamenalo by to to isté.

int i = 5;
Integer j = i;
int k = j;

System.out.println(i);  // 5
System.out.println(j);  // 5
System.out.println(k);  // 5

S autoboxingom treba zaobchádzať opatrne. Lepšie je používať metódy explicitne, môžeme sa tak vyhnúť hľadaniu veľmi nenápadných chýb. Premenná typu "Integer" môže obsahovať hodnotu null, premenná typu "int" vždy obsahuje číslo; ak na to zabudneme, môže pri premene nastať výnimka NullPointerException.

Pri používaní typov "Integer" a "int" sa líši aj význam znamienka "==". Pre primitívne typy znamienko "==" porovnáva čísla. Pre objektové typy znamienko "==" testuje, či je to ten istý objekt. Ak teda máme dva rôzne objekty, ktoré vyjadrujú rovnaké číslo, znamienko "==" vráti, že sú rôzne; rovnosť obsahu zistíme pomocou metódy "equals". Pri dvoch rôznych primitívnych typoch, napríklad "int" a "long" znamienko "==" porovná, či obsahujú rovnaké číslo; pri objektoch "Integer" a "Long" bude metóda "equals" vždy vracať negatívny výsledok. Pri práci s číslami ako objektmi treba dávať veľký pozor, čo vlastne porovnávame.

(C) 2008 Viliam Búr viliam-bur.blogspot.com

int i = 5;
int j = 5;
if (i == j) {  // ano
 System.out.println("I a J sa rovnaju");
}

Integer I = new Integer(5);
Integer J = new Integer(5);
if (I == J) {  // nie
 System.out.println("I a J su ten isty objekt");
}
if (I.equals(J)) {  // ano
 System.out.println("I a J su rovnake");
}

int x = 5;
long y = 5;
if (x == y) {  // ano
 System.out.println("X a Y sa rovnaju");
}

Number X = new Integer(5);
Number Y = new Long(5);
if (X == Y) {  // nie
 System.out.println("X a Y su ten isty objekt");
}
if (X.equals(Y)) {  // nie!
 System.out.println("X a Y su rovnake");
}
if (X.longValue() == Y.longValue()) {  // ano
 System.out.println("X a Y sa rovnaju");
}

Rozsah jednotlivých celočíselných typov si môžeme vypočítať podľa počtu ich bitov, alebo použijeme ich statické premenné "MIN_VALUE" a "MAX_VALUE".

System.out.println("Byte " + Byte.MIN_VALUE + " " + Byte.MAX_VALUE);
// Byte -128 127
System.out.println("Short " + Short.MIN_VALUE + " " + Short.MAX_VALUE);
// Short -32768 32767
System.out.println("Integer " + Integer.MIN_VALUE + " " + Integer.MAX_VALUE);
// Integer -2147483648 2147483647
System.out.println("Long " + Long.MIN_VALUE + " " + Long.MAX_VALUE);
// Long -9223372036854775808 9223372036854775807

Ak chceme číslo zapísať do reťazca, môžeme použiť metódu "toString" (existuje aj statická) alebo pomocou znamienka "+" pripojiť číslo k reťazcu (napríklad prázdnemu). Ak to chceme urobiť v inej ako desiatkovej sústave, použijeme statickú metódu "toString" s dvoma parametrami. Základ číselnej sústavy nesmie byť väčší ako "Character.MAX_RADIX", ale väčšinou potrebujeme základy od 2 do 16, a tie sú v poriadku. Pre najčastejšie základy existujú aj statické metódy "toBinaryString", "toOctalString", "toHexString" ktoré konvertujú parameter ako celé číslo bez znamienka.

int x = 123456789;
System.out.println(Integer.toHexString(x));   //  75bcd15
System.out.println(Integer.toString(x, 16));  //  75bcd15

int y = -123456789;
System.out.println(Integer.toHexString(y));   // f8a432eb
System.out.println(Integer.toString(y, 16));  // -75bcd15

Ak chceme naopak z reťazca získať číslo, použijeme statickú metódu "parseInt" ("parseByte",...) alebo "valueOf", podľa toho, či chceme dostať primitívny typ alebo objekt. (Ak reťazec obsahuje číslo bez znamienka, získané napríklad pomocou metódy "toHexString", môže nastať prekročenie rozsahu a výnimka "NumberFormatException".)

System.out.println(Integer.parseInt("cafe", 16));  // 51966

S celými číslami môžeme robiť aj bitové operácie: "&" logický súčin (obidva bity), "|" logický súčet (aspoň jeden bit), "^" exkluzívny logický súčet (práve jeden bit). Bity môžeme posúvať pomocou operátorov: "<<" posun doľava, ">>" posun doprava so zachovaním znamienka, ">>>" posun doprava s doplnením núl.

int i = Integer.parseInt("0011", 2);
int j = Integer.parseInt("0101", 2);

System.out.println(Integer.toBinaryString(i & j));  // 0001
System.out.println(Integer.toBinaryString(i | j));  // 0111
System.out.println(Integer.toBinaryString(i ^ j));  // 0110

int k = -100;

System.out.println(Integer.toBinaryString(k));
// 11111111111111111111111110011100
System.out.println(Integer.toBinaryString(k << 1));
// 11111111111111111111111100111000
System.out.println(Integer.toBinaryString(k >> 1));
// 11111111111111111111111111001110
System.out.println(Integer.toBinaryString(k >>> 1));
// 01111111111111111111111111001110

Náhodné celé čísla získame pomocou objektu java.util.Random. Tu máme simuláciu hádzania kockou:

java.util.Random r = new java.util.Random();
for (int i = 0; i < 100; i++) {
 System.out.println(1 + r.nextInt(6));
}

Súvisiace články:

Menovky:

Informačná diéta

Je pekné leto. Mohol by som tráviť dni niekde pri vode, alebo ísť do parku s kamarátmi alebo s dobrou knihou. Namiesto toho sedím celé dni za počítačom.

Jasné, mám takú povahu. Pokiaľ sa nedeje niečo výnimočne zaujímavé, alebo pokiaľ si nepoviem "mal by som", je mi príjemnejšie nájsť si nejakú vlastnú zábavu a naplno sa jej venovať. A už asi dvadsať rokov čoraz viac tejto zábavy súvisí s počítačom. Takáto povaha má svoje veľké nevýhody, ale bolo by nečestné sťažovať sa, veď zároveň má aj veľké výhody. Čas a úsilie, ktoré som kedysi investoval do počítača ako koníčka, sa mi dnes mnohonásobne zúročujú na výplatnej páske. Za zarobené peniažky si môžem kúpiť pár vecí, ktoré robia život príjemnejším, alebo sa časti z nich zriecť a získať tým viac voľného času. Pocit, že rozumiem, ako veci fungujú, a že aj sám dokáže niečo pekné vytvoriť, tiež nie je na zahodenie. Asi by som svoju povahu za inú nevymenil, len si musím sledovať jej slabé miesta a pravidelne robiť záchranné akcie smerom k vyrovnanejšiemu spôsobu života. A to v podstate musí každý.

Strávenie slnečného leta pred monitorom mi teda v princípe neprekáža, pokiaľ si v ňom dokážem urobiť rozumný počet prestávok na niečo iné. Trápi ma iná vec. V poslednej dobe je takto strávený čas čoraz menej produktívny a príjemný. Keď som pred desiatimi alebo pätnástimi rokmi presedel celý deň pri monitore, výsledkom bola na konci dňa napísaná poviedka, alebo na konci týždňa naprogramovaná hra; a pritom som sa ešte stihol dosýta zahrať. Trávil som dni príjemne, a niečo po mne zostávalo. Pripúšťam, že tie hry neboli žiadne zázraky, ale veľa som sa pri ich písaní naučil, a myslím, že to boli sľubné kroky k vytvoreniu niečoho naozaj dobrého v budúcnosti.

Keď sa dnes končí môj počítačový deň, mám zvyčajne problém povedať, čo som vlastne celý deň robil. Najprv som si pozrel nejaké blogy, či dnes niekto napísal niečo zaujímavé a či sa pri tom rozvinula aj zaujímavá diskusia. Bol som presvedčený, že mi to zaberie len pár minút, nanajvýš hodinku, a potom pôjdem robiť niektorú z tých užitočnejších vecí, ktoré už dlho odkladám (naučím sa modelovať 3D alebo programovať v Ruby, pripravím si prednášku o Jave, nakreslím komix, napíšem scenár kampane pre Wesnoth alebo krátky návod na Perl a regular expressions, a potom možno vypnem počítač a začnem sa venovať veciam mimo neho). Prečítal som si asi päťdesiat článkov, ktoré mi pripadali zaujímavé, ale teraz si okrem jedného alebo dvoch ani neviem spomenúť, o čom boli. Pozrel som si diskusie, bolo tam pár blbcov, ktorí písali somariny, prípadne sa prišli pohádať s autorom alebo navzájom; boli tam aj rozumné komentáre, a pár veľmi dobrých a vtipných postrehov, ale tiež si už na žiaden nespomínam; pár komentárov som pridal aj ja. Prečítal som si e-maily a na niektoré som odpísal. Pozrel som si hitparády najlepších článkov; nejaká téma ma zaujala, tak som si ju našiel vo Wikipédii, kde bola pomerne prehľadne spracovaná aj s linkami na súvisiace témy. Niekto sa mi ozval cez ICQ a niečo sme riešili, tiež mi odporučil jeden vraj výborný web, ale teraz naň nemám čas, odložil som si ho do záložiek na neskôr, mám ich tam už asi sto. Medzitým sa zotmelo, zapadlo slnko, prišla polnoc, sú dve hodiny ráno a písmenká sa mi začínajú rozchádzať pred očami. Vypínam teda počítač a idem spať. V posteli si zhrniem, čo som vlastne chcel dnes urobiť, a čo som naozaj urobil. Nejako to nesedí, a tak si dávam predsavzatie, že zajtra to bude inak. Potom zaspávam. Ráno prichádzam k počítaču pevne rozhodnutý robiť niečo užitočné. Len si narýchlo pozriem blog, či sa náhodou niečo... a sakra, už je zase tma, ďalší deň preč, ďalší týždeň, ďalší mesiac... snáď takto nestrávim celý život? To rozhodne nie! Len chvíľku počkať, mám vo vedľajšom okne otvorený veľmi zaujímavý článok, dočítam ho, a hneď idem robiť niečo užitočné.

Čo s tým? Prvá dôležitá vec je, že musím niečo urobiť, ak nechcem, aby horeuvedený odstavec popisoval všetky zostávajúce dni môjho života. Druhá dôležitá vec je, že musím niečo urobiť hneď teraz; nie až o hodinu, a nie až potom, čo si prečítam pár zaujímavých článkov na úvod dňa; v týchto slepých uličkách som si už párkrát rozbil nos. Ale čo presne mám robiť? Impulzívnym riešením by bolo vypnúť počítač, obuť sandále, a vypadnúť na zvyšok dňa niekam do prírody, kde nie je elektrina. Ešte lepšie by bolo odísť niekam do prírody na týždeň alebo na mesiac. To vôbec nie sú zlé nápady, ale budem sa v tej prírode ukrývať celý zvyšok života? A ak nie, tak čo potom? Na chlieb si viem zarobiť iba pomocou počítača, takže sa k nemu skôr či neskôr vrátim. Nestačí mi iba zastaviť tento kolobeh; potrebujem aj poistku, aby sa koleso opäť nerozkrútilo.

Počítač za to nemôže. Je to len nástroj, ktorý mi kedysi slúžil na užitočné veci. Problém sa nachádza v mojej hlave. Čo ma tak vytrvalo ťahá čítať stovky a stovky článkov, z ktorých drvivá väčšina môj život vlastne nijako neobohacuje? Je to azda zvedavosť? Nie, zvedavý som predsa aj na tie veci, na ktoré si v poslednej dobe neviem nájsť čas. Zvedavosť by ma naopak odtiahla od čoraz nudnejšieho stereotypu k niečomu novému. Je to akýsi hlad; obrovský iracionálny neukojiteľný hlad po pasívnom prijímaní informácií. Vyžaduje si informácie hotové, tu a teraz; neznesie čakanie, uvažovanie, experimentovanie. Nedá sa nasýtiť žiadnym množstvom informácií; jedine keby som snáď prečítal všetko dostupné, ale to sa na internete stať nemôže. Nemá racionálny dôvod; a keď mu rozum hovorí, že takéto jednanie škodí, podhodí mu nejakú zámienku, výhovorku, ilúziu, lož. ("Iba na chvíľku." "Už len pár stránok." "Toto už bude určite dobré." "Toto je iné." "Potrebuješ byť v obraze." "Jedného dňa sa ti to môže hodiť." "Čo ak sa stane niečo dôležité, a ty o tom nebudeš vedieť?") Čiastočne má pravdu; už som sa takto dostal aj k veľmi užitočným informáciám. Na jednu užitočnú a skutočne využitú informáciu však pripadá sto potenciálne užitočných informácií, ktoré nikdy nevyužijem, tisíc informácií potenciálne užitočných pre niekoho iného, a milión zbytočností. Navyše, čím viac času trávim prijímaním informácií, tým viac sa tento pomer zhoršuje, lebo informácie sa opakujú v mierne upravenej podobe. Skrátka, keby som nejakú informáciu naozaj potreboval, omnoho užitočnejšie by bolo opýtať sa niekoho alebo použiť Google. Alebo pre všeobecný rozhľad možno použiť aj nejaký rebríček najlepších článkov, ale stačí raz za mesiac; veď naozaj dôležité veci sa tam objavia iba málokedy. Nazvime teda tento jav pravým menom: "informačné obžerstvo".

(C) 2008 Viliam Búr viliam-bur.blogspot.com

V inej podobe som sa s týmto javom stretol už predtým. V detstve som "hltal" knihy z knižnice; našťastie som sa naučil čítať veľmi rýchlo, a v pravidelnejších návštevách knižnice mi bránila lenivosť. (Môže nám byť drobnou útechou, že keď máme veľa zlých vlastností, začnú si niekedy navzájom prekážať.) Potom som spoznal závislosť na televízii; najmä po vzniku súkromných televízií, keď sa začalo vysielať aj niečo pozerateľné. To bolo podobné. Myslel som si, že za tú obrovskú stratu času môžu dlhé a časté reklamné vstupy, a nemožnosť prispôsobiť si vysielací čas svojim potrebám. Dnes si myslím, že aj keby televízia vysielala bez reklám, keby si divák mohol hocikedy spustiť hociktorý program, ba aj keby mal možnosť preskakovať v ňom menej zaujímavé časti, pri dostatočnom počte kanálov a teda pri dostatočnom výbere programov by bol výsledok rovnaký, ak nie ešte horší. Pocit "mám to pod kontrolou" závislosť často prehlbuje; ak je všetko pod kontrolou, netreba predsa podliehať panike; ak sa s tým dá kedykoľvek skončiť, nie je dôvod nedopriať si výnimočne ešte jeden posledný pohárik / cigaretku / telenovelu / webovú stránku, a riešenie odložiť na neskôr.

Televíziu som vyriešil radikálne; žiadnu doma nemám, a dúfam, že ani nikdy mať nebudem. Televízia ohlupuje. Najlepšie to vidno vtedy, keď sa niekoľko ľudí rozpráva v miestnosti s nahlas pusteným televízorom, ktorý práve bliká, huláka, a všemožne sa snaží upútať pozornosť. Možno ste na to takí zvyknutí, že to už nevnímate, ale pre odvyknutého je to ako návšteva v cirkuse. Keby ste takúto scénku nahrali, ale vystrihli by ste z nej televízor aj s jeho obrazovými a zvukovými prejavmi, vyzeralo by to ako posedenie na psychiatrii. Vážne! Ľudia sa zhovárajú otočení k sebe bokom alebo chrbtom; majú vypleštené oči a nehybné mimické svalstvo, ktoré sa občas náhodne prudko zmení; reč je prerušovaná dlhými pauzami, téma náhodne preskakuje a takmer nikdy sa nedokončí. Chápem, že pre politikov a inzerentov je takéto ohlupovanie užitočné, ale dokiaľ nie je televízia povinná a jej vypínanie trestné, dovtedy sa jej budem vyhýbať. Máme už síce povinné platenie za štátnu televíziu, ale jej skutočné sledovanie sa zatiaľ našťastie nekontroluje a nevynucuje.

Ako si teda poradiť s iracionálnym informačným hladom? Kamarátka ma raz naučila užitočný postup na fyzický hlad. Nemám si vraj klásť otázku "chcem jesť?", pretože ak je poruke niečo chutné, odpoveď je skoro vždy "áno". Správne si treba položiť otázku "som hladný?", a na tú je odpoveď väčšinou "nie". Samotnou zmenou otázky a rozdelením stravy na niekoľko menších porcií (otázku si treba položiť po každej z nich, v prípade zápornej odpovede sa využije chladnička a neskôr mikrovlnka) sa podľa mojej skúsenosti zníži príjem potravy aspoň o 50%, a človek pritom nikdy nehladuje dlhšie ako minútu. Pri tejto diéte musíte mať vždy nejaké zásoby, a skutočný hlad treba okamžite (malou porciou) zahnať, v ľubovoľnú dennú dobu; tým sa odstráni strach z hladu a nutkanie zjesť trochu viac do zásoby.

Rovnaký postup v prípade informácií znamená prestať sa pýtať "chcem si niečo (zaujímavé) prečítať?", kde zvyčajne nasleduje kladná odpoveď, ale namiesto toho si položiť otázku "potrebujem túto informáciu?", a pokračovať iba vtedy, keď tú informáciu naozaj potrebujem na niečo konkrétne. Samozrejme, človek vždy podľahne pokušeniu prečítať si aj niečo viac, ale pri podozrení, že čas začal opäť ubiehať príliš rýchlo, si treba tento postoj pripomenúť a vrátiť sa k nemu. Ak niečo budeme naozaj potrebovať, Google nám to nájde v pravý čas. A surfovať web s úmyslom zistiť, čo nové sa stalo vo svete, netreba častejšie ako raz za mesiac. Keby náhodou vypukla tretia svetová vojna, vaši známi vás na to iste upozornia.

Treba si uvedomiť, že aj pasívne prijímanie informácií niečo stojí. Platíme za ne svojím časom, a toho je málo. (Život je všeobecne krátky, ale v porovnaní s celkovým množstvom dnes dostupných informácií nadobúda mikroskopické rozmery.) Platíme za ne preťažením mozgu, ktorý sa ich potom snaží tráviť alebo iba presýtene grgá, namiesto toho, aby riešil užitočné veci alebo sa povzniesol k tvorivosti a vyjadreniu jedinečnej individuality nášho bytia. Chcem prežívať svoj život, alebo si chcem iba čítať, ako ho prežívajú druhí? Toto je môj čas, a som pripravený oň bojovať. Podelím sa oň s priateľmi, ktorí sa rovnako podelia so mnou, najlepšie v osobnom kontakte. Životy neznámych ľudí v hlbinách internetu budú musieť plynúť bezo mňa, rovnako ako aféry celebrít, prejavy politikov, a marketingové vyhlásenia firiem. Ak napíšete niečo, čo bude pre mňa naozaj užitočné, rád si to prečítam; po škrtnutí zbytočností mi na to zostane čas.

Súvisiace linky: