Mesterséges intelligencia a gyakorlatban: bevezetés a robotfoci programozásba

Bátfai, Norbert

E. adjunktus, DEIK.
Egyetemi adjunktus
Debreceni Egyetem, Informatikai Kar
Információtechnológiai Tanszék


                    
                    
                

Kömlődi, Ferenc

Szakmai lektor 
monitoring, jövőkutatás
Neumann János Számítógép-tudományi Társaság
Hírmagazin


                   
               

tudományos író (science writer)
IDG Hungary Zrt.
Computerworldl


                   
               

Szakmai lektor 
orgname
orgdiv


                    
                

Novák, Ildikó

Nyelvi lektor  
online magántanár
http://www.angolora.hu
http://www.angolora.hu


                    
                

Új Széchenyi Terv logó.

A tananyag a TÁMOP-4.1.2-08/1/A-2009-0046 számú Kelet-magyarországi Informatika Tananyag Tárház projekt keretében készült. A tananyagfejlesztés az Európai Unió támogatásával és az Európai Szociális Alap társfinanszírozásával valósult meg.

Magyarország megújul logó.

A Kelet-magyarországi Informatika Tananyag Tárház projekt logója.

Nemzeti Fejlesztési Ügynökség http://ujszechenyiterv.gov.hu/ 06 40 638-638

Az EU logója.

2011

Verziótörténet
Verzió 0.0.12011. augusztus 22.Bátfai
Elkezdem a jegyzet összeállítását DocBook 4.4 XML-ben: ajánlás, tartalomjegyzék kialakítása.
Verzió 0.0.22011. szeptember 17.Bátfai
Az első kiadás, hogy a jegyzet tudja támogatni a futó Prog2 labort.
Verzió 0.0.32011. szeptember 18.Bátfai
Az Atan alapú részek beillesztése.
Verzió 0.0.42011. szeptember 23.Bátfai
Mottók keresése, beillesztése.
Verzió 0.0.52011. szeptember 25.Bátfai
Az Atan 1.0 alapú részek beillesztése.
Verzió 0.0.62011. szeptember 27.Bátfai
Az Agent2D alapú részek beillesztése.
Verzió 0.0.72011. szeptember 28.Bátfai
Az Atan alapú részek átnézése, zavaróan redundáns forráskódok kivágása.
Verzió 0.0.82011. szeptember 29.Bátfai
További magyarázó ábrák beillesztése.
Verzió 0.0.92011. szeptember 30.Bátfai
Átnézés, apró javítások.
Verzió 0.0.102011. október 1.Bátfai
A bevezető rész preface tagból chapter tagba szervezve.
Verzió 0.0.112011. október 2.Bátfai
Nagy képek felbontásának átskálázása.
Verzió 0.0.122011. október 4.Bátfai
Mighty Magyars FC beillesztése.
Verzió 0.0.132011. október 4.Bátfai
13-as kiadás természetesen nincs.
Verzió 0.0.142011. október 6.Bátfai
Hibajavítás, beillesztett forráskódok gyomlálása.
Verzió 0.0.152011. október 8.Bátfai
Golden Team FC beillesztése.
Verzió 0.0.162011. október 9.Bátfai
Zárszó beillesztése.
Verzió 0.0.172011. október 10.Bátfai
Hibajavítás, kulcsszavak.
Verzió 0.0.182011. október 11.Bátfai
DocBook XML tagokkal a szöveg bőséges annotálása.
Verzió 0.0.192011. október 12.Bátfai
Nyomtatás, átolvasás.
Verzió 0.0.202011. október 14.Bátfai
A tartalom szűkítése, vágása.
Verzió 0.0.212011. október 15.Bátfai
Átolvasás (köszönet érte Bátfai Erikának), hibajavítás.
Verzió 0.0.222011. november 8.Bátfai
Egységesen a 3.0.0-s agent2d-vel lejátszott mérkőzések a fejezetcímek szervezéséhez.
Verzió 0.0.232011. november 9.Bátfai
Egyéb beillesztett teljes szövegek (pom.xml, stdout-os naplók) gyomlálása.
Verzió 0.0.242011. november 10.Bátfai
A PLK, a Prog2 labor labdarúgó-kupa kiírása.
Verzió 0.0.252011. november 10.Bátfai
A jegyzetben tárgyalt források kirakása, ellenőrzése: http://www.inf.unideb.hu/~nbatfai/mircsource/.
Verzió 0.0.262011. november 11.Bátfai
Forráskód csipetek indentálásának ellenőrzése.
Verzió 0.0.272011. november 11.Bátfai
Szerkezet áttekintése, kisebb átcsoportosítások, vagy dokumentumon belüli, ritkábban pedig kimutató hivatkozások beszúrása.
Verzió 0.0.282011. november 12.Bátfai
Átolvasás, ellenőrzés.
Verzió 0.1.02011. november 12.Bátfai
Szakmai lektorálásra előkészítve (ezt követi majd a nyelvi).
Verzió 0.1.12011. november 16.Bátfai
Szerző fotó update.
Verzió 0.1.22011. november 18.Bátfai
Szakmai lektor adatainak beillesztése.
Verzió 0.1.32011. november 20.Bátfai
Irodalomjegyzék átnézése.
Verzió 0.2.02011. november 23.Bátfai
A szakmai lektor véleményének beillesztése.
Verzió 0.3.02011. november 29.Bátfai
A nyelvi lektor javításainak átvezetése és véleményének beillesztése.
Verzió 1.0.02011. december 6.Bátfai
Linkek aktualizálása, a könyv fejlesztésének lezárása.

Ajánlás

A jelen ajánlást a FerSML projektnek címezzük: ha a robotfoci (RoboCup) utópisztikus célkitűzése az, hogy 2050-re a robot csapat győzze le az igazi (emberi) világbajnok csapatot, akkor a FerSML elegánsan utópisztikus céljának választhatjuk, hogy ugyanabban az időben a humán csapatok nemzetei egy FerSML avatár alapú robotot (vagy csak szoftvert) válasszanak szövetségi kapitánnyá.

Tartalom

Előszó
1. Bevezetés
A RoboCup kezdeményezés
A 2D szimulációs liga
A kapcsolódó szoftverek és dokumentáció bemutatása
RoboCup Soccer Simulator
Agent2D
Atan
Foci gyorstalpaló
A jegyzethez készített saját csapatok letöltése
A jegyzetről
A jegyzet kurzusai
A FerSML platform
A szerzőről
A lektorokról
I. A 2D szimulációs liga tárgyalása
2. A 2D szimulációs liga
Az RCSS robotfoci szimulációs szervere
Az RCSS szerver naplózása
Az RCSS futballpálya
Az ágensek kapcsolata a szimulált világgal
A szoftverek telepítése
3. Agent2D
A szoftverek telepítése
librcsc
soccerwindow2
agent2d
FormationEditor
A szoftverek tárgyalása
4. Atan
A szoftver telepítése
A szoftver használata GNU/Linux és Windows környezetben
A szoftver tárgyalása
Programozási feladatok
5. Atan-1.0
A szoftver telepítése
A szoftver használata GNU/Linux és Windows környezetben
A szoftver tárgyalása
II. Saját csapat építése
6. Atan alapú csapatok
HELIOS_base - Csorda FC, 24:0
A Csorda FC osztályai
HELIOS_base - Delibáb FC, 20:0
A Delibáb FC osztályai
HELIOS_base - Kékhalál FC, 10:0
A Kékhalál FC osztályai
HELIOS_base - Aranycsapat FC, 9:0
Az Aranycsapat FC osztályai
7. Atan 1.0 alapú csapatok
HELIOS_base - Aranycsapat FC (Atan 1.0), 32:0
Az Aranycsapat FC osztályai
Programozási feladatok
HELIOS_base - Marvellous Magyars FC, 29:0
A Marvellous Magyars FC állományai és osztályai
Einstein gyorsuló liftjében
HELIOS_base - Mighty Magyars FC, 36:0
A Mighty Magyars FC osztályai
A Mighty Magyars FC értékelése
Az eddigi fejlesztések módszertana
HELIOS_base - Golden Team FC, 35:0
A Golden Team FC osztályai
A Golden Team FC 0.0.2 mint multiágens rendszer
Quo vadis Golden Team FC?
HELIOS_base - Golden Team FC 0.0.2, 34:0
Deliberatív vagy reaktív?
8. Agent2D alapú csapatok
Az agent2d források tárgyalása, avagy szerelem első látásra
Munkamenet az agent2d forrásokkal
Az agent2d OO struktúrája
Irodalomjegyzék

Az ábrák listája

1.1. Az rcssserver ablaka Windows alól futtatva.
1.2. Az rcssmonitor ablaka Windows alól futtatva.
1.3. A SoccerWindow ablaka Windows alól futtatva.
1.4. A soccerwindow2-ben rcg-ből visszajátszva a 2011-es világbajnokság WrightEagle - HELIOS2011 döntője.
1.5. rcg állomány mentése a SoccerWindow alkalmazással.
1.6. FerSML platformbeli FC Basel - Debrecen szimuláció az Public Resource Football Computing programmal.
1.7. A Public Resource Football Computing program „összes felállás” vizsgálatának kimenete (DW: debreceni, BW: bázeli győzelem, DG: eldöntetlen s a sorokban a másik csapat 5 ugyanilyen felállása szerepel).
1.8. Tudható felállás 12 órával a mérkőzés előtt tudott kezdővel.
1.9. Tudható felállás néhány órával a mérkőzés előtt tudott, valódi kezdő tagokkal.
1.10. Utólagos 4-3-3 felállás.
1.11. A valódi felállás.
2.1. Az 1-es sorszámú ágens a bal oldalon, kirúgás előtt a balhátvéd pozíciójában.
2.2. Az RCSS futballpálya koordináta rendszere.
2.3. A szögek értelmezése az RCSS futballpálya koordináta rendszerében.
2.4. Az RCSS szimulációs ciklus.
2.5. Fej és test egy irányban, az rcssmonitor-ban.
2.6. Fej és test egy irányban, a soccerwindow2-ben.
2.7. Fej 90 fokkal elforgatva, az rcssmonitor-ban.
2.8. Fej 90 fokkal elforgatva, a soccerwindow2-ben.
2.9. Mozgás a pályán.
2.10. A látási információk feldolgozása.
2.11. A robotikával kapcsolatos programok telepítése egy aktuális Fedora 15 disztribúcióban.
2.12. A robotikával kapcsolatos szoftverek finomabb szelekciója egy aktuális Fedora 15 disztribúcióban.
3.1. A HELIOS_base és a Marvellous Magyars FC farkasszemet néz a soccerwindow2-ben.
4.1. Atan NetBeans projektben.
4.2. Az atan.model.ControllerPlayer.
4.3. Az atan.model.ActionsPlayer.
6.1. HELIOS_base - Csorda FC, 24:0
6.2. A mezőnyjátékosok egyetlen viselkedése: a labda követése.
6.3. A kapus elhagyja a kaput, illetve számos esetben a játékosok a pályát.
6.4. A játékosok elhagyják a játékteret.
6.5. A Délibáb FC négyes számú hatalmas bombája 22-ről.
6.6. Pillanatkép az Atan Sample1 - Atan Sample2 mérkőzésről.
6.7. A 9-es mezt viselő védő egy félidőnyi trajektóriája.
6.8. Az 5-ös mezt viselő középpályás egy félidőnyi trajektóriája.
6.9. A 4-es mezt viselő csatár egy félidőnyi trajektóriája.
6.10. Az Atan-os példa-csapat 11-es mezt viselő játékosának egy félidőnyi trajektóriája.
6.11. A 4-es mezt viselő csatár fut elvégezni a szögletet.
6.12. A w63 csomag osztályainak szervezése.
7.1. A játékos a labdához megy elvégezni a szögletet.
7.2. A játékos a kapu felé fordul a szöglet elvégzése előtt.
7.3. Szerencsés gól szögletből.
7.4. A relatív érző metódusok értelmezése.
7.5. A játékos GPS eszköze.
7.6. A FerSML projekt logójának előkészítése.
7.7. Az alternatív helyzetmeghatározás működése
8.1. Az agent2d és a librcsc a Doxygen HTML kimenetének böngészése.
8.2. A rcsc::SoccerAgent osztály leszármazási fája a Doxygen PDF kimenetében.

Végszó

A tananyag a TÁMOP-4.1.2-08/1/A-2009-0046 számú Kelet-magyarországi Informatika Tananyag Tárház projekt keretében készült. A tananyagfejlesztés az Európai Unió támogatásával és az Európai Szociális Alap társfinanszírozásával valósult meg.

Az UMFT és az EU logója.

A Kelet-magyarországi Informatika Tananyag Tárház projekt logója.

 

„I'll come back.”

 
 --Terminator [TERMINATOR] T E R M I N A T O R

Remélhetőleg mire idáig, a könyv végére érkezel az olvasásban és az ezzel párhuzamos gép melletti feldolgozással, már magad is tudod, hogy a „megtanulni programozni” fogalmát ugyanúgy rekurzívan lehet megalapozni, mint a GNU jelentését, ami ugye azt jelenti, hogy a „GNU's Not Unix!”. Hasonlóan programozni csak úgy lehet megtanulni, ha programozol.

Előszó

Ismert érzés programozói körökben a „kellene írni egy jó programot”. A jóság fogalma persze egyáltalán nem magától értetődő, szoftverekre meg pláne nem az. De a szóban forgó esetben a programozók tipikusan azt értik alatta, hogy „írni kellene egy olyan programot, amelynek az írását élvezem”.

Reményeim szerint a könyv folyamatosan és fokozatosan finomított példacsapatai ennek az érzésnek használati eseteivé tudnak válni azáltal, hogy a kezdő programozó kiindulásául szolgálnak majd saját csapata elkészítésében, miközben gyarapodó tudásával nap mint nap szobrászkodik csapata forráskódjain.

1. fejezet - Bevezetés

Ebben a bevezető részben

mindezek mellett vázoljuk a Debreceni Egyetem Informatikai Karának sporttudományi érdeklődését a FerSML platform rövid bevezetésével.

„November 25-én, a wembleyi stadionban ez a csapat állt ki az angolok ellen: Grosics-Buzánszky, Lóránt, Lantos-Bozsik, Zakariás-Budai II., Kocsis, Hidegkuti, Puskás, Czibor.”

Sebes Gusztáv [SEBES]

A RoboCup kezdeményezés

 

„First, I believe that this nation should commit itself to achieving the goal, before this decade is out, of landing a man on the moon and returning him safely to the earth.”

 
 --President John F. Kennedy [KENNEDY] Special Message to the Congress on Urgent National Needs, May 25, 1961

Vajon melyik a nagyobb kihívás? 1961-ben célul tűzni, hogy az ember az égbe utazva elérje szülőbolygójának sápadt kísérőjét: a Holdra lépjen, méghozzá az említett évtized végéig. Vagy 1997-ben távoli célként azt megjelölni, hogy olyan robotok alkotta futball csapatunk lesz, amelyik le tudja győzni az aktuális emberi világbajnok csapatot. Érdekes és nehéz kérdés, de természetesen költőinek szántuk. Esetleg annyit jegyzünk meg, hogy az utóbbi időkorlátja lényegesen nagyobb, 2050-re tűzték ki, a robotfoci (RoboCup) távoli céljaként.

A RoboCup tehát egy kihívás, amelyet egy sztenderd MI feladat formájában állított a kutatók elé a [KITANO] alapcikk[1]. Időközben a kihívás tovább bővült, amíg ma az alábbi négy fő feladatot tartalmazza a RoboCup:

  • RoboCup Soccer: itt természetesen megmaradt az alapfeladat, a futball, amit a használt robotoktól függő öt nagyobb kategóriában lehet űzni. Amikor valójában nincs is robot test, tehát amikor csak magával a „futballal” kell foglalkozni, az a szimulációs liga (Soccer Simulation League) , amit még további a 2D és a 3D ligákra bonthatunk. Azt megjegyezhetjük, hogy a 2D szimulációs liga mérkőzésének figyelése tudja ma a nézőben leginkább a valódi foci élvezése közben keletkezett érzeteket kelteni és megfordítva, van olyan focis liga, amelynek mérkőzései ma még egyáltalán nem keltik azt.

    A spektrum másik vége a humanoid liga (Soccer Humanoid League), az itt használt humaniod robotokon belül is megkülönböztetnek három alkategóriát, ami a robot méretei szerint lehet gyerek, ifjúsági vagy felnőtt.

    A két most említett „véglet” között találjuk méret szerint növekvő sorrendben a Soccer Small Size League-t, ahol maximum 15 cm magas és 18 cm átmérőjű kerülettel bíró robotok csatáznak. Majd a Soccer Middle Size League-t, ahol az 50 centiméternél kisebb átmérőjűek. Itt említjük a Standard Platform League-t (SPL), ahol egységesen ugyanazokat a robotokat használják a kutatók. Ennek megfelelően 2008-ig beszélhettünk a Sony Aibo League, a „négylábú” ligáról. Azóta és napjainkban pedig az Aldebaran Nao SPL az aktuális [KALYANAKRISHNAN]. Ezek a robotok kétlábúak, tulajdonképpen 60 centis humanoidok.

  • RoboCup Rescue: itt mentést végző robotokkal találkozhatunk, ezen belül is valódiakkal (Rescue Robot League) vagy szimuláltakkal (Rescue Simulation League).

  • RoboCup @Home: ha valahová a klasszikus porszívó robotot akarjuk elhelyezni, akkor ide kell tennünk, de ne becsüljük le ezt a kategóriát, hiszen itt lesz majd az „I, robot” film [IROBOT] Sonny-ja is!

  • RoboCupJunior: e kategórián belül is találkozunk foci és mentő robotokkal, illetve újdonságként megjelenik a táncoló (robodance) robot. A kategória legfőbb jellemzője a célkorosztály lehetőségeinek megfelelően a LEGO© robotok használata, hiszen a LEGOMindstorms® Robotics Invention System (RIS 2.0) csomagot a gyártó 12 éves kortól ajánlotta. A várhatóan 10 éves termékciklusát nemrégiben megkezdő új NXT csomagnál ezt a határt már a 10 éves korra szállították le.

[Tipp]Olvasnál még róla? Olvass!

A RoboCup lapját az érdeklődőknek érdemes folyamatosan figyelemmel kísérni: http://www.robocup.org/. A szimulációs liga irányában pedig a kapcsolódó wiki lap ad jó iránymutatást a kezdő érdeklődőknek, mert itt visszamenőleg összefogva megtalálják a világbajnokokat, illetve a döntőket is megnézhetik a belinkelt YouTube videókon: http://wiki.robocup.org/wiki/Soccer_Simulation_League. De a későbbiekben arra is mutatunk majd példát, hogy a mérkőzés naplóállományai alapján hogyan tudjuk visszanézni a találkozót.

A 2D szimulációs liga

 

„Although RoboCup's final target is a world cup with real robots, RoboCup offers a software platform for research on the software aspects of RoboCup.”

 
 --Hiroaki Kitano [KITANO] RoboCup: The Robot World Cup Initiative

A RoboCup szűkebb értelemben egyben magát a robotfoci világbajnokságot is jelöli, amit 2010-ben például Singapore városában rendeztek meg. A 2D szimulációs liga (RoboCup Soccer Simulation, RoboCup Soccer Simulator vagy röviden RCSS) esetén megszokott a csapatok rövid bemutató dokumentumainak, az úgynevezett TDP-k (Team Description) dokumentumok elérhetőségének biztosítása. A megnevezett világverseny esetén ezeket itt találjuk: http://www.socsim.robocup.org/files/2D/tdp/RoboCup2010/.

A kezdő érdeklődőnek érdemes megnéznie egy 2D szimulációs ligás mérkőzést. Legyen ez például a szóban forgó nemzetközi torna döntője, amit a YouTube videó megosztón is megtalálunk: http://www.youtube.com/watch?v=BVWkndHk3AE. Mint a 2D szimulációs ligában mindig, másodpercenként 10 szerveroldali szimulációs ciklussal 10 percben élvezhetjük a mérkőzést, azaz 6000 szimulációs ciklusban küzd meg egymással a két 10+1 fős csapat. Futballnak hat, ez nem lehet vitás! De megjegyezhetjük, hogy a ligán belüli fejlődés kitapintásához érdemes a korábbi döntőket is megnézni. Ám ezzel a szállal a jegyzetben felhagyunk, hiszen el akarjuk kerülni a „hogyan játszott volna ez a csapat az Aranycsapattal szemben” típusú kérdéseket. Bár a jövőben (a FerSML platformon akár) még az ilyen felvetések is vizsgálhatóak lehetnek, ennek szellemét fejezte ki egy kollégám a „ha rendelkezésünkre állnának Puskásék avatárjai, meglehet kiderülne, hogy az 54-es berni döntőn tíz esetből hétszer győztünk volna” megjegyzésével [BATFAI].

A kapcsolódó szoftverek és dokumentáció bemutatása

A jelen jegyzetben három szoftver (esetenként szoftver-csokor) megismerésére koncentrálunk, ezek a

Mindhárom nyílt forráskód alapú fejlesztés, a következő pontok hivatkozta szoftverek, specifikációk és dokumentációs anyagok megtalálhatóak a megfelelő most közölt linkeken.

Az RoboCup Soccer Simulator legfontosabb komponense maga a szerver, az rcssserver. Ehhez csatlakoznak UDP kliensként a játékos ágensek. A kliensek és szerver közötti kommunikációs protokollokat az rcssmanual dokumentum rögzíti. A szimuláció megjelenítését alapestben a rcssmonitor program használatával tehetjük meg.

Az Agent2D kliens ágenst a 2010-ben világbajnok (2009-ben és 2011-ben második, 2007-ben és 2008-ban harmadik) HELIOS Japán csapat használja, illetve saját szoftvereik keretében (a librcsc-vel és a soccerwindow2-vel együtt) fejlesztik.

Az Atan [ATAN] egy Java alapú interfész, amely megkönnyíti a saját csapat írását azzal, hogy átveszi a szerverrel történő (az rcssmanual protokolljai szerinti szabványos) kommunikáció megvalósításának terhét a fejlesztő válláról.

RoboCup Soccer Simulator

rcssserver

Az rcssserver a foci világának „mátrixa”, karakteres felületű szerver folyamat. Fejlesztése 1997 óta folyamatos, licence GNU LGPL.

1.1. ábra - Az rcssserver ablaka Windows alól futtatva.

Az rcssserver ablaka Windows alól futtatva.


A karakteres konzolon a szerver stdout-ra küldött alapvető naplózása (például adott kliens csatlakozott, kilépett) látható. A figyelmes olvasóban az is tudatosulhat, hogy a csatlakozó klienseket v13-oknak azonosítja, s valóban az Aranycsapat FC Atan 1.0 alapú, ami valóban a v13 szerverre van felkészítve a jelen jegyzet írásának idején.

rcssmonitor

Az rcssmonitor feladata a szerver által felépített, karbantartott szimulációs világ megjelenítése. Fejlesztése a kezdetektől a rcssserver-el összefonva történik, licence GNU GPL v3.

1.2. ábra - Az rcssmonitor ablaka Windows alól futtatva.

Az rcssmonitor ablaka Windows alól futtatva.


A pálya képe és a játékosok egyértelműek, a kis fehér, üres körök a játékosok által érzékelhető fix „tereptárgyak”, az RCSS terminológiában zászlók.

rcsslogplayer

Az RCSS szerver alapértelmezésben menti abba a könyvtárba, ahonnan elindították a mérkőzés rcg állományát, az rcsslogplayer képes ezt rcssmonitor programként visszajátszani, licence GNU GPL v3.

rcssmanual

Az rcssmanual a RoboCup Soccer Simulator felhasználói kézikönyve, s egyben a szerver és kliensei kommunikációja megismerésének elsődleges forrása, licence GNU FDL.

Agent2D

 

„Come with me if you want to live.”

 
 --REESE [TERMINATOR] T E R M I N A T O R

librcsc

Az RCSS kliensek fejlesztését támogató osztálykönyvtár [HELIOS], licence GNU GPL v3.

agent2d

Egy példa RCSS kliens [HELIOS], licence ugyancsak GNU GPL v3. A fejlesztők külön kiemelik a kapcsolódó éves TDP-kben, például a [HELIOS]-ban, hogy kezdő csapatoknak ideális lehet az elinduláshoz ezt választani a RoboCup-on való sikeres szerepléshez. A 2011-es torna résztvevői közül (természetesen a fejlesztő, most második helyet szerző HELIOS csapaton túl) a EdInferno.2D [EDINFERNO2D], ParaNoid [PARANOID], NADCO-2D [NADCO2D], AUA2D [AUA2D] és a Photon [PHOTON] csapatok fogadták meg ezt a tanácsot.

SoccerWindow és soccerwindow2

Mindkét program a robotfoci megjelenítését végzi, s egyben rcsslogplayer programok is, azaz a korábbi napló állományokból újra tudják játszani az adott mérkőzést. Az előbbi annyiban tud többet, hogy számos diagnosztikai funkciót (például a játékosok, a labda által bejárt trajektória mutatása stb.) is biztosít.

1.3. ábra - A SoccerWindow ablaka Windows alól futtatva.

A SoccerWindow ablaka Windows alól futtatva.


[Megjegyzés]RoboCup 2011 döntő, WrightEagle - HELIOS2011, 3:2

A napló állományokat felhasználva is újrajátszhatunk mérkőzéseket, nem kell mást tennünk, mint betölteni az adott, most például a RoboCup világbajnokság 2011-es döntőjének WrightEagle - HELIOS2011 mérkőzésének rcg fájlját és máris „felvételről” élvezhetjük a mérkőzést.

1.4. ábra - A soccerwindow2-ben rcg-ből visszajátszva a 2011-es világbajnokság WrightEagle - HELIOS2011 döntője.

A soccerwindow2-ben rcg-ből visszajátszva a 2011-es világbajnokság WrightEagle - HELIOS2011 döntője.


Atan

Interfészként egy magasabb absztrakciós szint bevezetésével biztosítja, hogy kliensünket egyrészt Javában valósítsuk meg, másrészt lehetővé teszi, hogy e közben ne kelljen az RCSS szerverrel történő UDP feletti kommunikációs protokoll implementációjával foglalkozni, mert azt teljesen elrejti, elfedi az őt használó API programozó elől.

Foci gyorstalpaló

 

„Unfortunately, no one can be told what the Matrix is. You have to see it for yourself.”

 
 --MORPHEUS [MATRIX] The Matrix

A robotfoci színtiszta programozás. Természetesen a labdarúgást szeretőknek ad egy plusz élményt az ezzel való foglalatoskodás, illetve könnyebbség, hogy ők a szimuláció elemeit, eseményeit (például gólvonal vagy szöglet) eleve ismerik. Akik csak a programozás szerelmesei, de a futballt nem űzik vagy figyelik, azoknak ajánljuk a FIFA hivatalos [FIFA] szabálykönyvét átlapozásra, amelyből a labdarúgás alapfogalmai, szabályai gyorsan elsajátíthatók.

Ahogyan ezt az alpontot nyitottuk, a robotfoci kliens ágensek készítése színtiszta programozás. Ha magát a futball játékot, mint élményt akarod megismerni, sosem késő, de amiként Morfeusz nem tudta elmondani, mi a Mátrix, mi sem tudjuk leírni, mi a foci. Neked magadnak kell megismerned: egy labdával és néhány baráttal menjetek le a pályára!

A jegyzethez készített saját csapatok letöltése

 

... „to acquire a new programming paradigm you need to read the manual, you need to see a lot of examples, and you need to try writing and running programs yourself.”

 
 --Gregory Chaitin [METAMATH] META MATH! The Quest for Omega

Eleinte törekedtünk a „tannenbaumi hagyomány” megtartására, hogy teljes kódokat közöljünk a könyvben, ám az annyira gyarapodott, hogy mostanra tipikusan inkább csak kódcsipeteket közlünk. Viszont a jegyzetben kifejlesztett legtöbb csapat kódja Maven projektként a kurzus blogjának megfelelő posztjairól letölthetőek, s természetesen magának a jegyzetnek is a mellékletét képezik, illetve a szerző honlapján minden, még a kisebb források is megtalálhatóak a http://www.inf.unideb.hu/~nbatfai/mircsource/ címen.

[Megjegyzés]A források életrekeltéséről
  • Első ismerkedéskor (úgy a Javával, mint a robotfocival) a parancssori javac fordítóval történő fordítást javaslom, majd a java paranccsal történő futtatást. Például a bevezető Csak a foci FC, Büntető FC vagy a Ping-pong FC csapatoknál és társaik esetében.

  • A szintén bevezető, de kissé jobban tagolt, például a Csorda FC, Délibáb FC vagy a Kékhalál FC csapatoknál a NetBeans integrált fejlesztői környezetet javaslom.

  • A fent linkelt Aranycsapat variánsoknál viszont már a Maven projektként való kezelést javaslom elsődlegesnek.

A jegyzethez készített saját csapatok szervezése

A csapatok szervezésének alapja nem a pályán mutatott teljesítmény, hanem az oktatásban történő felhasználás. Példaképpen említhetem itt, hogy a jegyzet kronológiája szerint sokkal korábbi Aranycsapat FC tipikusan 2 góllal veri a sokkal későbbi Mighty Magyars FC csapatot, de az utóbbi játéka sokszor már focira emlékeztet, szemben az előbbivel, ahol ha ez elő is fordul, az puszta véletlen, abban az értelemben, hogy nem programoztuk be.

Prog2 labdarúgó-bajnokság (első osztály), PB I

Az előző megjegyzés tükrében első osztályba nem kerülne jegyzetbeli csapat, de az elegencia kedvéért mégis így szervezzük, s felhívjuk a figyelmet, hogy jó csapatok nem néhány hét alatt születnek. Követendő példaként szomszédunk, Románia neves csapatára, az OXSY csapatra tekinthetünk, amely fejlesztése egy diplomamunka keretében 2002-ben kezdődött meg a nagyszebeni Lucian Blaga Egyetemen. A munka folytatódott és a csapat 2009-es [OXSY] TDP-jéből kiderül, hogy jöttek az évekkel az eredmények, 2009-re jutottak oda, hogy világbajnoki bronzérmesek legyenek.

Prog2 labdarúgó-bajnokság (harmadosztály), PB III

A jegyzetről

 

„Nem tudok kimerítő leírást adni arról, hogy hogyan tudsz megtanulni programozni - nagyon összetett tudásról van szó. Egyet azonban elárulhatok: a könyvek és tanfolyamok nem érnek túl sokat (sok, valószínűleg a legtöbb hacker autodidakta). Aminek van értelme: (a) kódot olvasni és kódot írni.”

 
 --Eric S. Raymond (fordította Kovács Emese) [HACKERHOWTO] Hogyan lesz az emberből Hacker

A jegyzet kurzusai

Magas szintű programozási nyelvek 2

Egyetemünkön egy széles, 100-200 fős lélekszámú közösséget képez a mérnök informatikus BSc szakon futó mindenkori Magas szintű programozási nyelvek 2 (MI BSc, INBK302) című kurzus hallgatói tábora. A jelen jegyzet tulajdonképpen e kurzus laborgyakorlata (a céges feladatok melletti) úgynevezett „egyetemi” feladatának a teljesítését segíti, ami éppen egy saját robotfoci csapat elkészítése. Az említett kurzusban azt a hagyományt teremtjük meg, hogy a labor teljesítésének feltétele egy céges vagy egy egyetemi feladat megoldása és a megoldás sikeres bemutatása a laborközösség előtt.

Tehát komoly súlyt kap ennek a célkurzusnak a gyakorlati részében a foci. Sőt, a futballt itt általánosságban érthetjük, mert nem csak a RoboCup a vizsgált és bevezető szinten feldolgozott terület, hanem a sporttudományi ihletésű FerSML, azaz a labdarúgás-szimuláló jelölőnyelv is gyakorlati témánk.

WEB 2.0 diákok WEB 2.0 tanárok

A kurzus szervezésének alapja a kurzus blogja, amelyet a http://progpater.blog.hu/ címen talál meg a kedves olvasó. Magát a kurzus lapját pedig a http://nehogy.fw.hu/ címen tartjuk karban. A jelen alpont címét az Informatika a felsőoktatásban 2011 konferencián tartott hasonló című, a blogos, YouTube-os és általában a Web 2 jellegzetességek kurzusbeli felhasználását bemutató előadásunk sugallta, amit a konferencia kiadványban (a 451. oldaltól) tekinthet meg az érdeklődő olvasó. Az említett blogot abból a szempontból is ajánlhatjuk megtekintésre, hogy mivel e köré egy élő közösség szerveződik, itt a kommentek között válaszokat találhat, vagy akár direkt és természetes módon felteheti kérdéseit is az érdeklődő robotfoci csapat vagy leendő FerSML avatár fejlesztő. Ez konkrétan abból adódik, hogy a szóban forgó egyetemi kurzus laborgyakorlatait teljes mértékben a blog posztjai tematizálják, illetve a hallgatók kommentek formájában ezen a felületen kommunikálnak egymással és a kurzussal. A heti formába szervezett nappali oktatáshoz direktben kapcsolódó posztok a következők:

[Megjegyzés]rcg és rcl állományok

Ezek a fájlnévkiterjesztések a „record game” és a „record log” kifejezésekből származnak. A kurzus blogjának megfelelő posztjainak kommentjeiként például rcg állományok megadását kérem, hogy magam is le tudjam játszani, meg tudjam nézni a beküldő hallgató által jelzett mérkőzést vagy annak részét. Utóbbit, azaz részek magadását javaslom beküldeni, amit például a következő módon készíthetsz el. Használod monitorként a SoccerWindow alkalmazást, amelyen a mérkőzés után megszakítod a szerverrel a kapcsolatot, majd a következő ábrának megfelelően a szimulációs ciklusok görgetősávjával behozod a kiemelni, demonsrálni kívánt szituációt, amelynek az elejét megjelölöd. Lezajlik a szóban forgó mérkőzés részlet, amelyek a végét megintcsak megjelölöd, s ezzel egyben nyílik is az a párbeszédablak, amely a megfelelő rcg állomány mentését intézi. A kép mutatta (amelyben a hazai csapat bedobás utáni gyors, rövid egyérintőből bomba gólt szerez) esethez kapcsolódó rcg fájlt a hasznalat-segment.rcg címen a kedves olvasó is megtekintheti. Érdemes ehhez a fájlnévkiterjesztéshez hozzárendelni a SoccerWindow vagy a soccerwindow2 programot.

1.5. ábra - rcg állomány mentése a SoccerWindow alkalmazással.

rcg állomány mentése a SoccerWindow alkalmazással.


Az RCSS szerver alapértelmezésben menti abba a könyvtárba, ahonnan indították a mérkőzés rcg és rcl állományait, a dátumból, az időből, a csapatok neveiből és az eredményből képzett fájlneveken, mint például a 20110915 1111-MightyFC_3-vs-Bcsapat_1.rcg. és a 201109151111-MightyFC_3-vs-Bcsapat_1.rcl. Ezekbe a szöveges fájlokba az RCSS szerver naplózásának boncolgatásakor bele is pillantunk majd.

[Megjegyzés]A forráskódok szedéséről

A „bazár világ - Release Early, Release Often” szervező elvét [KATEDRALIS] szem előtt tartva a jegyzet készítése során folyamatosan adtuk ki az újabb változatokat. A kezdeti időszakban a legtöbb esetben a szereplő forráskódokat sorszámozva helyeztük el a jegyzetben. Mivel a programkód a tanulói-fejlesztői megismerés elsődleges forrása, így minden osztály esetén ügyeltem, hogy az osztály teljes forrása egyben is szerepeljen. Ezt a tannenbaumi hagyományt tipikusan követem a jegyzeteimben (például Programozó Páternoszter, Javát tanítok) és mivel a jelen anyag fő megjelenési formája elektronikus, ez most nem okozhatott volna esetleges terjedelmi problémákat sem. De mégis, a felszaporodó és az apró fejlesztési lépések miatt kódban alig különböző nagyszámú forrás a tapasztalat szerint visszavetette a jegyzet használhatóságát, így a a forráskódok agresszív gyomlálására kényszerültünk.

Terveink szerint tehát a jegyzet a Magas szintű programozási nyelvek 2 kurzus egyik pillére lesz azzal, hogy olyan szoftver prototípusokat ad, amelyet a ráépülő kurzusokon (például a Bevezetés a robotikába vagy a A mesterséges intelligencia alapjai tárgyakon) tudnak sikerrel alkalmazni, felhasználni a hallgatók.

Prog2 labor labdarúgó-bajnokság, PLB

Megalapítjuk a kurzusban ezt az intézményt. A jegyzet készülésével párhuzamosan, most első alkalommal a hallgatók 8, egyenként maximum két fős csapata jelentkezhet a kurzus blogján egy maximum néhány, de tipikusan egy oldalas TDP jellegű pdf állománnyal, melyben röviden bemutatják a csapatukat.

A csapatok (lévén ez egy programozás alapkurzus) csakis Java alapúak lehetnek és

  • vagy „from scratch” kifejlesztendőek

  • vagy Atan 1.0 alapon készítendőek (de a csapat egyéb kódjában „from scratch” legyenek)

  • vagy a jegyzet valamely Atan 1.0 alapú példa csapatának továbbfejlesztésével kell létrejönniük.

A 8 csapatból az első 6 tagjai megkapják a labor teljesítés aláírását mérlegelés nélkül. A „kieső” két csapatnak az általános követelmény alapján kell teljesítenie a labort (kivéve, ha a PLK-beli szereplésük alapján mentesülnek ettől), tehát egyetemi vagy céges feladat, de minden esetben szóban védenie kell a kódjait.

A jegymegajánlással kapcsolatos vadászatban a bajnokság pontjai (győzelem 5 trófea, döntetlen 3, vereség 2) beszámítanak mind a 8 csapat tagjainál. Továbbá a PLB befejeztével a bajnoknak 20, a második helyezettnek 15, a bronz érmesnek pedig 10 pontot írunk jóvá.

Prog2 labor labdarúgó-kupa, PLK

Az iménti PLB kiegészítésére megalapítjuk a kurzusban a kupa, a Prog2 labor labdarúgó-kupa, röviden PLK intézményét is. A hallgatók 16, egyenként maximum két fős csapata jelentkezhet a kurzus blogján egy maximum néhány oldalas TDP jellegű pdf állománnyal, melyben röviden bemutatják a csapatukat. Igen, természetesen PLB-beli csapatok is jelentkezhetnek, sőt!

A csapatok a PLB-hez hasonlóan csakis Java alapúak lehetnek és

  • vagy „from scratch” kifejlesztendőek

  • vagy Atan 1.0 alapon készítendőek (de a csapat egyéb kódjában „from scratch” legyenek)

  • vagy a jegyzet valamely Atan 1.0 alapú példa csapatának továbbfejlesztésével kell létrejönniük.

A 16 csapatból az első 4 tagjai megkapják a labor teljesítés aláírását mérlegelés nélkül. A „kieső” csapatoknak az általános követelmény alapján kell teljesítenie a labort (kivéve, ha a PLB-beli szereplésük alapján mentesülnek ettől), tehát egyetemi vagy céges feladat, de minden esetben szóban védenie kell a kódjait.

A jegymegajánlással kapcsolatos vadászatban a kupa pontjai megintcsak a bajnokságéval azonos módon (győzelem 5 trófea, döntetlen 3, vereség 2) beszámítanak mind a 16 csapat tagjainál. Továbbá a PLK befejeztével a kupagyőztesnek 20, a második helyezettnek 15, a bronz érmesnek pedig 10 pontot írunk jóvá.

Mindkét torna versenynaptárát megtalálhatja a kedves olvasó a http://progpater.blog.hu/2011/10/25/prog2_labor_labdarugo-bajnoksag_plb posztban. Annyit még megjegyezhetünk, hogy ha a programozás oktatásának megújításával (l. ezen a linken a 451. oldaltól) kapcsolatos erőfeszítéseink majd gyümölcsözőnek bizonyulnak, akkor minden laborközösségben megrendezünk egy bajnokságot, s ezek fölött szervezzük majd a bajnokok ligáját, de (ahogyan Mal Braithwaite, a fiatal Santiago Munez edzője a Newcastle-nél a [GOAL] moziban mondaná) „de még nem ma”!

E sorok írásakor aktuális a kurzus laborjain a védés. Ennek során a hallgató forráskódban mutatja be csapatát a laborközösség előtt. Védése bevezetésében pedig (projektorral kivetítve) fejlesztett csapata játékát mutatja be. Így napirenden van a kérdés, hogy ezt a bemutatót milyen csapatokkal és milyen szabályokkal kell játszani? Mivel a PLB sikeres csapatai egyre gyakrabban számolnak be arról az élményükről, hogy a jegyzet valamelyik csapatából kiindulva, saját továbbfejlesztett csapatukkal 6,7,8 góllal verik a Golden Team FC csapatot. Így adódik a a felvetés, hogy az e csapat elleni 1-2 gólos győzelem magasságához állítsuk be a „teljesítés lécet”.

Bevezetés a robotikába

Jóval szűkebb közösséget képeznek a mérnök informatikus BSc szakon futó Bevezetés a robotikába című (PTI BSc, INMV690L) kurzus hallgatói, de a jegyzet annál inkább sikerrel hasznosítható ebben a kurzusban.

A mesterséges intelligencia alapjai

A mesterséges intelligencia alapjai című (INAK441) kurzus ágensekkel foglalkozó részében a jegyzetre támaszkodva jól megalapozható a hallgatók témabeli (intelligens ágensek) intuitív szemléletének kialakítása.

A FerSML platform

 

„Különleges megbízást kaptam, az ellenfelek játékát kellett megfigyelnem és a látottakról beszámolnom. Mennyivel más volt ez most számomra, mint amikor egy mérkőzést megnézek és megállapítom, hogy ez a játékos tetszett, a másik viszont sok hibával játszott. Papírral, ceruzával a kezemben figyeltem a játékosokat. Feljegyeztem hogyan helyezkednek, hogyan mennek fel fejelni, merre fordulnak, milyenek voltak a leadásaik, milyenek a rajtjaik. Egyszóval megfigyeltem erényeiket és hibáikat. Alaposan, a legapróbb részletekig.”

 
 --Sándor Károly [SEBES]

A FerSML platform [FERSML] egy futball szimulációs jelölőnyelvből és az ezt értelmező szimulációs szoftverből áll. A szimulációs jelölőnyelven, a játék és a játékosok folyamatos (a lehetőségektől függően teljesen vagy részben automatizált illetve egyáltalán nem automatizált: lásd például a szurkolói avatárok) megfigyelésével alakítjuk ki a játékosokat absztraháló XML dokumentumokat, a platform terminológiájában az avatárokat. A szimulációs szoftver az avatárokkal képes nagy mennyiségű szimulációt készíteni, lefuttatni, melyek elemzése a döntéstámogatás új lehetőségeinek tárházát villantja fel, melyet abban az esetben tartunk majd sikeresnek, ha ezeket a labdarúgás szakmai stábok hasznosnak ítélik napi munkájuk segítésében.

A platform célja hosszútávon tehát olyan szimuláció-alapú döntéstámogató eszközzé válni, amit a sporttudományi szakemberek és a vezetőedzők, edzők elfogadnak. Rövidtávú cél pedig egy olyan szimulációs környezet megalkotása, melyben a megfigyelt jellemzők (például adott tornán a lőtt összes gólok száma, mérkőzésen a lőtt gólok száma, gólkülönbség és sorolhatnánk az egyébként az irodalomban rendelkezésre álló adatokat) ugyanolyan eloszlást követnek a szimulációkban, mint amilyeneket mutatnak a valóságban.

A FerSML platform és a robotfoci kapcsolata

A FerSML platform keretein belül nem feladatunk, fel sem merül a semmiből egy olyan játékos ágenst kialakítani, aki rendelkezik a pálya és a játék egy olyan belső reprezentációjával, amely alapján képes az elvárt megfelelő, azaz (mesterségesen) intelligens viselkedésre. Ez a robotfoci alapvető küldetése, de a FerSML platformon ez fel sem merül, itt triviálisan feltesszük, hogy ennek az absztrakciós szintnek a megléte a-priori adott.

Tehát a robotfoci eredményeinek felhasználása szempontjából jelen pillanatban csakis a szimulációs modell felhasználhatóságának vizsgálata merülhet fel, konkrétan, hogy lehet-e a rövidtávú cél teljesítésében az egyik vizsgált szimulációs modellként azokat felhasználni.

A fordított helyzet, tehát a FerSML platform felől nézve triviálisan adódik egy FerSML2RoboCup jellegű alkalmazás készítése, ami tehát egy RCSS focicsapat generálását jelenti a FerSML avatárokból.

A FerSML platform egy használati esete

 

„Úgy érzem, nem érdemeltünk vereséget ezen a mérkőzésen, mert több helyzetünk volt.”

 
 --Herczeg András, DVSC-TEVA [HERCZEG]

Kiemeli a FerSML platform sporttudományi motivációját, ha megnézzük használatának egy esetét, amelyet egyébként az [PRFC] munkában küldtünk be közlésre, ahol a Bajnokok Ligája 3. selejtezőkörének Debreceni VSC - FC Basel (0-2) és FC Basel – Debreceni VSC (3-1) mérkőzésével kapcsolatosan vizsgálódtunk.

Ennek során csak a leggyengébb adatforrásokkal tudtunk dolgozni, azaz avatárjaink csakis szurkolói avatárok voltak. Ez konkrétan azt jelenti, hogy a mérkőzés nézése közben néhány adatot szisztematikusan megfigyeltünk és feljegyeztünk, tipikusan hasonlóakat, mint amelyeket az UEFA is közölni szokott statisztikaként a BL mérkőzésekről. Ezek alapján futtattuk a szimulációkat az összes lehetséges felállás párosításban, az eredményekből a 4-3-2-1 formáció tűnt a leggyümölcsözőbbnek.

1.6. ábra - FerSML platformbeli FC Basel - Debrecen szimuláció az Public Resource Football Computing programmal.

FerSML platformbeli FC Basel - Debrecen szimuláció az Public Resource Football Computing programmal.


1.7. ábra - A Public Resource Football Computing program „összes felállás” vizsgálatának kimenete (DW: debreceni, BW: bázeli győzelem, DG: eldöntetlen s a sorokban a másik csapat 5 ugyanilyen felállása szerepel).

A Public Resource Football Computing program „összes felállás” vizsgálatának kimenete (DW: debreceni, BW: bázeli győzelem, DG: eldöntetlen s a sorokban a másik csapat 5 ugyanilyen felállása szerepel).


Ennek az eredménynek megfelelően a továbbiakban már csak a debreceni 4-3-2-1 hadrendet vizsgáltuk: a mérkőzés előtt a sajtóból sejtett kezdővel készített felállásban 20.000 mérkőzést futtattunk le, amelyekben a Debrecen 53% a Basel 5 százalékban győzött, s ami mellet 42 százalékban döntetlen született.

1.8. ábra - Tudható felállás 12 órával a mérkőzés előtt tudott kezdővel.

Tudható felállás 12 órával a mérkőzés előtt tudott kezdővel.


Néhány órával a mérkőzés előtt már tudható volt a biztos kezdő névsora, ezzel újrafuttattuk a számításokat, a következő ábra mutatta felállás módosítás után. Azt kaptuk, hogy 20.000 mérkőzésből a Debrecen győz 50, az ellen 13 százalékban és döntetlennel végződik a találkozó 47 százalékban.

Hogyan változtak az arányok?
12 óranéhány
5350
513
4247

1.9. ábra - Tudható felállás néhány órával a mérkőzés előtt tudott, valódi kezdő tagokkal.

Tudható felállás néhány órával a mérkőzés előtt tudott, valódi kezdő tagokkal.


A következő ábra felállásával már (tehát a második) mérkőzés után dolgoztunk, 4-3-3-at játszott a csapat, de itt még Czvitkovics Péter és Varga Péter pozícióját felcseréltük a középpályán.

Hogyan változtak az arányok?
12 óra: 53
néhány: 50
4-3-3: 11
12 óra: 5
néhány: 13
4-3-3: 8
12 óra: 42
néhány: 47
4-3-3: 80

1.10. ábra - Utólagos 4-3-3 felállás.

Utólagos 4-3-3 felállás.


Az utolsó felállás ábra már a valóban játszott felállás.

Hogyan változtak az arányok?
12 óra: 53
néhány: 50
4-3-3: 11
valódi: 9
12 óra: 5
néhány: 13
4-3-3: 8
valódi: 15
12 óra: 42
néhány: 47
4-3-3: 80
valódi: 76

1.11. ábra - A valódi felállás.

A valódi felállás.


Összefoglalva alfejezetünk választott „Úgy érzem, nem érdemeltünk vereséget ezen a mérkőzésen, mert több helyzetünk volt.” mottója mutatja, hogy a platform eredményei összeegyeztethetőek a vezetőedző véleményével.

A szerzőről

Bátfai Norbert, nbatfai@inf.unideb.hu

Bátfai Norbert gimnáziumi éveit a Magyar Honvédség szervezésében Balassagyarmaton töltötte, tanulmányait a Bólyai János Katonai Műszaki Főiskolán folytatta, ahonnan őrmesterként szerelt le, majd 1996-ban szerzett programozó matematikusi, illetve 1998-ban kitüntetéses programtervező matematikusi oklevelet a Debreceni Egyetemen. 1998-ban megnyerte a Java Szövetség Java Programozási Versenyét.

Mobil információtechnológiai cége, az Eurosmobil, második helyezést ért el 2004-ben a Motorola JavaJáték Versenyén, ugyancsak az Eurosmobil 2004-ben a Sun és a Nokia közös Mobil Java Fejlesztői Versenyén a „Ha hívsz, támadok!” (H.A.H) hálózati ( Java EE™ szerver, Java ME™ kliens) játéksorozattal első díjat nyert. A mobil játékfejlesztés elmélete és gyakorlata és a kék (JSR 82) játékok címmel előadott az Eurosmobillal a Sun Java Fejlesztői Konferencián 2005-ben.

Társszerzője a Fantasztikus programozás [JAVACSKA] című ismeretterjesztő kalandregény sorozatnak, illetve a 2007-ben megjelent Javát tanítok [JAVATTANITOK] digitális szakkönyvnek. Szerzője a Nehogy már a mobilod nyomkodjon Téged! [NEHOGY] című könyvnek és a Nehogy már megint a mobilod nyomkodjon Téged! [NEHOGYMEGINT] című digitális szakkönyvnek.

Közel 10 évig volt a Debreceni Egyetem Informatikai Kar, Alkalmazott Matematika és Valószínűségszámítás Tanszékének munkatársa. Jelenleg ugyenezen a karon, az Információtechnológiai Tanszék egyetemi adjunktusa. Oktatási tapasztalata az alábbi előadásokon: Magas szintű programozási nyelvek 1, Magas szintű programozási nyelvek 2, Operációs rendszerek, Operációs rendszerek 2; illetve az alábbi tárgyak gyakorlatain alapul: Java esettanulmányok, J2SE hálózatok, Java appletek, CORBA, Programozás, Hálózatok, Formális nyelvek és automaták, Algoritmuselmélet, Bevezetés az informatikába, Operációs rendszerek, Alkalmazások fejlesztése WWW-re, XML-HTML, Objektumorientált programozás a középiskolában, Mobil programozás, Internettartalom menedzsment, Tartalomszolgáltatás, Magas szintű programozási nyelvek 1, Magas szintű programozási nyelvek 2.

A VISZ (Vezető Informatikusok Szövetsége) a 2008-as évben az Év Informatikai Oktatójának választotta.

2011-ben szerzett PhD doktori fokozatot informatikából. Jelen pillanatban a Debreceni Egyetem Információtechnológiai Tanszékének adjunktusa. Érdeklődése most a gépi tudatosságra irányul [COP].

A lektorokról

Kömlődi Ferenc, fkomlodi@agent.ai

Kömlődi Ferenc a jegyzet szakmai lektora. Az ELTE Bölcsészettudományi Karán 1985-ben szerzett tibeti-magyar szakos, majd a dániai Ebeltoft European Film College-ében 1995-ben filmkészítői diplomát. Számítástudománnyal, azon belül elsősorban a mesterséges intelligenciával az 1990-es évek közepe, az internet elterjedése óta foglalkozik, a témakörben több könyve és egyéb publikációja jelent meg.

Illyés Ildikó, ilyesildiko73@yahoo.co.uk

Novák Ildikó a jegyzet nyelvi lektora. 1996-ban szerzett angol középiskolai tanári és informatikus könyvtáros oklevelet a Debreceni Egyetemen.

A szakmai lektor vélekedése a könyvről

A szerző mesteri módon, gyakorlati problémákra fókuszálva, de az elméleti kérdéseket is megválaszolva, komoly tudományos-technológiai apparátust felvonultatva (és egyben humorosan), példák sokaságán mutatja be a mesterségesintelligencia-kutatás egyik legszemléletesebb és leglátványosabb, egyre nagyobb érdeklődéssel követett területét, egy olyan világot, ahol a programozás valóban szórakozássá válik.

A 2D szimulációs ligán keresztül az olvasó szintről szintre haladva mélyed el a robotfoci szerver- és kliensoldalában, a csapatépítés rejtelmeiben (Atan, Atan 1.0 és Agent2D alapú csapatok), szoftverekben/szoftvercsokrokban (RoboCup Soccer Simulator, Atan és Agent2D kliensek), szabályokban. Csapata egyre jobb teljesítményre képes, a kezdeti „buta” egyedek együttese fokozatosan alakul át jól működő multiágens-rendszerré, Csorda FC-ből a legendás Aranycsapat előtt tisztelgő Golden Team FC-vé.

A napjaink számítástudományában meghatározó jelentőségű evolúciós szemléletet végig érvényesítő könyv olvasása nemcsak szakmabelieknek és a szakmát pont most elsajátítóknak, de érdeklődő kívülállóknak is meghozza a kedvet a robotfocihoz és programozásához.

A nyelvi lektor vélekedése a könyvről

Bátfai Norbert könyvének legnagyobb erénye az a szokatlan megközelítés, mellyel egy elvont tudományterületet, mint a programozás, a laikus számára is követhető és megvalósítható közelségbe hoz. Nagyszerű ötlet mindezt a sokak számára kedvenc focival ötvözni.

A könyv leendő programozóknak íródott, de mint laikus állíthatom, hogy a szerző útmutatóit lépésről lépésre követve bárkinek sikerülhet a robotfoci programozás.

A programozást tanulók ettől gyakorlatiasabb könyvre aligha találnak.



[1] A gyökerek kapcsán történeti aspektusokkal szándékosan nem foglalkozunk a jegyzetben. A tudománytörténet iránt érdeklődők az említett alapcikk szerzőihez fűződő, a japán elsőosztályú bajnokságról elnevezett „Robot J League”-re keresve bőségesen találnak történeti forrásokat.

I. rész - A 2D szimulációs liga tárgyalása

2. fejezet - A 2D szimulációs liga

Ebben a fejezetben a kliens-szerver modellbe szervezett robotfoci

  • szerveroldali szimulációját vezetjük be

  • majd a kliens oldalon ismertetünk két ágens implementációt.

„...taktikai fölényben vagyunk, de a világ gyorsan megtanulja, amit mi most tudunk és tovább is lép rajta.”

Sebes Gusztáv [SEBES]

Az RCSS robotfoci szimulációs szervere

 

„Knowledge is only part of understanding. Genuine understanding comes from hands-on experience.”

 
 --Seymour Papert, MIT [RIS20]

A bevezető és a korábbi részekben már említettük, hogy az rcssserver egy UDP szerver, amely az rcssmanual dokumentumban rögzített protokollt beszélve a robotfoci lelke, amelyhez ennek megfelelően a játékos és edző kliens ágensek UDP kapcsolaton keresztül kapcsolódnak.

Választott mottónk jegyében azzal kezdjük a szerverrel való ismerkedést, hogy egy saját kis UDP kliens programmal megszólítjuk és kicsit csevegünk vele. Természetesen a jelen jegyzetben nem akarunk hangsúlyt fektetni a hálózati programozásra, ezért a [PP] egy bevezető UDP kliens kódjából a következő „spagetti kód” antipattern menti kódot készítjük el. A teljes és így azonnali kipróbálásra alkalmas kód közlése előtt most előzetesen kiemeljük annak robotfoci specifikus részeit.

Első dolgunk a következő sorba csomagolt protokoll parancs küldése, a szerver válaszát majd a bevágott képernyőképeken követhetjük nyomon. A kapcsolódni akaró kliens ágens init parancsának általános, BNF-szerű szintaktikáját az rcssmanual dokumentumban [RCSSMANUAL] található alábbi: (init TeamName [(version VerNum)] [(goalie)]) kifejezés rögzíti, ahol a TeamName nem terminális szimbólum

TeamName ::= (-|_|a-z|A-Z|0-9)+
                 

alakban van definiálva. Jelen példánkban az (init AranycsapatFC (version 15)) parancsot küldjük

String parancs = "(init AranycsapatFC (version 15))";
                  

amelyre a szerver az alábbi (init l 1 before_kick_off) sorral kezdődő választ adja, amelyben a szerver az oldalt, hogy jobb (r) vagy bal (l), a játékos számát (1-11) illetve a játék aktuális állapotkódját közli az ágenssel, az alábbi általános, az [RCSSMANUAL] dokumentumból idézett szintaxisnak megfelelően.

(init Side Unum PlayMode)
Side ::= l | r
Unum ::= 1 | 11
PlayMode ::= one of play modes
                 

Konkrét (init l 1 before_kick_off) esetünkben tehát az ágens a bal oldalon van, száma 1-es és a kirúgás előtt vagyunk, amit a következő monitorbeli ábrán kicsit előreszaladva meg is mutatunk.

2.1. ábra - Az 1-es sorszámú ágens a bal oldalon, kirúgás előtt a balhátvéd pozíciójában.

Az 1-es sorszámú ágens a bal oldalon, kirúgás előtt a balhátvéd pozíciójában.


Annyiban ugrottunk előre, hogy a kép elkattintásakor már túl vagyunk a (move -35 -19) paranccs kiadásán is, ami a balhátvéd pozíciójába tudja mozgatni a játékost abban az esetben, ha még nem volt meg a kirúgás. De ez előtt a (server_param és (player_param kezdetű sorok formájában számos beállítást közöl a klienssel a szerver. Ezeket (ebben a fejlett RCSS verzióban már) könnyű szemmel is olvasni, hiszen az érték mellett annak nevét is küldi a szerver, ezeket láthatjuk a következő képernyőképen.

A szereplő hosztnév:portszám kiírást a mi programunk szövi a kimenetbe, hogy ne csak a szerver „mátrix világát” lássuk ott (a kis nyilakkal a szerver és a kliens közötti kommunikáció irányát mutatjuk, a -> irány mutat a szerver felé).

->
(init AranycsapatFC (version 15))
<-

127.0.0.1:49518
(init l 1 before_kick_off)
<-
127.0.0.1:49518
(server_param (audio_cut_dist 50)(auto_mode 0)(back_dash_rate 0.6)(back_passes 1)(ball_accel_max 2.7)(ball_decay 0.94)(ball_rand 0.05)(ball_size 0.085)(ball_speed_max 3)(ball_stuck_area 3)(ball_weight 0.2)(catch_ban_cycle 5)(catch_probability 1)(catchable_area_l 1.2)(catchable_area_w 1)(ckick_margin 1)(clang_advice_win 1)(clang_define_win 1)(clang_del_win 1)(clang_info_win 1)(clang_mess_delay 50)(clang_mess_per_cycle 1)(clang_meta_win 1)(clang_rule_win 1)(clang_win_size 300)(coach 0)(coach_port 6001)(coach_w_referee 0)(connect_wait 300)(control_radius 2)(dash_angle_step 45)(dash_power_rate 0.006)(drop_ball_time 100)(effort_dec 0.005)(effort_dec_thr 0.3)(effort_inc 0.01)(effort_inc_thr 0.6)(effort_init 1)(effort_min 0.6)(extra_half_time 100)(extra_stamina 50)(forbid_kick_off_offside 1)(foul_cycles 5)(foul_detect_probability 0.5)(foul_exponent 10)...
<-
127.0.0.1:49518
(player_param (allow_mult_default_type 0)(catchable_area_l_stretch_max 1.3)(catchable_area_l_stretch_min 1)(dash_power_rate_delta_max 0)(dash_power_rate_delta_min 0)(effort_max_delta_factor -0.004)(effort_min_delta_factor -0.004)(extra_stamina_delta_max 50)(extra_stamina_delta_min 0)(foul_detect_probability_delta_factor 0)(inertia_moment_delta_factor 25)(kick_power_rate_delta_max 0)(kick_power_rate_delta_min 0)(kick_rand_delta_factor 1)(kickable_margin_delta_max 0.1)(kickable_margin_delta_min -0.1)(new_dash_power_rate_delta_max 0.0008)(new_dash_power_rate_delta_min -0.0012)(new_stamina_inc_max_delta_factor -6000)(player_decay_delta_max 0.1)(player_decay_delta_min -0.1)(player_size_delta_factor -100)(player_speed_max_delta_max 0)(player_speed_max_delta_min 0)(player_types 18)(pt_max 1)(random_seed 1317801592)(stamina_inc_max_delta_factor 0)(subs_max 3))
<-
127.0.0.1:49518
(player_type (id 0)(player_speed_max 1.05)(stamina_inc_max 45)(player_decay 0.4)(inertia_moment 5)(dash_power_rate 0.006)(player_size 0.3)(kickable_margin 0.7)(kick_rand 0.1)(extra_stamina 50)(effort_max 1)(effort_min 0.6)(kick_power_rate 0.027)(foul_detect_probability 0.5)(catchable_area_l_stretch 1))
<-
127.0.0.1:49518
(player_type (id 1)(player_speed_max 1.05)(stamina_inc_max 47.1974)(player_decay 0.396567)(inertia_moment 4.91418)(dash_power_rate 0.00563377)(player_size 0.3)(kickable_margin 0.61053)(kick_rand 0.0105305)(extra_stamina 74.074)(effort_max 0.903704)(effort_min 0.503704)(kick_power_rate 0.027)(foul_detect_probability 0.5)(catchable_area_l_stretch 1.00707))
<-
127.0.0.1:49518
(player_type (id 2)(player_speed_max 1.05)(stamina_inc_max 41.9072)(player_decay 0.300487)(inertia_moment 2.51216)(dash_power_rate 0.00651546)(player_size 0.3)(kickable_margin 0.648339)(kick_rand 0.0483387)(extra_stamina 55.845)(effort_max 0.97662)(effort_min 0.57662)(kick_power_rate 0.027)(foul_detect_probability 0.5)(catchable_area_l_stretch 1.13732))

...
                  

A csatlakozás után kiadhatjuk a move parancsot, amelynek általános formátuma

(move X Y)
X ::= -52.5 ~ 52.5
Y ::= -34 ~ 34
                  

ahol a szereplő számok egyszerűen a futball pálya méretei. Mi most a (move -35 -19) paranccsal a balhátvéd pozícióba állítottuk a szóban forgó ágensünket, ahogyan azt a korábbi képen is megmutattuk.

parancs = "(move -35 -19)";
                  

->
(move -35 -19)
<-

(sense_body 0 (view_mode high normal) (stamina 8000 1 130600) (speed 0 0) (head_angle 0) (kick 0) (dash 0) (turn 0) (say 0) (turn_neck 0) (catch 0) (move 1) (change_view 0) (arm (movable 0) (expires 0) (target 0 0) (count 0)) (focus (target none) (count 0)) (tackle (expires 0) (count 0)) (collision none) (foul  (charged 0) (card none)))
<-
127.0.0.1:51959
(see 0 ((f c) 39.6 28 0 0) ((f c t) 38.1 -23 0 0) ((f r t) 89.1 -10) ((f r b) 102.5 31) ((f g r b) 90.9 17) ((g r) 89.1 12) ((f g r t) 88.2 8) ((f p r b) 81.5 29) ((f p r c) 73.7 15) ((f p r t) 70.8 -1) ((F) 1.5 -131) ((f t 0) 40.4 -30) ((f t r 10) 49.4 -24) ((f t r 20) 58.6 -20) ((f t r 30) 68 -17) ((f t r 40) 77.5 -15) ((f t r 50) 87.4 -13) ((f t l 10) 32.1 -39) ((f b r 30) 87.4 42) ((f b r 40) 94.6 38) ((f b r 50) 102.5 34) ((f r 0) 94.6 12) ((f r t 10) 92.8 6) ((f r t 20) 92.8 -1) ((f r t 30) 92.8 -7) ((f r b 10) 96.5 17) ((f r b 20) 100.5 23) ((f r b 30) 104.6 28) ((b) 40.4 28) ((l r) 87.4 90))
<-(see
127.0.0.1:51959
(sense_body 0 (view_mode high normal) (stamina 8000 1 130600) (speed 0 0) (head_angle 0) (kick 0) (dash 0) (turn 0) (say 0) (turn_neck 0) (catch 0) (move 1) (change_view 0) (arm (movable 0) (expires 0) (target 0 0) (count 0)) (focus (target none) (count 0)) (tackle (expires 0) (count 0)) (collision none) (foul  (charged 0) (card none)))
<-
127.0.0.1:51959
(sense_body 0 (view_mode high normal) (stamina 8000 1 130600) (speed 0 0) (head_angle 0) (kick 0) (dash 0) (turn 0) (say 0) (turn_neck 0) (catch 0) (move 1) (change_view 0) (arm (movable 0) (expires 0) (target 0 0) (count 0)) (focus (target none) (count 0)) (tackle (expires 0) (count 0)) (collision none) (foul  (charged 0) (card none)))
<-
127.0.0.1:51959
(see 0 ((f c) 39.6 28) ((f c t) 38.1 -23) ((f r t) 89.1 -10) ((f r b) 102.5 31) ((f g r b) 90.9 17) ((g r) 89.1 12) ((f g r t) 88.2 8) ((f p r b) 81.5 29) ((f p r c) 73.7 15) ((f p r t) 70.8 -1) ((F) 1.5 -131) ((f t 0) 40.4 -30) ((f t r 10) 49.4 -24) ((f t r 20) 58.6 -20) ((f t r 30) 68 -17) ((f t r 40) 77.5 -15) ((f t r 50) 87.4 -13) ((f t l 10) 32.1 -39 0 0) ((f b r 30) 87.4 42) ((f b r 40) 94.6 38) ((f b r 50) 102.5 34) ((f r 0) 94.6 12) ((f r t 10) 92.8 6) ((f r t 20) 92.8 -1) ((f r t 30) 92.8 -7) ((f r b 10) 96.5 17) ((f r b 20) 100.5 23) ((f r b 30) 104.6 28) ((b) 40.4 28) ((l r) 87.4 90))
<-
127.0.0.1:51959
(sense_body 0 (view_mode high normal) (stamina 8000 1 130600) (speed 0 0) (head_angle 0) (kick 0) (dash 0) (turn 0) (say 0) (turn_neck 0) (catch 0) (move 1) (change_view 0) (arm (movable 0) (expires 0) (target 0 0) (count 0)) (focus (target none) (count 0)) (tackle (expires 0) (count 0)) (collision none) (foul  (charged 0) (card none)))

                  

Ezután a monitorban elindíthatjuk a játékot, azaz elvégezhetjük a középkezdést, tegyünk így! Ezt a kliensben most úgy tudjuk meg, hogy figyeljük a ciklusonként kapott (sense_body vagy (see kezdetű sorokat, ahol a (see Time ObjInfo+) protokoll szerint az első szám az idő, ha ez nullától különböző, akkor elkezdődött a találkozó, amit most egyszerűen ennyiben vizsgálunk:

        if (szervertol.startsWith("(sense_body")) {
          if (!szervertol.startsWith("(sense_body 0")) {
            break;
          }
        }

        if (szervertol.startsWith("(see")) {
          if (!szervertol.startsWith("(see 0")) {
            break;
          }
        }
                  

azaz a break-el kiugrunk a ciklusból, ha a (sense_body vagy (see kezdetű sor nem nullával folytatódik, tehát ha nem a 0. ciklusban vagyunk, akkor kilépünk a most végtelen ciklusunkból, majd kiadjuk a 45 fokkal elfordító parancsot minden ciklusban, amelynek megfelelően játékosunk szépen forogni fog.

parancs = "(turn 45)";
                  

->
(turn 45)
<-

(sense_body 3 (view_mode high normal) (stamina 8000 1 130600) (speed 0 0) (head_angle 0) (kick 0) (dash 0) (turn 0) (say 0) (turn_neck 0) (catch 0) (move 1) (change_view 0) (arm (movable 0) (expires 0) (target 0 0) (count 0)) (focus (target none) (count 0)) (tackle (expires 0) (count 0)) (collision none) (foul  (charged 0) (card none)))
->
(turn 45)
<-

(see 3 ((f c) 39.6 28) ((f c t) 38.1 -23) ((f r t) 89.1 -10) ((f r b) 102.5 31) ((f g r b) 90.9 17) ((g r) 89.1 12) ((f g r t) 88.2 8) ((f p r b) 81.5 29) ((f p r c) 73.7 15) ((f p r t) 70.8 -1) ((F) 1.5 -131) ((f t 0) 40.4 -30) ((f t r 10) 49.4 -24) ((f t r 20) 58.6 -20) ((f t r 30) 68 -17) ((f t r 40) 77.5 -15) ((f t r 50) 87.4 -13) ((f t l 10) 32.1 -39 0 0) ((f b r 30) 87.4 42) ((f b r 40) 94.6 38) ((f b r 50) 102.5 34) ((f r 0) 94.6 12) ((f r t 10) 92.8 6) ((f r t 20) 92.8 -1) ((f r t 30) 92.8 -7) ((f r b 10) 96.5 17) ((f r b 20) 100.5 23) ((f r b 30) 104.6 28) ((b) 40.4 28) ((l r) 87.4 90))
->
(turn 45)
<-

(sense_body 5 (view_mode high normal) (stamina 8000 1 130600) (speed 0 0) (head_angle 0) (kick 0) (dash 0) (turn 0) (say 0) (turn_neck 0) (catch 0) (move 1) (change_view 0) (arm (movable 0) (expires 0) (target 0 0) (count 0)) (focus (target none) (count 0)) (tackle (expires 0) (count 0)) (collision none) (foul  (charged 0) (card none)))
->
(turn 45)
<-
                  

Végezetül közöljük a most tárgyalt program teljes, kipróbálható kódját. Egyszerű szekvenciális szerkezetű forrás következik, összeállítunk egy parancsot, elküldjük az UDP kapcsolaton keresztül, majd többet-kevesebbet olvasunk onnan és a mindkét irányú forgalmat naplózzuk a sztender kimenetre.

public class Kliens {

  public static final int BUFFER_MERET = 4096;

  public static void main(String[] args) {
    try {
      java.net.DatagramSocket kapu = new java.net.DatagramSocket();
      byte[] buffer = new byte[BUFFER_MERET];

      String parancs = "(init AranycsapatFC (version 15))";
      System.out.println("->\n" + parancs + "\n<-\n");

      byte[] puffer = parancs.getBytes();
      System.arraycopy(puffer, 0, buffer, 0, puffer.length);
      java.net.InetAddress hoszt =
              java.net.InetAddress.getByName("localhost");

      java.net.DatagramPacket kuldendoCsomag =
              new java.net.DatagramPacket(buffer, puffer.length,
              hoszt, 6000);
      kapu.send(kuldendoCsomag);

      java.net.DatagramPacket fogadandoCsomag =
              new java.net.DatagramPacket(buffer, buffer.length);
      kapu.receive(fogadandoCsomag);

      int port = fogadandoCsomag.getPort();
      hoszt = fogadandoCsomag.getAddress();
      System.out.println(hoszt.getHostAddress() + ":" + port);
      System.out.println(new String(fogadandoCsomag.getData(),
              0, fogadandoCsomag.getLength()));

      for (int i = 0; i < 20; ++i) {
        fogadandoCsomag =
                new java.net.DatagramPacket(buffer, buffer.length);
        kapu.receive(fogadandoCsomag);

        port = fogadandoCsomag.getPort();
        hoszt = fogadandoCsomag.getAddress();
        System.out.println("<-\n" + hoszt.getHostAddress() + ":" + port);

        System.out.println(new String(fogadandoCsomag.getData(),
                0, fogadandoCsomag.getLength()));
      }

      parancs = "(move -35 -19)";
      System.out.println("->\n" + parancs + "\n<-\n");

      puffer = parancs.getBytes();
      System.arraycopy(puffer, 0, buffer, 0, puffer.length);

      buffer[puffer.length] = 0x0;
      // parancs = "(turn 45)\0";
      // (warning message_not_null_terminated)

      kuldendoCsomag =
              new java.net.DatagramPacket(buffer, puffer.length + 1,
              hoszt, port);
      kapu.send(kuldendoCsomag);
      fogadandoCsomag =
              new java.net.DatagramPacket(buffer, buffer.length);
      kapu.receive(fogadandoCsomag);

      System.out.println(new String(fogadandoCsomag.getData(),
              0, fogadandoCsomag.getLength()));

      for (;;) {

        fogadandoCsomag =
                new java.net.DatagramPacket(buffer, buffer.length);
        kapu.receive(fogadandoCsomag);

        port = fogadandoCsomag.getPort();
        hoszt = fogadandoCsomag.getAddress();
        System.out.println("<-\n" + hoszt.getHostAddress() + ":" + port);

        String szervertol = new String(fogadandoCsomag.getData(),
                0, fogadandoCsomag.getLength());

        System.out.println(szervertol);

        if (szervertol.startsWith("(sense_body")) {
          if (!szervertol.startsWith("(sense_body 0")) {
            break;
          }
        }

        if (szervertol.startsWith("(see")) {
          if (!szervertol.startsWith("(see 0")) {
            break;
          }
        }
      }

      parancs = "(turn 45)";

      for (;;) {

        System.out.println("->\n" + parancs + "\n<-\n");

        puffer = parancs.getBytes();
        System.arraycopy(puffer, 0, buffer, 0, puffer.length);

        buffer[puffer.length] = 0x0;
        // parancs = "(turn 45)\0";
        // (warning message_not_null_terminated)

        kuldendoCsomag =
                new java.net.DatagramPacket(buffer, puffer.length + 1,
                hoszt, port);
        kapu.send(kuldendoCsomag);
        fogadandoCsomag =
                new java.net.DatagramPacket(buffer, buffer.length);
        kapu.receive(fogadandoCsomag);

        System.out.println(new String(fogadandoCsomag.getData(),
                0, fogadandoCsomag.getLength()));

        Thread.sleep(100);

      }

    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
                  

A forrás kipróbálásához mindössze ennyit kell tenned:

                                 
[norbert@matrica konyvbeli]$ javac Kliens.java 
[norbert@matrica konyvbeli]$ java Kliens
                             

mielőtt persze a szervert és a monitort már elindítottad.

Megjegyezhetjük, hogy a J2SE 1.4 (Merlin) kiadás óta már a Java SE platformon is írhatsz nem blokkolódó multiplexelt I/O-t (a [PP]-ben találsz is erre példát). A mostani Kliens osztályt ennek megfelelően, azaz a java.nio csomagra alapozva átírva jobb képet kaphatsz a szerver működéséről.

Az RCSS szerver naplózása

A bevezető részekben már említetést tettünk az rcg és rcl állományokról, most ki is nyitjuk ezeket. Tárgyaltuk, hogy az RCSS szerver alapértelmezésben menti az rcg és rcl állományokat abba a könyvtárba, ahonnan a szervert elindították; a dátumból, az időből, a csapatok neveiből és az eredményből képzett állományneveken, ilyen név például a 20110915 1111-MightyFC_3-vs-Bcsapat_1.rcg. vagy a 201109151111-MightyFC_3-vs-Bcsapat_1.rcl. Az RCSS szerver kilépéskor nevezi át az állományokat, amelyek addig incomplete.rcg, incomplete.rcl neveken szerepelnek. A teljes mérkőzés után előbbi tipikusan 17, utóbbi 6 megabájt méretű (szöveges, olvasható fájlokról van szó, amelyeket hatékonyan tudsz tömöríteni, s ezért találkozol tipikusan tömör formában velük).

Az rcg állományok szerkezetéről az [RCSSMANUAL] csak a version 1-től a version 3-ig nyilatkozik (de azt megtudjuk, hogy a mágikus ULG = Unix logfile), miközben ez a verzió már az ötnél jár, a kívánt verziószám parancssorban is átadható a szervernek, idézve a server::help argumentummal indított v15-ös szerver kimenetéből:

                                 
        server::game_log_version=<INTEGER>
                current value: 5
                             

de kinyitva persze intuíciónkra hagyatkozhatunk:

                                 
ULG5
(server_param (audio_cut_dist 50)(auto_mode 0)(back_dash_rate 0.6)(back_passes...
...
(show 89 ((b) 52.5523 -6.3164 0.2541 0.2602) ((l 1) 0 0x9 -49.8548 -0.0283 0 -0...
(playmode 90 goal_l)
(team 90 MightyFC Bcsapat 1 0)
(show 90 ((b) 52.8061 -6.0452 0 0) ((l 1) 0 0x9 -49.8548 -0.0283 0 -0 -3.864 0...
(show 90 ((b) 52.8061 -6.0452 0 0) ((l 1) 0 0x9 -50 -0 0 -0 -3.864 0 (v h...
                             

s mint a nyílt forráskódú projektek esetén általában: megnézhetjük a forrást. Tegyük most a legegyszerűbb esetben, a fejlécet az initsenderlogger.cpp forrásban http://sourceforge.net/projects/sserver/files/rcssserver/15.0.1/ definiált

void
InitSenderLoggerV5::sendHeader()
{
    transport() << "ULG5" << std::endl;
}
                             

módszer nyomtatta. A források visszaolvasását a gólnál érdemes folytatni, ami persze nem tipikus, de sokkal rövidebb, mint ahogyan a bevágott részlet (team 90 MightyFC Bcsapat 1 0) sora mutatja. Ezt a Logger::writeGameLogImpl() módszeren keresztül, az ugyancsak a initsenderlogger.cpp forrásból a

void
InitSenderLoggerV4::sendTeam()
{
    serializer().serializeTeam( transport(),
                                stadium().time(),
                                stadium().teamLeft(),
                                stadium().teamRight() );
    transport() << std::endl;
}
                             

nyomtatja ki.

Az rcl állományok szerkezete beszédesebb, ezek esetleges olvasásához elég az intuíciónk, ha az ugyancsak az első gól környéki részt akarjuk „olvasni”:

89,0    Recv MightyFC_6: (turn 0)  
89,0    Recv Bcsapat_6: (turn 1)   
89,0    Recv MightyFC_9: (turn 0)  
89,0    Recv MightyFC_3: (turn 1)
90,0    (referee goal_l_1)
90,0    Recv Bcsapat_7: (move -22 -12)
90,0    Recv Bcsapat_4: (move -1 0)  
90,0    Recv MightyFC_10: (move -30 0)
90,0    Recv Bcsapat_1: (move -50 0)
                             

láthatjuk a parancsokat, végestelen végig. Ne lepődjünk meg, ha az (ön)gólt lövő 11-es játékos kick parancsáig elég sokat (90-től 60-ig) kell visszamennünk a fájl eleje felé:

66,0    Recv Bcsapat_3: (dash 100) 
66,0    Recv Bcsapat_8: (turn 13)  
66,0    Recv MightyFC_8: (turn 0)  
66,0    Recv Bcsapat_2: (turn 0) 
66,0    Recv Bcsapat_11: (kick 85 87)
66,0    Recv MightyFC_2: (turn 0)
66,0    Recv Bcsapat_5: (turn 0)     
66,0    Recv MightyFC_11: (turn 0)
                             

ami tehát a 66,0 Recv Bcsapat_11: (kick 85 87) sor szerint a 66. szimulációs ciklusban történt. De ne szaladjunk ennyire előre, egyelőre a grafikusan megjelenített mérkőzéseket figyeljük! Előre haladva a jegyzetben majd minden a helyére kerül.

Az RCSS futballpálya

A pálya méretarányai megfelelnek a FIFA pályára vonatkozó előírásainak [FIFA], az RCSS futballpálya 105 méter hosszú és 68 méter széles.

2.2. ábra - Az RCSS futballpálya koordináta rendszere.

Az RCSS futballpálya koordináta rendszere.


A pályára illesztett síkbeli koordinátarendszer origója a középkezdés pontjára van illesztve.

2.3. ábra - A szögek értelmezése az RCSS futballpálya koordináta rendszerében.

A szögek értelmezése az RCSS futballpálya koordináta rendszerében.


A szögeket az óramutató járásával megegyező irányban pozitívban, ellenkező irányban negatívban mérjük.

Az ágensek kapcsolata a szimulált világgal

A szimulált mérkőzés másodpercenként 10 szimulációs ciklussan 6000 lépésen át, azaz 10 (félindőnként tehát kétszer 5) percig tart. Egy szimulációs ciklusban a tipikus ágens felveszi érzeteit a szervertől és ezek elemzése alapján elküldi válaszait a szervernek. A szimuláció valós időben történik, tehát ha a kliens feldolgozása például lassú, akkor kimaradhat a parancsa a szóban forgó ciklusból.

2.4. ábra - Az RCSS szimulációs ciklus.

Az RCSS szimulációs ciklus.

A játékos modellje

Az RCSS szimulációban a játékosnak a pályán van

  • teste (body), s annak iránya;

  • nyaka (neck), s annak iránya;

  • állóképessége (stamina);

A test iránya és az állóképesség a játékos mozgatásában játszik főszerepet. A nyak, azaz a játékos fejének iránya az általa látott terület irányát jelöli ki.

A következő, az rcssmonitor és a soccerwindow2 programokból kivágott képeken a belső kör maga a játékos, a külső az a terület, amin belül a játékos meg tudja rúgni a labdát. A 90 fokos tortaszelet pedig az a terület, amilyen irányban a játékos lát, ez gyakorlatilag a fej „iránya”.

2.5. ábra - Fej és test egy irányban, az rcssmonitor-ban.

Fej és test egy irányban, az rcssmonitor-ban.

2.6. ábra - Fej és test egy irányban, a soccerwindow2-ben.

Fej és test egy irányban, a soccerwindow2-ben.

2.7. ábra - Fej 90 fokkal elforgatva, az rcssmonitor-ban.

Fej 90 fokkal elforgatva, az rcssmonitor-ban.

2.8. ábra - Fej 90 fokkal elforgatva, a soccerwindow2-ben.

Fej 90 fokkal elforgatva, a soccerwindow2-ben.

A mozgás modellje

A pályán történő mozgások egy sík koordináta rendszerben mennek végbe. A következő ábrán az rcssmanual dokumentum jelöléseivel azt tüntettük fel, hogy egy játékos éppen a (px0, py0) koordinátájú pozícióban van, a labda pedig a (pxt, pyt) koordinátájúban. A játékos sebességvektora v0, a labdáé vt. Ennek megfelelően a labda játékoshoz relatív koordinátái a (prx, pry) és például az iránya arctan(pry / prx) - a0 [RCSSMANUAL].

2.9. ábra - Mozgás a pályán.

Mozgás a pályán.

A játékos ágensek által kiadható főbb parancsok a következők.

  • A (move x_koordináta y_koordináta) parancs: paramétereiben megadott koordinátára állítja a játékost, de csakis középkezdéskor, azaz a félidők elején és a gólok után van hatása, szimulációs ciklusonként egyszer adható ki. A középkezdés felállásának megadásakor azt tételezzük fel, hogy a szóban forgó csapat a bal oldalon áll, azaz az x koordináták mindenképpen negatívak!

    Például: (move -35 -19).

  • A (kick -100_tól_+100_ig_az_erő -180_tól_+180_ig_a_szög) parancs: megrúgja a labdát az adott erővel az adott irányba, szimulációs ciklusonként egyszer adható ki.

    Például: (kick 60 10).

  • A (dash -100_tól_+100_ig_az_erő) parancs: a játékost az adott erővel meglöki abban az irányban, amelyben a játékos teste áll, szimulációs ciklusonként egyszer adható ki. A játékos állóképessége a megadott erővel, ha annak előjele negatív, akkor annak kétszeresével csökken. Fontos látni, hogy adott esetben a játékos testének iránya és sebességvektora (az ábrán a v0) eltérő irányú lehet (például éppen ciklusokon át mozog a játékos, amikor közben kap egy turn majd egy dash parancsot a következő ciklusban).

    Például: (dash 40).

  • A (turn -180_tól_+180_ig_a_szög) parancs: elfordítja a játékos testét. A szög a test aktuális álláshoz relatív.

  • A (turn_neck -180_tól_+180_ig_a_szög) parancs: a játékos testétől függetlenül, ahhoz relatívan (és maximum -90, +90) tartományban elforgatja a fejét. Fontos, hogy ezzel (és nem csak a turn paranccsal, ami ugye a testtel együtt nyilván a fejet is fordítja) egyetemben a játékos látószöge is változik. Szimulációs ciklusonként egyszer adható ki, de lehet együtt hívni a turn, move vagy kick parancsokkal.

    Például: (turn 15).

  • A (say üzenet) parancs: a játékosok közötti kommunikációt szolgálja.

  • A (catch -180_tól_+180_ig_a_szög) parancs: ez egy kapus parancs, (a kapus testéhez relatív) adott irányban megpróbálja elkapni a labdát.

  • A (change_view narrow_vagy_normal_avagy_wide low_vagy_high) parancs: az ágens látását szabályozza: ha például a látás minőségét high értékre emeljük, ezzel egyben megduplázzuk a két látás-észlelés (a szervertől kapott see parancsok) között eltelt időt.

    Például: (change_view narrow high).

[Tipp]Előzetesen az RCSS verziókról

Hamarosan kitérünk majd rá, hogy az RCSS élő platformként folyamatosan fejlődik, ezt hangsúlyosan figyelhetjük meg az újabb és újabb parancsok megjelenésénél. A v8-as verzóban megjelent például az (attentionto) parancs.

  • Az (attentionto csapat szám) parancs: ha az adott csapat megadott játékosának vannak a (say) paranccsal küldött üzenetei az következő ciklusban, akkor ezek közül hall; egyébként az adott csapat tagjai közül valakitől. Újabb kiadott (attentionto) parancs felülírja a korábbit, az (attentionto off) kikapcsolja ezt a fókuszált figyelmet.

    Például: (attentionto l 5).

A paraméterek tekintetében pontos eligazítást ad az RCSS szerver v8-as verzójának NEWS állománya, miszerint a csapat az alábbi terminálisok valamelyike lehet: opp, our, l, r, left, right, avagy maga a csapat pontos neve.

A látási érzékelés

Már találkoztunk a látási érzékeléssel, amikor a szerver see-vel kezdődő válaszára vetettünk egy pillantást saját UDP kliensünkkel:

(see 0 ((f c) 39.6 28 0 0) ((f c t) 38.1 -23 0 0) ((f r t) 89.1 -10) ((f r b) 102.5 31) ((f g r b) 90.9 17) ((g r) 89.1 12) ((f g r t) 88.2 8) ((f p r b) 81.5 29) ((f p r c) 73.7 15) ((f p r t) 70.8 -1) ((F) 1.5 -131) ((f t 0) 40.4 -30) ((f t r 10) 49.4 -24) ((f t r 20) 58.6 -20) ((f t r 30) 68 -17) ((f t r 40) 77.5 -15) ((f t r 50) 87.4 -13) ((f t l 10) 32.1 -39) ((f b r 30) 87.4 42) ((f b r 40) 94.6 38) ((f b r 50) 102.5 34) ((f r 0) 94.6 12) ((f r t 10) 92.8 6) ((f r t 20) 92.8 -1) ((f r t 30) 92.8 -7) ((f r b 10) 96.5 17) ((f r b 20) 100.5 23) ((f r b 30) 104.6 28) ((b) 40.4 28) ((l r) 87.4 90))
                  

ezt látja tehát a kliens, hogyan értelmezi? Hogy ezt lássuk, kezdjük el feldolgozni!

A protokoll rögzíti, hogy a vizsgálandó válasz szerkezete a következő:

(see Time ObjInfo+)
                  

azaz a (see után jön egy idő, hogy hányadik szimulációs ciklust rójuk éppen, majd legalább egy, de akár több ObjInfo, amit a [RCSSMANUAL] BNF-ben így definiál:

ObjInfo ::= (ObjName Distance Direction DistChange DirChange BodyFacingDir HeadFacingDir )
| (ObjName Distance Direction DistChange DirChange
| (ObjName Distance Direction)
| (ObjName Direction)
ObjName ::= (p [”Teamname” [UniformNumber [goalie]]])
| (b)
| (g [l|r])
| (f c)
| (f [l|c|r] [t|b])
| (f p [l|r] [t|c|b])
| (f g [l|r] [t|b])
| (f [l|r|t|b] 0)
| (f [t|b] [l|r] [10|20|30|40|50])
| (f [l|r] [t|b] [10|20|30])
| (l [l|r|t|b])
| (B)
| (F)
| (G)
| (P)
                  

A (see 0 ((f c) 39.6 28 0 0) tehát azt mondja, hogy a 0. pillanatban, azaz az első szimulációs ciklusban látom a f(lag) c zászlót, 39,6 méter távolban, 28 fokos szögben. Vegyük a következő ObjInfo-t: ((f c t) 38.1 -23 0 0)! Itt az ObjName az (f c t) ami a | (f [l|c|r] [t|b]) ágra illeszkedik és a középvonal felső oldalvonalnál álló zászlóját jelenti, amelyet most éppen -23 fokos szög alatt 38,1 méter távolban látok. Ne felejtsük el, hogy mindezek az információk zajjal vannak terhelve, s tanulmányozzuk a következő ábrát, ahol a látott további zászlók közül még néhányat feltüntettünk.

2.10. ábra - A látási információk feldolgozása.

A látási információk feldolgozása.

A látási érzékelés jellemzőit (látott szög, minőség) a (change_view látott_szög minőség) parancs megfelelő paraméterezésével tudod állítani.

A hallási érzékelés

A játékosok tipikusan a tőlük 50 méter távolságon (lásd audio_cut_dist szerverparaméter) belül kiadott (say) parancsokat hallják. Nagyon fontos, hogy a játékosok között csakis ez a kommunkikáció engedélyezett és minden más szigorúan tilos!

Játsszva a képzavarral: alább láthatjuk, hogy mit hallhat az 1-es játékos

(hear 12 -141 our 2 "bedob")
                  

ha a 2-es ezt mondta a környezetében:

(say bedob)
                  

a hallot információk sorrendben az idő (szimulációs ciklusok száma), a relatív szög, ahonnan jött a hang, csapattársa, méghozzá a 2-es mondta azt, hogy bedob.

A hallási érzékelés jellemzőit a (attentionto) paranccsal tudod állítani.

A testi érzékelés

A testi érzékelés a játékos állapotát mutatja meg, megjelenési formájával már sokat találkoztál eddig is, ez a szervertől érkező (sense_body) parancs. Például a

sense_body 14 (view_mode high narrow) (stamina 8000 1 130600) (speed 0 0) (head_angle 0) (kick 0) (dash 0) (turn 0) (say 4) (turn_neck 0) (catch 0) (move 1) (change_view 4) (arm (movable 4) (expires 19) (target 20 45) (count 1)) (focus (target none) (count 0)) (tackle (expires 0) (count 0)) (collision none) (foul  (charged 0) (card none)))
                  

az idő után közli a látás aktuálisan beállított paramétereit, aztán a játékos erején, sebességén át számos számlálót, végül még azt is, van-e lapja a játékosnak. (A számlálókat a megbízhatatlan kommunikáció feletti amúgy is zajos csatorna adminisztrálására használhatod fel.)

A szoftverek telepítése

Az rcssserver programot és társait, a programok beszerzését már felvillantottuk a bevezető részben, most néhány szóval segítjük a telepítésüket, ami a két elterjedt rendszerben tipikusan eltér.

Telepítés GNU/Linux rendszerekben

Ha abban az izgalmas helyzetben vagy, hogy éppen most teszel fel egy új Linuxot, akkor járhatsz a királyi úton, miszerint már a telepítőben megadhatod, hogy akarsz majd robotfocizni.

2.11. ábra - A robotikával kapcsolatos programok telepítése egy aktuális Fedora 15 disztribúcióban.

A robotikával kapcsolatos programok telepítése egy aktuális Fedora 15 disztribúcióban.


2.12. ábra - A robotikával kapcsolatos szoftverek finomabb szelekciója egy aktuális Fedora 15 disztribúcióban.

A robotikával kapcsolatos szoftverek finomabb szelekciója egy aktuális Fedora 15 disztribúcióban.


Az elterjedt disztribúciókra csomagból is telepítheted a robotfoci szóban forgó hozzávalóit. Fedora 15 esetén mindent tartalmaz például a rcssserver-gui-15.0.0-1.fc15.x86_64.rpm csomag, de egyesével is felteheted őket akár a csomagkezelődben rákeresve is természetesen.

Kicsit munkásabb a forrásból történő telepítés, ami minden szóban forgó (autoconf-os) csomag telepítése kapcsán teljesen egy kaptafára megy, s majd a japán csapat szoftvereinek telepítésénél mutatjuk meg. Ennek előnye, hogy a legeslegfrissebb verziót tudod feltenni, használni. Példaképpen felvillantjuk a rcsslogplayer forrásból történő telepítését:

[norbert@matrica RoboCup]$ mv ../Downloads/rcsslogplayer-15.0.0.tar.gz .
[norbert@matrica RoboCup]$ gunzip rcsslogplayer-15.0.0.tar.gz 
[norbert@matrica RoboCup]$ tar xvf rcsslogplayer-15.0.0.tar
[norbert@matrica RoboCup]$ cd rcsslogplayer-15.0.0
[norbert@matrica rcsslogplayer-15.0.0]$ ./configure --with-qt4-moc=/usr/bin/moc-qt4
[norbert@matrica rcsslogplayer-15.0.0]$ make
[norbert@matrica rcsslogplayer-15.0.0]$ su
Password: 
[root@matrica rcsslogplayer-15.0.0]# make install
                  

ez láthatólag egy egyszerű GNU autoconf-os telepítés, amelyet részletezni majd a soccerwindow2 telepítésénél fogunk.

Ha esetleg mégsem sikerülne feltelepíteni, akkor a posztot kommentelve egy kérdéssel megpróbálhatod megosztani a problémádat.

Telepítés Windows környezetekben

Windows alá egyszerű zip állományokként tudot letölteni a szóban forgó szoftvereket, amit csak ki kell csomagolnod és futtathatod az exe állományokat. Annyit kell megjegyeznünk, hogy tipikusan a legfrissebb verziókból nem találsz ilyen zipelt Windows binárist, de egy-két kiadással korábbiból már ugyanott igen.

RCSS verziók

Az RCSS, azaz a 2D szimulációs liga megismerésének fő pillére a kliens ágensek és a szerver közötti, a megelőző alfejezetekben is tárgyalt protokolljának az ismerete, aminek fő forrása a 2003-ból származó [RCSSMANUAL] kézikönyv. Akkoriban például az rcssserver a v7 és a v9 verziói között járt. A lényegi dolgok megismerésére elég ugyan a kézikönyv, de az újabb finomságokat is érdekes és érdemes tudni. Ezért célszerű átfutni a letöltött forrás projektek NEWS állományait. A v13-omban például felmerül, hogy időhúzásért akár sárga lapot is kaphatna az ágens, ha lenne sárga lap..., majd a v14-beli NEWS már tudósít, hogy több kontextusban is megjelent a sárga lap.

3. fejezet - Agent2D

Ebben a fejezetben a világklasszis japán HELIOS csapat alapját, a HELIOS_base csapatot és a kapcsolódó RoboCup tools (rctools) szoftvereit ismerheted meg.

Ennek megfelelően itt például az alábbi programokkal találkozol majd:

„The only one who can tell me I'm not good enough is you. And even then I may not agree with you.”

Santiago Munez [GOAL]

A szoftverek telepítése

A most telepítésre kerülő szoftverek beszerzését már tárgyaltuk, most a sikeres használatba vételüket próbáljuk megtámogatni a jelen fejezettel.

librcsc

Ha nem a rendszeredbe akarod installálni, hanem csak magadhoz (tehát egyszerű felhasználóként), akkor használd a következő prefixel a configure szkriptet: ./configure --prefix=/home/norbert/local, s ekkor persze nem kell a végén rendszergazdaként, hanem csak sima felhasználóként a make install, azaz a tényleges telepítést elvégző parancsot kiadni.

[norbert@matrica RoboCup]$ mv ../Downloads/librcsc-4.1.0.tar.gz .
[norbert@matrica RoboCup]$ gunzip librcsc-4.1.0.tar.gz 
[norbert@matrica RoboCup]$ tar xvf librcsc-4.1.0.tar
[norbert@matrica RoboCup]$ cd librcsc-4.1.0
[norbert@matrica librcsc-4.1.0]$ ./configure
[norbert@matrica librcsc-4.1.0]$ make
[norbert@matrica librcsc-4.1.0]$ su
Password: 
[root@matrica librcsc-4.1.0]# make install
                  

soccerwindow2

[norbert@matrica RoboCup]$ mv ../Downloads/soccerwindow2-5.1.0.tar.gz .
[norbert@matrica RoboCup]$ gunzip soccerwindow2-5.1.0.tar.gz 
[norbert@matrica RoboCup]$ tar xvf soccerwindow2-5.1.0.tar                
[norbert@matrica RoboCup]$ cd soccerwindow2-5.1.0
[norbert@matrica soccerwindow2-5.1.0]$ ./configure --with-qt4-moc=/usr/bin/moc-qt4
[norbert@matrica soccerwindow2-5.1.0]$ make
[norbert@matrica soccerwindow2-5.1.0]$ su
Password: 
[root@matrica soccerwindow2-5.1.0]# make install
                  

hogy hol a szereplő moc-qt4 azt megmondja például a which moc-qt4 parancs.

agent2d

Ha a librcsc könyvtárat lokálisan telepítetted, azaz a ./configure --prefix=/home/norbert/local alakú parancsot használva tetted fel, akkor itt add meg a configure szkriptnek a pontos helyet a ./configure --with-librcsc=/home/norbert/local formában. Itt már a make install parancsra nincs szükség, a munka menete innen a szokásos: cpp és fejlécállományok forrásban módosít, a make paranccsal fordít, majd futtat (bosszankodik és ezt iterálja).

[norbert@matrica RoboCup]$ mv ../Downloads/agent2d-3.1.0.tar.gz .
[norbert@matrica RoboCup]$ gunzip agent2d-3.1.0.tar.gz 
[norbert@matrica RoboCup]$ tar xvf agent2d-3.1.0.tar 
[norbert@matrica RoboCup]$ cd agent2d-3.1.0
[norbert@matrica agent2d-3.1.0]$ ./configure
[norbert@matrica agent2d-3.1.0]$ make
                  

Hogy lássuk, nem csalás, nem ámítás a forrásból történő telepítés, futtassuk is a telepített szoftvereket: kössünk le egy rangadót a világklasszis japán HELIOS csapat és a jegyzet Marvellous Magyars FC csapata között!

Első lépés az RCSS szerver indítása, bárhol állva kiadhatjuk a parancsot, hiszen a rendszerbe telepítettük.

[norbert@matrica ~]$ rcssserver
rcssserver-15.0.0

Copyright (C) 1995, 1996, 1997, 1998, 1999 Electrotechnical Laboratory.
2000 - 2011 RoboCup Soccer Simulator Maintenance Group.

Simulator Random Seed: 1318409790
CSVSaver: Ready
STDOutSaver: Ready
Using simulator's random seed as Hetero Player Seed: 1318409790
wind factor: rand: 0.000000, vector: (0.000000, 0.000000)

Hit CTRL-C to exit
                  

Indítsuk a monitort, legyen ez a szintén most, a japán csapat rctools csomagjából feltett soccerwindow2, megintcsak bárhonnan kiadhatjuk, hiszen rendszer szinten és nem valahová lokálba tettük fel.

[norbert@matrica ~]$ soccerwindow2
******************************************************************
 soccerwindow2 5.1.0
 Copyright: (C) 2005 - 2011. Hidehisa Akiyama
 All rights reserved.
******************************************************************

Connect to rcssserver on [127.0.0.1]
send: (dispinit version 4)
***ERROR*** RCSSParamParser. unknown parameter name or invalid value. name=[red_card_probability] value=[0]
updated server port number = 38793
                  

Indítsuk most az agent2d világklasszis japán csapat magját, a HELIOS_base csapatot! Itt már lokálban vagyunk, fontos a hol, az agent2d gyökerében, ahonnan a src/start.sh paranccsal futtathatjuk a csapat ágenseit.

[norbert@matrica agent2d-3.1.0]$ src/start.sh 
******************************************************************
 HELIOS base
 Created by Hidehisa Akiyama and Hiroki Shimora
 Copyright 2000-2007.  Hidehisa Akiyama
 Copyright 2007-2011.  Hidehisa Akiyama and Hiroki Shimora
 All rights reserved.
******************************************************************
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_req=1 ttl=64 time=0.065 ms

--- localhost ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.065/0.065/0.065/0.000 ms
******************************************************************
 librcsc 4.1.0
 Copyright 2000 - 2007. Hidehisa Akiyama.
 Copyright 2007 - 2011. Hidehisa Akiyama and Hiroki Shimora
 All rights reserved.
******************************************************************
*****************************************************************
 This program is based on agent2d created by Hidehisa Akiyama.
 Copyright 2006 - 2011. Hidehisa Akiyama and Hiroki Shimora.
 All rights reserved.
*****************************************************************
                  

S végül kifut a fűre a saját csapatunk is:

[norbert@matrica MarvellousMagyarsFC-0.0.1]$ java -jar target/site/MarvellousMagyarsFC-0.0.1-jar-with-dependencies.jar
                  

Miután az RCSS szerver naplózza, hogy mindenki megvan.

A new (v4) monitor connected.
A new (v14) player (HELIOS_base 1) connected.
A new (v14) player (HELIOS_base 2) connected.
A new (v14) player (HELIOS_base 3) connected.
A new (v14) player (HELIOS_base 4) connected.
A new (v14) player (HELIOS_base 5) connected.
A new (v14) player (HELIOS_base 6) connected.
A new (v14) player (HELIOS_base 7) connected.
A new (v14) player (HELIOS_base 8) connected.
A new (v14) player (HELIOS_base 9) connected.
A new (v14) player (HELIOS_base 10) connected.
A new (v14) online coach (HELIOS_base) connected.
A new (v14) player (HELIOS_base 11) connected.
A new (v13) player (MarvellousFC 1) connected.
A new (v13) player (MarvellousFC 2) connected.
A new (v13) player (MarvellousFC 3) connected.
A new (v13) player (MarvellousFC 4) connected.
A new (v13) player (MarvellousFC 5) connected.
A new (v13) player (MarvellousFC 6) connected.
A new (v13) player (MarvellousFC 7) connected.
A new (v13) player (MarvellousFC 8) connected.
A new (v13) player (MarvellousFC 9) connected.
A new (v13) player (MarvellousFC 10) connected.
A new (v13) player (MarvellousFC 11) connected.
A new (v13) online coach (MarvellousFC) connected.
                  

A soccerwindow2 felületen néznek már farkasszemet a felek, jöhet a kezdőrúgás! A végeredményt nem említeném, elég annyi, hogy nem feltartott kézzel mentünk ki a pályára, de csapatunk nem szerzett pontot...

3.1. ábra - A HELIOS_base és a Marvellous Magyars FC farkasszemet néz a soccerwindow2-ben.

A HELIOS_base és a Marvellous Magyars FC farkasszemet néz a soccerwindow2-ben.

A feltelepített szoftverek kipróbálásáról egy videót (screencast hanggal) is készítettünk, amit ide most beágyaztunk, illetve a YouTubeSM videómegosztón és eredeti minőségben a szerző honlapján is elhelyeztünk.

A feltelepített szoftverek kipróbálásáról egy videót (screencast hanggal) is készítettünk, amit ide most beágyaztunk, illetve a YouTubeSM videómegosztón és eredeti minőségben a szerző honlapján is elhelyeztünk.

FormationEditor

[norbert@matrica RoboCup]$ mv ../Downloads/fedit2-0.0.0.tar.gz .
[norbert@matrica RoboCup]$ gunzip fedit2-0.0.0.tar.gz 
[norbert@matrica RoboCup]$ tar xvf fedit2-0.0.0.tar 
[norbert@matrica RoboCup]$ cd fedit2-0.0.0
[norbert@matrica fedit2-0.0.0]$ ./configure --with-qt4-moc=/usr/bin/moc-qt4
[norbert@matrica fedit2-0.0.0]$ su
Password: 
[root@matrica fedit2-0.0.0]# make install
                  
[Megjegyzés]Nem akar működni...

Előfordulhat, hogy a configure szkript nem akar lefutni, a make parancs hibával áll le, a make install parancs valamilyen hozzáférési hibát ad, vagy egy frissen készített megosztott programkönyvtárat nem látunk futtatáskor. Első esetben a csomagkezelővel fel kell tenned a hiányzó szoftver komponenseket a rendszeredbe, illetve a szkript kapcsolóival játszani. A fordítási hibák kapcsán itt-ott egy néhány karakteres szösszenetet betenni a forrásokba. Jogosultsági hiba tipikusan akkor lehet, ha nem mentél át rendszergazdába, vagy nem létező könyvtárakat használtál a configure paraméterezésénél. Ha egy programkönyvtárra vonatkozó hibát látsz, akkor az azt (a hiányzó programkönyvtárat) tartalmazó mappát kell bevenned az LD_LIBRARY_PATH változóba, tipikusan ilyenformán: export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH.

Esetleges végső elkeseredésedben pedig kérj segítséget egy komment formájában a kurzus blogján.

A szoftverek tárgyalása

A librcsc és az agent2d forrásainak tárgyalását a saját csapatok építését bevezető részben tesszük meg.

4. fejezet - Atan

Ebben a fejezetben egy olyan interfészt ismersz meg,

  • amely biztosítja, hogy Java nyelven készíthess csapatot,

  • illetve sikerrel megold az első programozási feladatokat.

„First you listen to your heart, then you listen to your head and then your wife will tell you what to do.”

Rudi van der Merwe [GOALII]

A szoftver telepítése

Az Atan [ATAN] interfész egy Javás csomag, gépeden való megjelenésében egyetlen jar fájl, az atan.jar (amelynek beszerzését korábban már tárgyaltuk). Ennek megfelelően használata megegyezik úgy a GNU/Linux rendszereken, mint a Windows környezetekben.

A szoftver használata GNU/Linux és Windows környezetben

A csomag naplózásra a Apache log4j csomagot használja, amelyet így le kell töltened. Tipikusan fordításhoz és futtatáshoz lesz szükséged a szóban forgó csomagokra. Kezdjük az utóbbival! GNU/Linux parancssorban a

[norbi@sgu atan_0.4.3]$ java -cp atan.jar:log4j-1.2.16.jar sample.Simple1Run
                  

parancsot kell kiadni az Atan valamelyik példa atan/java/sample/Simple1Run.java csapatának a futtatásához. Itt az Atan telepítési könyvtárában (ami most egyszerűen az, amit a letöltése utáni kicsomagoláskor kaptam) állva adtam ki a parancsot és a -cp kapcsolóval adtam meg relatíven a CLASSPATH szükséges elemeit, azaz azokat az archívumokat, amelyekben a Java virtuális gép megtalálja majd azokat az osztályokat, amelyeket nem mi írtunk, de használunk. Windows alatt ugyanez a parancs így fest:

C:\Users\Norbi\atan_0.4.3>java -cp atan.jar;log4j-1.2.16.jar sample.Simple1Run
                  

gyakorlatilag nincs különbség. Ha fordításnál akarod használni az Atan csomagot, akkor a parancsok annyiban módosulnak, hogy a java parancs helyett a javac parancsot használod majd a parancssorban. (Amennyiben a fenti parancssor nem működött, lehetséges, hogy az aktuális könyvtár nálad nincs benne a CLASSPATH változóban, ekkor fűzd bele a . aktuális könyvtárat a parancssorba a -cp atan.jar:log4j-1.2.16.jar:. vagy a -cp atan.jar;log4j-1.2.16.jar;. kapcsoló formájában.)

A szoftver használata NetBeans környezetben

Ha lett volna különbség a különbőző operációs rendszereken történő használatban, azokat a NetBeans úgyis elrejtené, de nem is volt. Aki NetBeans projektként gondozná majd a saját csapatát, annak mindössze annyi a dolga, hogy jobb gombot nyom a projekt néven, majd a Properties menüpont alatt a Libraries/Compile és a Libraries/Run alatt kitallózza a szóban forgó jarokat, amint az alábbi ábrán mutatjuk.

4.1. ábra - Atan NetBeans projektben.

Atan NetBeans projektben.


A szoftver tárgyalása

A csomag felhasználójának szempontjából annak legfontosabb két interfésze a atan.model csomagba szervezett atan.model.ControllerPlayer és a atan.model.ActionsPlayer. Előbbi az őt implementáló (RCSS kliens ágens) objektumhoz juttatja az érzékelési információkat, például, hogy látja a labdát. Utóbbi pedig az ágens objektum tevékenység parancsait juttatja el az RCSS szervernek.

Minden ciklus (egy see parancs érkezése a szervertől) elején hívódik az atan.model.ControllerPlayer objektum (azaz az az objektum, amelyik implementálja az atan.model.ControllerPlayer interfészt) preInfo metódusa, majd az adott játékszituációnak megfelelő, például a látási érzékelést absztraháló metódusa, mondjuk éppen az infoSeeBall metódus, ha a szerver olyan parancsot küld, hogy a játékos látja a labdát. Végül, a ciklusban érkezett parancsoknak megfelelő metódusok meghívódása után következik a postInfo metód hívása, tipikusan itt adja meg a kliens ágens az érzékelési információk alapján, a megfelelő atan.model.ActionsPlayer metódusokkal a reakcióit [ATAN].

A két interfésszel való ismerkedést folytassuk azzal, hogy egy pillantást vetünk a típusok és paraméterek nélkül legenerált UML diagrammjaikra.

4.2. ábra - Az atan.model.ControllerPlayer.

Az atan.model.ControllerPlayer.


Emeljünk ki és tárgyaljunk röviden néhány „érző metódust”!

  • infoSeeBall(): ez a módszere hívódik az atan.model.ControllerPlayer objektumnak, azaz a játékosunknak, ha látja a labdát. A metódus pontos szignatúrája az alábbi:

    public void infoSeeBall(double distance, double direction, double distChange, double dirChange, double bodyFacingDirection, double headFacingDirection)
                        

    a függvény aktuális paramétereiben kapja a szituáció kapcsán tudható adatokat: a distance változó hordozza a labda távolságát, a direction a labda pozíciójának relatív szöge a játékos testéhez képest, a distChange megadja, hogyan változott meg az előző ciklushoz képest a labda távolsága, a dirChange analóg, az utolsó két változó értéke a protokoll szerint mindig zérus itt, ezek a változók egy másik (például a infoSeePlayerOwn módszerben) látott játékos hozzánk képest relatív test és fej szögét adják.

  • infoHearPlayMode(): ez a módszere hívódik az atan.model.ControllerPlayer objektumnak, azaz a játékosunknak, ha a játékmód változásáról (például középkezdés, bedobás vagy szöglet stb.) kap információt. A függvény szignatúrája a következő.

    public void infoHearPlayMode(atan.model.enums.PlayMode playMode)
                        

    aktuális paraméterében (idézve az atan/model/enums/PlayMode.java forrásból) kapja a szituáció kódját:

    /**
     * An enum for PlayMode
     * @author Atan
     */
    public enum PlayMode {
    
        /**
         * The mode of a game before it starts.
         */
        BEFORE_KICK_OFF,
    
        /**
         * The time has finished.
         */
        TIME_OVER,
    
        /**
         * The default play mode.
         */
        PLAY_ON,
                        

    A saját csapatunknál a kapott érték alapján írhatunk például egy több irányú elágaztatást:

        @Override
        public void infoHearPlayMode(atan.model.enums.PlayMode playMode) {
    
            super.infoHearPlayMode(playMode);
    
            switch (playMode) {
                case CORNER_KICK_OWN:
                    szoglet = true;
                    break;
                case GOAL_KICK_OWN:
                    kirugas = true;
                    break;
                    ...                    
                        

4.3. ábra - Az atan.model.ActionsPlayer.

Az atan.model.ActionsPlayer.


Programozási feladatok

Adott területen az első programozási feladatoknál sosem árthat a sok segítség, sőt! Ezért esetenként a következő feladat csokorban megadunk majd konrét megoldásokat is.

Aranylabor FC

Készíts egy olyan Atan alapú programot, amely az Aranycsapat felállását veszi fel a középkezdéskor!

Ha még nem tetted meg, most telepítsd az Atan csomagot a gépedre.

  1. Vegyük azt a hadrendet, amelyben a magyar nemzeti 11 felállt a híres „hat-hármas”, sokszor az évszázad mérkőzésének titulált találkozón a Wembley Stadionban 1953-ban. A felállását például ebben a [DEIKFOCI] cikkünkben is megtalálhatjátok.

  2. Használhatod a következő feladat CsakALabdaFC és JatekosAdapter osztályát.

  3. Nincs más dolgod, mint a középkezdéskori felállást megadni, azaz hívni a megfelelő aktuális paraméter értékekkel a move metódust az előző pont hivatkozta kódrészletek JatekosAdapter osztályában.

„Csak a labda” FC

Készíts egy olyan Atan alapú programot, amelyben a csapat minden játékosa egy emberként rohan a labda után a mérkőzés során.

  1. Egy megoldási javaslatként felélesztheted a CsakALabdaFC és a JatekosAdapter osztályok forrásait, létrehozhatsz egy NetBeans projektet, de egyszerűbb, ha parancssorban dolgozol az alábbiak szerint:

    C:\Users\Norbi>javac -cp Downloads\atan_0.4.3\atan.jar;Downloads\atan_0.4.3\log4j-1.2.16.jar;. CsakALabdaFC.java
    
    C:\Users\Norbi>java -cp Downloads\atan_0.4.3\atan.jar;Downloads\atan_0.4.3\log4j-1.2.16.jar;. CsakALabdaFC
                                    
  2. Íme a CsakALabdaFC osztály kódja. A kód szervezéséről többet olvashatsz a A CsordaFC osztály pontban.

    public class CsakALabdaFC extends atan.model.AbstractTeam {
    
      public CsakALabdaFC(String team, int port, String host) {
    
        super(team, port, host);
    
      }
    
      @Override
      public atan.model.ControllerPlayer getNewController(int number) {
    
        return new JatekosAdapter();
    
      }
    
      public static void main(String[] args) {
    
        org.apache.log4j.BasicConfigurator.configure();
    
        if (args.length == 1) {
          new CsakALabdaFC(args[0], 6000, "localhost").connectAll();
        } else {
          new CsakALabdaFC("CsakALabdaFC", 6000, "localhost").connectAll();
        }
      }
    }
                                    
  3. S íme a JatekosAdapter osztály kódja. A kód szervezéséről többet olvashatsz A JatekosAdapter osztály pontban.

    /**
     * Az atan http://atan1.sourceforge.net/javadoc/atan/model/ControllerPlayer.html
     * API doksija alapján az összes interfészmetódus (tipikusan üres testtel) van
     * itt definiálva. (De most itt implementáljuk a Csak a labda FC "viselkedését" is.)
     */
    public class JatekosAdapter implements atan.model.ControllerPlayer {
    
      private atan.model.ActionsPlayer jatekos;
      /*
       * Ezt az egyszerű, tipikusan szimpla logikai változókkal 
       * jelzett/megvalósított egyszerű reflexszerű "viselkedést" az Atan 
       * példa forrásaiból tanultuk (atan/java/sample/Simple.java stb).
       */
      protected boolean kozepkezdes;
      protected boolean latomAFocit;
      protected double distanceFoci;
      protected double directionFoci;
      protected double distChangeFoci;
      protected double dirChangeFoci;
    
      @Override
      public atan.model.ActionsPlayer getPlayer() {
        return jatekos;
      }
    
      @Override
      public void setPlayer(atan.model.ActionsPlayer jatekos) {
        this.jatekos = jatekos;
      }
    
      @Override
      public void preInfo() {
        kozepkezdes = false;
        latomAFocit = false;
      }
    
      @Override
      public void postInfo() {
    
        if (kozepkezdes) {
          kozepkezdes();
        } else if (latomAFocit) {
          utana();
        } else {
          keresem();
        }
    
      }
    
      protected void keresem() {
    
        getPlayer().turn(25);
        getPlayer().turnNeck(50);
        getPlayer().dash(30);
    
      }
    
      @Override
      public void infoHearPlayMode(atan.model.enums.PlayMode playMode) {
    
        kozepkezdes = true;
    
      }
    
      /* 4-4-2 */
      protected void kozepkezdes() {
    
        switch (getPlayer().getNumber()) {
    
          // Kapus
          case 1:
            getPlayer().move(-51, 0);
            break;
    
          // Tamadok
    
          case 2:
            getPlayer().move(-2, -10);
            break;
          case 3:
            getPlayer().move(-1, 1);
            break;
    
          // Kozeppalya
    
          case 4:
            getPlayer().move(-16, -24);
            break;
    
          case 5:
            getPlayer().move(-18, -12);
            break;
          case 6:
            getPlayer().move(-17, 12);
            break;
    
          case 7:
            getPlayer().move(-18, 27);
            break;
    
          // Vedo
    
          case 8:
            getPlayer().move(-35, -26);
            break;
    
          case 9:
            getPlayer().move(-33, -10);
            break;
    
          case 10:
            getPlayer().move(-34, 12);
            break;
    
          case 11:
            getPlayer().move(-35, 24);
            break;
    
        }
    
      }
    
      @Override
      public void infoSeeBall(double distance, double direction, double distChange, double dirChange,
              double bodyFacingDirection, double headFacingDirection) {
    
        latomAFocit = true;
        distanceFoci = distance;
        directionFoci = direction;
        dirChangeFoci = distChange;
        dirChangeFoci = dirChange;
    
      }
    
      protected void utana() {
    
        getPlayer().turn(directionFoci);
        getPlayer().turnNeck(directionFoci + dirChangeFoci);
        getPlayer().dash(100);
    
        if (distanceFoci < 0.6) {
    
          getPlayer().kick(20, directionFoci);
    
        }
    
      }
    
      ...
    
      @Override
      public String getType() {
        return "CsakALabdaFC";
      }
    }
                                    

Büntető FC

Készíts egy olyan Atan alapú programot, amelyben a csapat minden játékosa a saját büntetőterületén belül marad a mérkőzés során, de a büntetőterületen belül persze úgy viselkedik, mint az előző feladat csapata.

  1. Egy megoldási javaslatként most még megint csak felélesztheted a BuntetoFC és a megfelelő JatekosAdapter osztályok forrásait.

  2. Íme a BuntetoFC osztály kódja.

    public class BuntetoFC extends atan.model.AbstractTeam {
    
      public BuntetoFC(String team, int port, String host) {
    
        super(team, port, host);
    
      }
    
      @Override
      public atan.model.ControllerPlayer getNewController(int number) {
    
        return new JatekosAdapter();
    
      }
    
      public static void main(String[] args) {
    
        org.apache.log4j.BasicConfigurator.configure();
    
        if (args.length == 1) {
          new BuntetoFC(args[0], 6000, "localhost").connectAll();
        } else {
          new BuntetoFC("BuntetoFC", 6000, "localhost").connectAll();
        }
      }
    }
                                    
  3. S íme a példának megfelelő JatekosAdapter osztály kódja. Az osztály jelen listájából az üres testtel szereplő módszereket, amint az előző példában, most is kitöröltük, hogy kíméljük a fákat.

    /**
     * Az atan http://atan1.sourceforge.net/javadoc/atan/model/ControllerPlayer.html
     * API doksija alapján az összes interfészmetódus (tipikusan üres testtel) van
     * itt definiálva. (De most itt implementáljuk a Büntető FC "viselkedését" is.)
     */
    public class JatekosAdapter implements atan.model.ControllerPlayer {
    
      private atan.model.ActionsPlayer jatekos;
      /*
       * Ezt az egyszerű, tipikusan szimpla logikai változókkal 
       * jelzett/megvalósított egyszerű reflexszerű "viselkedést" az Atan 
       * példa forrásaiból tanultuk (atan/java/sample/Simple.java stb).
       */  
      protected boolean kozepkezdes;
      protected boolean latomAFocit;
      protected double distanceFoci;
      protected double directionFoci;
      protected double distChangeFoci;
      protected double dirChangeFoci;
      protected boolean mozoghat;
    
      @Override
      public atan.model.ActionsPlayer getPlayer() {
        return jatekos;
      }
    
      @Override
      public void setPlayer(atan.model.ActionsPlayer jatekos) {
        this.jatekos = jatekos;
      }
    
      @Override
      public void preInfo() {
        kozepkezdes = false;
        latomAFocit = false;
        mozoghat = false;
      }
    
      @Override
      public void postInfo() {
    
        if (kozepkezdes) {
          kozepkezdes();
        } else if (latomAFocit) {
          utana();
        } else {
          keresem();
        }
    
      }
    
      protected void keresem() {
    
        getPlayer().turn(25);
        getPlayer().turnNeck(50);
        getPlayer().dash(30);
    
      }
    
      @Override
      public void infoHearPlayMode(atan.model.enums.PlayMode playMode) {
    
        kozepkezdes = true;
    
      }
    
      /* A kapu elé */
      protected void kozepkezdes() {
    
        getPlayer().move(-51, getPlayer().getNumber());
    
      }
    
      @Override
      public void infoSeeBall(double distance, double direction, double distChange, double dirChange,
              double bodyFacingDirection, double headFacingDirection) {
    
        latomAFocit = true;
        distanceFoci = distance;
        directionFoci = direction;
        dirChangeFoci = distChange;
        dirChangeFoci = dirChange;
    
      }
    
      protected void utana() {
    
        getPlayer().turn(directionFoci);
        getPlayer().turnNeck(directionFoci + dirChangeFoci);
    
        if (mozoghat) {
          getPlayer().dash(100);
        }
    
        if (distanceFoci < 0.6) {
    
          getPlayer().kick(20, directionFoci);
    
        }
    
      }
    
      @Override
      public void infoSeeFlagPenaltyOwn(atan.model.enums.Flag flag, double distance, double direction, double distChange,
              double dirChange, double bodyFacingDirection, double headFacingDirection) {
    
        if (distance > 14.0) {
          mozoghat = true;
        }
      }
    
      @Override
      public void infoSeeFlagGoalOwn(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
              double bodyFacingDirection, double headFacingDirection) {
    
        if (distance > 4.0) {
          mozoghat = true;
        }
      }
      
      ...
    
      @Override
      public String getType() {
        return "BuntetoFC";
      }
    }
                                    

Kapuba FC

Készíts egy olyan Atan alapú programot, amelynek minden játékosa a saját kapujában (a gólvonalon) áll és onnan figyeli a labdát.

Javaslatom, hogy a Büntető FC forrásaiból indulj ki.

  • Az előző feladathoz hasonló megoldást kell készítened, hiszen csak annyi változik itt, hogy kisebb területre kell koncentrálnod a játékosaidat.

Foci iszony FC

Készíts egy olyan Atan alapú programot, amelyben a játékosok nemhogy a labda felé mozognak, hanem éppen menekülnek attól a mérkőzés során.

Javaslatom, hogy a Csak a labda FC forrásaiból indulj ki.

  • A „minimalista” első megközelítésben a Csak a labda FC alkalmazásban a dash hívásokban a paraméter előjelét az ellenkezőjére változtathatod, vagy ezzel szemben a „maximalista” megközelítésben verbálisan tervezel egy viselkedést és beprogramozod az ennek megfelelő érzékelést és az azokra adott reakciókat. Ám utóbbira is mindenképpen szükséged lesz, mert kikötjük, hogy a játékosok meneküljenek bár a labdától, de ne hagyják el a pályát!

Ping-pong FC

Készíts egy olyan Atan alapú programot, amelynek minden játékosa a "játéktól" függetlenül oda-vissza, a pálya teljes széltében vízszintesen mozog a mérkőzés során.

Itt azt javaslom, hogy a Büntető FC alkalmazásból indulj ki, majd a „minimalista” megközelítésben ne fordulj, hanem megintcsak a dash hívásokban a paraméter előjelét változtasd át az ellenkezőjére. Ha viszont a „maximalista” megközelítés híve vagy, akkor a pálya szélén fordulj, de készülj fel a tervez, beprogramoz, tesztel, majd néha örül, többször bosszankodik ciklus sokszori iterációjára!

  1. Íme a PingPongFC osztály kódja, amely szinte változatlan a már megismerthez, megszokotthoz képest.

    public class PingPongFC extends atan.model.AbstractTeam {
    
      public PingPongFC(String team, int port, String host) {
    
        super(team, port, host);
    
      }
    
      @Override
      public atan.model.ControllerPlayer getNewController(int number) {
    
        return new JatekosAdapter();
    
      }
    
      public static void main(String[] args) {
    
        org.apache.log4j.BasicConfigurator.configure();
        org.apache.log4j.Logger.getLogger("atan.model").setLevel(org.apache.log4j.Level.OFF);
    
        if (args.length == 1) {
          new PingPongFC(args[0], 6000, "localhost").connectAll();
        } else {
          new PingPongFC("PingPongFC", 6000, "localhost").connectAll();
        }
      }
    }
                                    
  2. S íme a példának megfelelő, „minimalista” megközelítésben megírt JatekosAdapter osztály kódja. Az osztály jelen listájából az üres testtel szereplő módszereket, amint a korábbi ugyanilyen nevű osztályánál, most is kitöröljük, hogy kíméljük a fákat.

    /**
     * Az atan http://atan1.sourceforge.net/javadoc/atan/model/ControllerPlayer.html
     * API doksija alapján az összes interfészmetódus (tipikusan üres testtel) van
     * itt definiálva. (De most itt implementáljuk a Ping Pong FC "viselkedését" is.)
     */
    public class JatekosAdapter implements atan.model.ControllerPlayer {
    
      private atan.model.ActionsPlayer jatekos;
      /*
       * Ezt az egyszerű, tipikusan szimpla logikai változókkal 
       * jelzett/megvalósított egyszerű reflexszerű "viselkedést" az Atan 
       * példa forrásaiból tanultuk (atan/java/sample/Simple.java stb).
       */  
      protected boolean kozepkezdes;
      /* Merre mehetek? */
      public static final int ELORE = 50;
      public static final int HATRA = -ELORE;
      /* Merre megyek éppen? */
      protected int irany = ELORE;
    
      @Override
      public atan.model.ActionsPlayer getPlayer() {
        return jatekos;
      }
    
      @Override
      public void setPlayer(atan.model.ActionsPlayer jatekos) {
        this.jatekos = jatekos;
      }
    
      @Override
      public void preInfo() {
        kozepkezdes = false;
      }
    
      @Override
      public void postInfo() {
    
        if (kozepkezdes) {
          kozepkezdes();
        } else {
          megy();
        }
    
      }
    
      protected void megy() {
    
        getPlayer().dash(irany);
    
      }
    
      @Override
      public void infoHearPlayMode(atan.model.enums.PlayMode playMode) {
    
        kozepkezdes = true;
    
      }
    
      /* A kapu elé */
      protected void kozepkezdes() {
    
        getPlayer().move(-40, -30 + getPlayer().getNumber() * 5);
    
      }
    
      @Override
      public void infoSeeFlagOwn(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
              double bodyFacingDirection, double headFacingDirection) {
    
        if (distance < 8.0) {
          irany = HATRA;
        } else if (distance > 115.0) {
          irany = ELORE;
        }
    
      }
    
      @Override
      public void infoSeeFlagOther(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
              double bodyFacingDirection, double headFacingDirection) {
    
        if (distance < 8.0) {
          irany = HATRA;
        } else if (distance > 115.0) {
          irany = ELORE;
        }
    
      }
      
      ...
    
      @Override
      public String getType() {
        return "PingPongFC";
      }
    }
                                    
[Megjegyzés]A dokumentációs megjegyzések

Az imént közölt forráskód csipetekben már biztosan feltűnik, hogy nem hogy alig, hanem szinte egyáltalán nem kommentezünk. Ez persze a fejlesztés során megengedhetetlen, hiszen a dokumentációs megjegyzésekből készül majd automatikusan fejlesztésünk API dokumentációja. Nem beszélve arról, hogy a kódolási konvenciók betartása tudja azt bizonyítani, hogy már programozók is azt gondolják a forrásunkra nézve, hogy az valóban egy program az adott, most éppen Java nyelven.

Azért teszünk így mert egy tipikus feladat a kurzusban a megfelelő kommentezés megszokása, ezért ezt visszatérően ismételjük is, például a csapatok Maven projektjeinek Checkstyle hibáinak javításánál: Many heads are inevitably better than one* vagy Embernek néznek ki, izzadnak, büdös a leheletük*.

Mágnes FC

Készíts egy olyan Atan alapú programot, amelynek minden játékosa az ellenfél játékosanak szoros emberfogására törekszik.

Végül itt azt tanácsolom, hogy a infoSeePlayerOther metódus használatával kísérletezz az előző feladatban javasoltak szerint.

  • Használd fel valamelyik korábbi csapat indító és adapter osztályát a megoldásodhoz!

5. fejezet - Atan-1.0

Ebben a fejezetben az Atan interfész legújabb változatával ismerkedsz meg,

  • amelyet egyelőre még binárisban nem terjesztenek, ezért majd a SourceForge.net Subversion tárolójából lehozott forrásokat magadnak kell build-elned,

  • de cserébe számos újdonsághoz jutsz: alkalmazhatsz például edzőt a csapatodnál, vagy használhatod csapatod címerét.

„SPOONER You're a machine. An imitation. An illusion of life. Can a robot write a symphony? Can a robot take a blank canvas and paint a masterpiece? SONNY Can you do either of those things?”

[IROBOT] I, Robot

A szoftver telepítése

Minden változatlanul igaz, amit az Atan korábban tárgyalt (0.4.3) verziója kapcsán megismertél. Most viszont a atan.jar Java archívumot Neked magadnak kell elkészítened. Az Atan 1.0 egy Apache Ant projekt, melyet a projekt fejlesztői szájtjáról, azon belül is a SourceForge.net Subversion tárolójából tudsz letölteni. Ez például a GNU/Linux rendszereken nem áll másból, mint a

svn co https://atan1.svn.sourceforge.net/svnroot/atan1 atan1
              

parancs kiadásából. Ám tapasztalatom szerint ennek az Ant projektnek a használata kicsit nehézkes, ezért készítettem belőle egy Apache Maven projektet, hiszen a kurzusunkban is ez az a platform, amelyet preferálunk. Ennek megfelelően a pom.xml állományt az alábbiak szerint készítettem el. Helytakarékosságból a szájt bővítmény résznél számos kódelemző riportgenerátort itt most kivágtunk a fájlból, de természetesen a teljes állomány szerepel a szóban forgó Maven projektben.

<?xml version="1.0" encoding="UTF-8"?>
<!--
Norbert Bátfai, batfai.norbert@inf.unideb.hu

At this moment the Atan 1.0 is available only in source form at http://sourceforge.net/projects/atan1/ (as an
Ant project). This pom.xml is created to work with Atan 1.0 as a Maven project.

[norbert@matrica atan-1.0.0]$ mvn3 clean generate-sources javacc:jjdoc package site assembly:single install:install-file -Dfile=target/atan-1.0.0.jar -DgroupId=atan -DartifactId=atan -Dversion=1.0.0 -Dpackaging=jar

To build the artifact atan-1.0.0.jar it is usually sufficient to give the command:
[norbert@matrica atan-1.0.0]$ mvn3 clean package

After these, atan-1.0.0.jar may be immediately used in the creation of your Atan-based football team: 
[norbi@sgu AranycsapatFC-0.0.1]$ mvn3 clean package site assembly:single

To build your team it is usually sufficient to give the command:
[norbi@sgu AranycsapatFC-0.0.1]$ mvn3 clean package
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>atan</groupId>
  <artifactId>atan</artifactId>
  <packaging>jar</packaging>
  <version>1.0.0</version>
  <name>Atan</name>
  <url>http://sourceforge.net/projects/atan1/</url>
  <description>This artifact is based the Atan ant project from its SourceForge.net Subversion repository: http://sourceforge.net/projects/atan1/</description>
  <organization>
    <name>Atan</name>
    <url>http://sourceforge.net/projects/atan1/</url>
  </organization>
  <licenses>
    <license>
      <name>GNU GPL</name>
      <url>http://www.gnu.org/licenses/gpl.html</url>
    </license>
  </licenses>
  <developers>
    <developer>
      <name>Atan</name>
      <url>http://sourceforge.net/projects/atan1/</url>
      <organization>http://sourceforge.net/projects/atan1/</organization>
      <organizationUrl>http://sourceforge.net/projects/atan1/</organizationUrl>
    </developer>
  </developers>
  <contributors>
    <contributor>
      <name>Norbert Batfai</name>
      <email>nbatfai@gmail.com</email>
      <url>http://www.inf.unideb.hu/~nbatfai</url>
      <organization>University of Debrecen</organization>
      <organizationUrl>http://www.inf.unideb.hu</organizationUrl>
      <roles>
        <role>I created the pom.xml</role>
      </roles>
    </contributor>
  </contributors>
  <dependencies>
    <dependency>
      <groupId>maven</groupId>
      <artifactId>maven-jar-plugin</artifactId>
      <version>1.8.1</version>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.16</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>javacc-maven-plugin</artifactId>
        <version>2.6</version>
        <executions>
          <execution>
            <id>javacc</id>
            <goals>
              <goal>javacc</goal>
            </goals>
            <configuration>
              <sourceDirectory>src/main/java/atan/parser</sourceDirectory>
              <outputDirectory>src/main/java</outputDirectory>
              <timestampDirectory>target/generated-src/javacc-timestamp</timestampDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>2.3</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-assembly-plugin</artifactId>
        <configuration>
          <descriptorRefs>
            <descriptorRef>project</descriptorRef>
          </descriptorRefs>
          <outputDirectory>${project.reporting.outputDirectory}</outputDirectory>
        </configuration>
      </plugin>
      <!-- reports -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-site-plugin</artifactId>
        <version>3.0</version>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
          <reportPlugins>
          ...
          </reportPlugins>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
            

A GNU/Linux és Windows rendszerekben ugyanúgy jársz el, csak a könyvtár változik a következő példában attól függően, hogy az adott rendszeren éppen hol állunk (további pontos iránymutatást adunk a szóban forgó Maven projekt README.txt állományában). A

[norbi@sgu atan-1.0.0]$ mvn clean generate-sources javacc:jjdoc package site assembly:single install:install-file -Dfile=target/atan-1.0.0.jar -DgroupId=atan -DartifactId=atan -Dversion=1.0.0 -Dpackaging=jar
            

életciklusokat tartalmazó parancsot adjuk ki a GNU/Linux parancsorban és hasonlót mondunk a Windows rendszerekben:

C:\Users\Norbi\Documents\MavenProjects\atan-1.0.0>mvn clean generate-sources javacc:jjdoc package site assembly:single install:install-file -Dfile=target/atan-1.0.0.jar -DgroupId=atan -DartifactId=atan -Dversion=1.0.0 -Dpackaging=jar
            

miközben a projektünk gyökerében a pom.xml állománnyal egy szinten álltunk. Természetesen a Maven életciklusok közül elegendő a mvn clean package használata, illetve ha még az RCSS protokoll beszéléséhez szükséges nyelvtan nincs Java forrásokra fordítva, akkor kell az mvn generate-sources. Továbbá az mvn install:install-file -Dfile=target/atan-1.0.0.jar -DgroupId=atan -DartifactId=atan -Dversion=1.0.0 -Dpackaging=jar akkor szükséges, amikor az Atan 1.0 tárgyát a lokális tárolóba akarjuk telepíteni.

A saját csapat forrásból történő felépítésén keresztül majd egy következő pont kis videó doksijában (screencast) láthatsz példát az Atan forrásból történő felépítésére.

A szoftver használata GNU/Linux és Windows környezetben

Megint csak teljesen platformfüggetlenek vagyunk, s amikor ezt olvassuk, akkor az előző pont nyomán feltehetjük, hogy az Atan 1.0 tárgya már a lokális tárolóba töltve pihen

[norbert@matrica ~]$ ls -l .m2/repository/atan/atan/1.0.0/
total 300
-rw-rw-r--. 1 norbert norbert 297463 Nov  8 08:43 atan-1.0.0.jar
-rw-rw-r--. 1 norbert norbert    452 Oct  2 11:25 atan-1.0.0.pom
-rw-rw-r--. 1 norbert norbert    160 Nov  8 08:44 _maven.repositories
[norbert@matrica ~]$ more .m2/repository/atan/atan/1.0.0/atan-1.0.0.pom 
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.x
sd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>atan</groupId>
  <artifactId>atan</artifactId>
  <version>1.0.0</version>
  <description>POM was created from install:install-file</description>
</project>
                

várva, hogy a fejlesztők a

        <dependency>
            <groupId>atan</groupId>
            <artifactId>atan</artifactId>
            <version>1.0.0</version>
        </dependency>
                  

koordinátákkal deklarálva használják. Amit az Atan 1.0-ra alapozott AranycsapatFC projektünkkel meg is teszünk. Ennek megfelelően a pom.xml állományunkat a következőképpen írjuk meg. Helytakarékosságból megintcsak vágunk az állományon, de természetesen a teljes szerepel a szóban forgó AranycsapatFC-0.0.1-project.zip projektben.

<?xml version="1.0" encoding="UTF-8"?>
<!--
 * pom.xml
 *
 * Aranycsapat FC
 *
 * Copyright (C) 2010, Bátfai Norbert
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Ez a program szabad szoftver; terjeszthető illetve módosítható a
 * Free Software Foundation által kiadott GNU General Public License
 * dokumentumában leírtak; akár a licenc 3-as, akár (tetszőleges) későbbi
 * változata szerint.
 *
 * Ez a program abban a reményben kerül közreadásra, hogy hasznos lesz,
 * de minden egyéb GARANCIA NÉLKÜL, az ELADHATÓSÁGRA vagy VALAMELY CÉLRA
 * VALÓ ALKALMAZHATÓSÁGRA való származtatott garanciát is beleértve.
 * További részleteket a GNU General Public License tartalmaz.
 *
 * A felhasználónak a programmal együtt meg kell kapnia a GNU General
 * Public License egy példányát; ha mégsem kapta meg, akkor
 * tekintse meg a <http://www.gnu.org/licenses/> oldalon.
 * -
 * Aranycsapat FC - 2011.09.25.
 * -
 *
 * Version history:
 *
 * 0.0.1    We are starting from the sources of Kekhalal FC
 *          http://progpater.blog.hu/2011/09/23/we_re_a_unit_not_a_one_man_show
 *
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>hu.fersml.aranyfc</groupId>
    <artifactId>AranycsapatFC</artifactId>
    <packaging>jar</packaging>
    <version>0.0.1</version>
    <name>AranycsapatFC</name>
    <url>http://progpater.blog.hu/2011/09/23/we_re_a_unit_not_a_one_man_show</url>
    <description>A "Mesterséges intelligencia a gyakorlatban: bevezetés a robotfoci programozásba" c. jegyzet egyik példaprojektje.</description>
    <organization>
        <name>Batfai Norbert, University of Debrecen, Department of Information Technology</name>
        <url>http://www.inf.unideb.hu/~nbatfai</url>
    </organization>
    <licenses>
        <license>
            <name>GNU GPL v3</name>
            <url>http://www.gnu.org/licenses/gpl.html</url>
        </license>
    </licenses>
    <developers>
        <developer>
            <id>norbi</id>
            <name>Norbert Batfai</name>
            <email>nbatfai@gmail.com</email>
            <url>http://www.inf.unideb.hu/~nbatfai</url>
            <organization>University of Debrecen</organization>
            <organizationUrl>http://www.inf.unideb.hu</organizationUrl>
        </developer>
    </developers>
    <dependencies>
        <dependency>
            <groupId>maven</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>1.8.1</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
        </dependency>
        <dependency>
            <groupId>atan</groupId>
            <artifactId>atan</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>hu.fersml.aranyfc.AranycsapatFC</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>project</descriptorRef>
                    </descriptorRefs>
                    <outputDirectory>${project.reporting.outputDirectory}</outputDirectory>
                </configuration>
            </plugin>
      <!-- reports -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-site-plugin</artifactId>
                <version>3.0-beta-3</version>
                <configuration>
                 ...                
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
                  

A GNU/Linux és Windows rendszerekben megintcsak egyformán járhatsz el, csak a könyvtár változik a következő példában attól függően, hogy az adott rendszeren éppen hol állsz. Az

[norbi@sgu AranycsapatFC-0.0.1]$ mvn clean package site assembly:single
                

életciklusokat tartalmazó parancsot adjuk ki a GNU/Linux parancsorban és hasonlóan ugyanezt mondjuk a Windows rendszerekben:

C:\Users\Norbi\Documents\MavenProjects\AranycsapatFC-0.0.1>mvn clean package site assembly:single
                

miközben a projektünk gyökerében állunk. Természetesen a tipikus esetek többségében elegendő a mvn clean package parancs kiadása, azaz ha nem akarjuk legenerálni a projekt szájtját, illetve előkészíteni a terjesztését.

[Megjegyzés][ERROR] Java heap space

Előfordulhat, ha a Maven projekteddel valamilyen memóriaintenzív (például a Checkstyle riport generálása a maven-checkstyle-plugin bővítménnyel) manővert hajtasz végre, hogy adott rendszeren ezt a hibát kapod, amit a parancssorban a kiadott

            set MAVEN_OPTS=-Xmx512m -XX:MaxPermSize=128m
                        

utasítással előzhetsz meg, ami a Maven használta Java rendszer által használható memóriaterület méretét növeli 512 megabájtra.

A szoftver használata NetBeans környezetben

A NetBeans környezetben történő felhasználás semmi újat nem hoz, hiszen itt nincs más dolgod, mint az elkészített atan-1.0.0.jar kitallózása a megfelelő, a korábbi pontban részletezett helyeken.

A szoftver tárgyalása

Az Atan 1.0 több interfésze és osztálya bővült. A főbb változásokat az Atan 0.4.3 interfészre alapozott Aranycsapat FC módosításain keresztül mutatjuk majd be, a kisebbek alkalmazását a Mighty Magyars FC csapatnál találjuk majd meg. Mielőtt a megváltozott forrásokat bemutatnánk (megint csak újra) megjegyezhetjük, hogy ezek Maven projektként letölthetőek a kurzus blogjának kapcsolódó posztjáról, de magának ennek a jegyzetnek is a mellékletét képezik.

II. rész - Saját csapat építése

6. fejezet - Atan alapú csapatok

Ebben a fejezetben saját csapatokat fejlesztünk, várhatóan növekvő játékerő-sorrendben, amit a japán világklasszis csapat magját alkotó HELIOS_base együttessel játszott mérkőzések eredményével mérünk. Most figyelmeztetlek: nagy saját sikerekre ne számíts, hiszen egy bevezető kurzus bevezető, néhány órás fejlesztési idejű csapatai állnak majd ki a 15 éve finomodó, ma nyilvánvalóan a világ egyik legjobb csapatával!

  • A Csorda FC csapatának tagjai képesek lesznek követni a labdát.

  • A Délibáb FC csapatának megszervezésével foglaljuk össze a Csorda FC fejlesztési tapasztalatait.

  • A Kékhalál FC csapatában tovább differenciáljuk a játékosokat: a kapus és a mezőnyjátékos korábbi elkülönülése mellett megjelenik a védő, a fedezet és a csatár szerepkör.

  • A Aranycsapat FC minden ágense egyedi forrású, az osztályok megválasztásával a világ legjobb emberi csapata tagjainak nevével tisztelgünk.

A Csorda FC, Délibáb FC és a Kékhalál FC csapatok készítésével szerezzük az első robotfoci tapasztalatokat, így itt nem tartjuk majd fontosnak a kapcsolódó OO alapelvek, például az OCP (Open/closed principle - azaz a kész forrásokat nem módosítjuk, hanem kiterjesztjük) tiszteletben tartását. A példánál maradva ez azt jelenti, hogy az említett három csapatnál az osztályhierarchiát gyakorlatilag megtartva az osztályokat (más csomagnevek alatt, egyszerűen) újrafogalmazzuk.

A következő, adott csapatokhoz kötődő fejezetek címében tehát a HELIOS_base csapattal vívott mérkőzések eredménye is megjelenik. E névadó mérkőzéseket el is mentettük, ezek a következők:

A csupán 9 kapott gól nagyon-nagyon kecsegtető, de ne bízzuk el magunkat, mert ez csak a látszat, egyrészt abból adódik, hogy csapataink bizonyos játékelemeket még nem tudnak kezelni (ilyen például a bedobás) ezért ki kell várni, amig a bíró az alapértelmezett 5 másodperc után újra játékba hozza a labdát. Ez a fajta „időhúzás” kedvez nekünk, illetve a HELIOS_base ágensek betömörülő passzív védelmünkkel szemben sokszor tanácstalanok, ez a másik fajta „időhúzás” is nyilván a mi malmunkra hajtja a vizet. Aki reálisabban akarja megítélni saját csapataink játékerejét, annak a GoldenFC - HELIOS2011, 0:50 mérkőzést ajánlom megtekinteni!

Az szóban forgó (és a felvételről is megtekinthető említett) mérkőzéseket az agent2d-3.0.0 csomag HELIOS_base csapatával játszottuk.

„... a siker általában a lelkes harcosokat támogatja.”

Hidegkuti Nándor [SEBES]

HELIOS_base - Csorda FC, 24:0

A jelen jegyzet logikája szerint a CsordaFC az olvasó első Java alapú robotfoci csapata. A puding próbájaként le is játszunk egy mérkőzést a világklasszis japán csapat HELIOS_base „fiók csapatával”. Nem váratlan, hogy a következő ábra sommázta súlyosan nagy arányú vereséget szenved el a csapatunk.

6.1. ábra - HELIOS_base - Csorda FC, 24:0

HELIOS_base - Csorda FC, 24:0


A csapat finomkodónak nem nevezhető névadója egyértelműen a játékosok mutatott viselkedése, miszerint tipikusan csoportba verődve követik a labdát.

6.2. ábra - A mezőnyjátékosok egyetlen viselkedése: a labda követése.

A mezőnyjátékosok egyetlen viselkedése: a labda követése.


De a Csorda FC hivatása nem is a világbajnoksági kvalifikáció, hanem annak megmutatása, hogyan használjuk az Atan interfészt elképzelt robotfoci csapat-viselkedésünk megvalósításához. Erről szólnak a következő, a Csorda FC osztályait ismertető pontok.

A Csorda FC osztályai

A fejlesztendő csapatot absztraháló osztályokat a hu.fersml.csordafc csomagba helyezzük.

A CsordaFC osztály

A Java platformon megszokott, hogy céleszköz specifikus alkalmazásmodelleket használunk. Például a javax.microedition.midlet.MIDlet osztályból származtatjuk a saját osztályunkat, ha mobiltelefonos alkalmazást készítünk, tipikusan a javax.servlet.http.HttpServlet osztályból, ha a szerver oldalon, a webszerver virtuális gépére, a java.applet.Applet osztályból, ha a kliens oldalon, a webböngésző virtuális gépére szánjuk leendő osztályunk értelmezését.

Az Atan alapú robotfoci csapat esetén a atan.model.AbstractTeam osztályból származtatjuk saját csapatunkat.

package hu.fersml.csordafc;

public class CsordaFC extends atan.model.AbstractTeam {

  public CsordaFC(String team, int port, String host) {

    super(team, port, host);

  }

  @Override
  public atan.model.ControllerPlayer getNewController(int number) {

    hu.fersml.csordafc.Jatekos jatekos = null;

    switch (number) {

      case 0:
        jatekos = new hu.fersml.csordafc.Kapus();
        break;

      case 10:
      case 9:
      case 8:
      case 7:
      case 6:
      case 5:
      case 4:
      case 3:
      case 2:
      case 1:
        jatekos = new hu.fersml.csordafc.Jatekos();
        break;

    }

    return jatekos;
  }

  public static void main(String[] args) {

    org.apache.log4j.BasicConfigurator.configure();

    if (args.length == 1) {
      new CsordaFC(args[0], 6000, "localhost").connectAll();
    } else {
      new CsordaFC("CsordaFC", 6000, "localhost").connectAll();
    }
  }
}
                    

Jelen osztályunkban implementációval láttuk el az ős atan.model.AbstractTeam osztály absztrakt getNewController metódusát. Mást gyakorlatilag nem is tehettünk volna, hiszen ez egy absztrakt metódusa az ős atan.model.AbstractTeam osztálynak. Lényegében annak konstruktorából hívódik, feladata az érzeteket átvenni képes objektum megadása az atan.model.SServerPlayer objektum felé, amely az ágens UDP kliens kapcsolatát absztrahálja.

Az Atan API dokumentáció nem részletezi a getNewController metódusának i változóját, ami nyilván adott játékosra utal. De mivel mindig félreértésre ad okot, hogy honnan kezdjük a számozást (0-tól, 1-től, illetve milyen tartománybeli lehet) ezért megnézzük az Atan (atan/model/AbstractTeam.java) forrásában.

                    
    private SServerPlayer[] players  = new SServerPlayer[11];

    ...

    /**
     *
     * @param i
     * @return
     */
    public abstract ControllerPlayer getNewController(int i);

    /**
     *
     */
    public void createNewPlayers() {
        for (int i = 0; i < size(); i++) {
            players[i] = new SServerPlayer(teamName, getNewController(i), port, hostname);
        }
    }
                    

Miután egyértelmű, hogy a számozást 0-tól kezdjük, hiszen az i egyben a players tömb indexe is.

A Jatekos osztály

A Jatekos osztályban tervezzük majd építgetni játékosaink absztrakcióját. Itt egyelőre ebből semmi nem látszik, hanem majd a JatekosAdapter osztályon keresztül a Kapus osztály domborítja ki a Csorda FC építésének koncepcióját, miszerint a JatekosAdapter osztály egy klasszikus adapter jellegű osztály, azaz (akár üres testtel is) összegyűjti az atan.model.ControllerPlayer interfész metódusait. Ez annyiban egyszerűsíti a fejlesztést, hogy az adapter osztályból származtatott osztályban csak azokat a metódusokat definiáljuk felül, amelyekkel érdemben foglalkozni akarunk.

package hu.fersml.csordafc;

public class Jatekos extends hu.fersml.csordafc.JatekosAdapter {
}
                    

Itt egyetlen „érző” metódussal sem kívántunk foglalkozni, mert mezőnyjátékosok esetén megelégszünk majd azzal az alapfunkcionalitással, hogy kövessék a labdát. Ennek programozását a következő pontban tesszük meg.

A JatekosAdapter osztály

Nagyobb lélegzetvételnyi osztály következik, a JatekosAdapter. De megjegyezhetjük, hogy nagysága csakis formális, hiszen kódjának legnagyobb része az atan.model.ControllerPlayer interfész metódusainak üres testtel való megvalósítása. Elkészítésénél egyszerűen úgy járunk el, hogy a atan/model/ControllerPlayer.java forrásból átmásoljuk a deklarált módszerek szignatúráját, s egy üres testet írunk a nevük után a pontosvessző helyett, azaz megadunk egy üres testes implementációt.

package hu.fersml.csordafc;

public class JatekosAdapter implements atan.model.ControllerPlayer {

  private atan.model.ActionsPlayer jatekos;
  protected boolean kozepkezdes;
  protected boolean latomAFocit;
  protected double distanceFoci;
  protected double directionFoci;
  protected double distChangeFoci;
  protected double dirChangeFoci;
  protected boolean latomASzelet;

  @Override
  public atan.model.ActionsPlayer getPlayer() {
    return jatekos;
  }

  @Override
  public void setPlayer(atan.model.ActionsPlayer jatekos) {
    this.jatekos = jatekos;
  }

  @Override
  public void preInfo() {
    kozepkezdes = false;
    latomAFocit = latomASzelet = false;
  }

  @Override
  public void postInfo() {

    if (kozepkezdes) {
      kozepkezdes();
    } else if (latomASzelet) {
      palyanMaradni();
    } else if (latomAFocit) {
      rugdEsFuss();
    } else {
      teblabol();
    }

  }

  @Override
  public void infoHearPlayMode(atan.model.enums.PlayMode playMode) {

    kozepkezdes = true;

  }

  ...
  // a Csak a labda FC-től ugyanaz a /* 4-4-2 */ felállás. 
  ...

  @Override
  public void infoSeeBall(double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomAFocit = true;
    distanceFoci = distance;
    directionFoci = direction;
    dirChangeFoci = distChange;
    dirChangeFoci = dirChange;

  }

  protected void rugdEsFuss() {

    if (distanceFoci < 0.6) {

      getPlayer().kick(65, directionFoci + 90);

    }
    getPlayer().turn(directionFoci);
    getPlayer().turnNeck(directionFoci + dirChangeFoci);
    getPlayer().dash(100);

  }

  @Override
  public void infoSeeFlagRight(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    if (distance < 8.0) {
      latomASzelet = true;
    }

  }

  @Override
  public void infoSeeFlagLeft(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    if (distance < 8.0) {
      latomASzelet = true;
    }

  }

  @Override
  public void infoSeeFlagOwn(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    if (distance < 8.0) {
      latomASzelet = true;
    }

  }

  @Override
  public void infoSeeFlagOther(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    if (distance < 8.0) {
      latomASzelet = true;
    }

  }

  protected void palyanMaradni() {

    getPlayer().turn(90);
    getPlayer().turnNeck(180);
    getPlayer().dash(40);
  }

  protected void teblabol() {

    getPlayer().turn(25);
    getPlayer().turnNeck(50);
    getPlayer().dash(30);

  }

  ...
  
  @Override
  public String getType() {
    return "CsordaFC";
  }
}
                

Az osztály példányváltozói a középkezdés és a labda követésének megvalósítását szolgálják. Ezzel összhangban az a atan.model.ControllerPlayer interfész „érző” metódusai közül a infoHearPlayMode és az infoSeeBall metódusokat látjuk el érdemi implementációval.

JatekosAdapter osztályon alapuló ágensek viselkedését a postInfo függvényből vezéreljük: középkezdéskor 4-4-2 pozíciót vesz fel a csapat, ha a játék során az ágens érzékeli (látja) a focit, akkor a rugdEsFuss metódusé lesz a vezérlés, ami a labda felé mozgatja a játékost, illetve egyfajta oldalról becsúszó szerelés (directionFoci + 90 a kódban) formájában az elég közeli labdát meg is rúgja.

protected void rugdEsFuss() {

    if (distanceFoci < 0.6) {

      getPlayer().kick(65, directionFoci + 90);

    }
    getPlayer().turn(directionFoci);
    getPlayer().turnNeck(directionFoci + dirChangeFoci);
    getPlayer().dash(100);

  }
                    

Ha viszont a játékos nem látja a játékszert, akkor kisebbet fordul testtel, nagyobbat fejjel, kvázi mintha szétnézne és lassan lép is a teblabol függvény értelmében.

protected void teblabol() {

    getPlayer().turn(25);
    getPlayer().turnNeck(50);
    getPlayer().dash(30);

  }
                    

A Kapus osztály

Ha a CsordaFC osztályban a kapus szerepébe egy Jatekos osztálybeli objektumot állítunk, akkor az alábbi ábrán is látható és persze természetes módon a kapus mezőnyjátékosként viselkedve elhagyja a kaput.

6.3. ábra - A kapus elhagyja a kaput, illetve számos esetben a játékosok a pályát.

A kapus elhagyja a kaput, illetve számos esetben a játékosok a pályát.


Tehát azt akarjuk biztosítani, hogy a kapus ne hagyja el őrhelyét. Ehhez új példánytagként bevezetjük a mozoghat változót, ami arról tudósít, hogy a kapus egyáltalán mozoghat-e, illetve felül definiáljuk az ős rugdEsFuss függvényét. A mozoghat változót az alábbi kódcsipet szerint akkor kapcsoljuk be, ha a kapus közel kerül a tizenhatoshoz vagy a gólvonalhoz.

@Override
  public void infoSeeFlagPenaltyOwn(atan.model.enums.Flag flag, double distance, double direction, double distChange,
          double dirChange, double bodyFacingDirection, double headFacingDirection) {

    if (distance > 14.0) {
      mozoghat = true;
    }
  }

  @Override
  public void infoSeeFlagGoalOwn(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    if (distance > 4.0) {
      mozoghat = true;
    }
  }
                    

A rugdEsFuss függvény új implementációja pedig azt mondja, hogy ha nagyon közel van a labda, akkor megpróbálom megfogni, ha csak közel, megrúgni, ha megfelelően közel van, határozottabban rámozdulni, egyébként pedig csak figyelni és finomabban rámozdulni.

protected void rugdEsFuss() {

    if (distanceFoci < 0.7) {
      getPlayer().catchBall(directionFoci);
    } else if (distanceFoci < 0.9) {
      getPlayer().kick(100, directionFoci + 90);
    } else if (distanceFoci < 30.0) {
      egyuttElaJatekkal(60);
    } else {
      egyuttElaJatekkal(20);
    }

  }
                    

S most utólag foglaljuk össze az osztály teljes kódját!

package hu.fersml.csordafc;

public class Kapus extends hu.fersml.csordafc.Jatekos {

  protected boolean mozoghat;

  @Override
  public void preInfo() {
    super.preInfo();
    mozoghat = false;
  }

  protected void rugdEsFuss() {

    if (distanceFoci < 0.7) {
      getPlayer().catchBall(directionFoci);
    } else if (distanceFoci < 0.9) {
      getPlayer().kick(100, directionFoci + 90);
    } else if (distanceFoci < 30.0) {
      egyuttElaJatekkal(60);
    } else {
      egyuttElaJatekkal(20);
    }

  }

  protected void egyuttElaJatekkal(int ero) {

    getPlayer().turnNeck(directionFoci + dirChangeFoci);
    getPlayer().turn(directionFoci + dirChangeFoci);

    if (mozoghat) {
      getPlayer().dash(ero);
    }
  }

  @Override
  public void infoSeeFlagPenaltyOwn(atan.model.enums.Flag flag, double distance, double direction, double distChange,
          double dirChange, double bodyFacingDirection, double headFacingDirection) {

    if (distance > 14.0) {
      mozoghat = true;
    }
  }

  @Override
  public void infoSeeFlagGoalOwn(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    if (distance > 4.0) {
      mozoghat = true;
    }
  }
}
                    

Pályán maradni

A Kapus osztály iménti bevezetése képernyőképénél említettük, hogy a játékosok sok esetben elhagyják a pályát (itt megjegyezhetjük, hogy tapasztalataink szerint az Atan 0.4.3 interfész mellett a keleti oldalra tett csapat tipikusan stabilabban érzékel).

6.4. ábra - A játékosok elhagyják a játékteret.

A játékosok elhagyják a játékteret.


Ezt megelőzendő felvettük latomASzelet újabb logikai példányváltozót a JatekosAdapter osztályba, ami azt jelzi, hogy a játékos látja a játéktér szélét. (Mint már említettük, a pálya sok esetben értelmezhetetlen elhagyása is megfigyelhető az Atan 0.4.3 használata esetén, de ez varázsütésre eltűnik, ha majd áttérünk az 1.0 API használatára.)

Naplózás

Robotfoci témában még a saját szoftverünk működésével is ismerkednünk kell, ehhez nélkülözhetetlen a naplózás. Az Atan erre az Apache log4j csomagot használja. Ha a saját osztályainkban naplózni szeretnénk, célszerű az Atan sajátját elnyomni, például a naplózási szint

public static void main(String[] args) {

        org.apache.log4j.BasicConfigurator.configure();
        org.apache.log4j.Logger.getLogger("atan.model").setLevel(org.apache.log4j.Level.INFO);
                    

magasabbra állításával, s ezzel párhuzamosan a vizsgálandó osztályban megadni egy naplózó objektumot

public class Kapus extends hu.fersml.csordafc.Jatekos {

    protected boolean mozoghat;
    private static org.apache.log4j.Logger logger =
            org.apache.log4j.Logger.getLogger(Kapus.class);
                    

amelybe INFO naplózási szinten nyomhatjuk a kérdéses információkat

        if (getPlayer().isTeamEast() ) {
            logger.info(latomAFocit);
        }
                    
[Megjegyzés]Az Atan naplózásának kikapcsolása

A fentiekkel összhangban így tudod kikapcsolni az Atan naplózását.

public static void main(String[] args) {

    org.apache.log4j.BasicConfigurator.configure();
    org.apache.log4j.Logger.getLogger("atan.model").setLevel(org.apache.log4j.Level.OFF);
                      

Példaképpen tekintsük meg a Kekhalal projekt Tamado osztályának naplózását.

[Figyelem]Az Atan naplózás kikapcsolásának veszélyei

Ha az előző pontnak megfelelően a naplózást kikapcsolod, bizonyos hibák misztikusnak ható következményekkel járhatnak, például meg sem moccannak a játékosaid a pályán, miközben néha-néha éppcsak topog egy-kettő. Ilyenkor meglehet, hogy egyszerű programozási hibát vétettél, amiről nem tudsz, mert az Atan naplózását elnyomtad. Ilyen hiba lehet például egy rossz tömbindex kezelést kísérő java.lang.ArrayIndexOutOfBoundsException amit azonnal meglátsz az alábbi beállítás mellett:

public static void main(String[] args) {

    org.apache.log4j.BasicConfigurator.configure();
    org.apache.log4j.Logger.getLogger("atan.model").setLevel(org.apache.log4j.Level.ERROR);
                      

HELIOS_base - Delibáb FC, 20:0

A Délibáb FC csatatának megírását az előző fejezet Csorda FC csapata fejlesztési tapasztalatai összefoglalásának igénye szülte, melynek során a JatekosAdapter osztályt kicsit megmetsszük, azaz eddigi funkcionalitását áttoljuk a gyermek Jatekos osztályba, így a JatekosAdapter egy (közel) tiszta adapter osztállyá válik majd. Magának a Jatekos osztálynak a viselkedését pedig megpróbáljuk némiképpen finomítani. De ez csak látszólagos nagy „taktikai fegyelmet” hoz majd a csapat pályán mutatott viselkedésében, mert idővel kiütközik a Csorda FC már megismert „játékmintája”.

Nem meglepő módon ez a csapat is nagyon súlyos vereségeket tud elszenvedni a világklasszis japán csapattól, itt egy kevéssé súlyos (13:0) mérkőzés felvételét találhatja meg a kedves olvasó: 201109141048-HELIOS_base_13-DelibabFC_0.rcg.

A Delibáb FC osztályai

A fejlesztendő csapatot absztraháló osztályokat a hu.fersml.delibabfc csomagba helyezzük.

A DelibabFC osztály

A DelibabFC osztály gyakorlatilag ugyanaz, mint az előző CsordaFC osztály, csak a naplózási szint állításában különbözik, így csak ezt a csipetet közöljük.

  public static void main(String[] args) {

    org.apache.log4j.BasicConfigurator.configure();
    org.apache.log4j.Logger.getLogger("atan.model").setLevel(org.apache.log4j.Level.INFO);

    if (args.length == 1) {
      new DelibabFC(args[0], 6000, "localhost").connectAll();
    } else {
      new DelibabFC("DelibabFC", 6000, "localhost").connectAll();
    }
  }
}
                

A Jatekos osztály

A Jatekos osztályunk szépen kikerekedett, mert a JatekosAdapter osztályból szinte mindent, ami nem annak adapter jellegével kapcsolatos (azaz nem a atan.model.ControllerPlayer interfész metódusának üres testű implementációja) áthelyeztünk ebbe az osztályba.

package hu.fersml.delibabfc;

public class Jatekos extends hu.fersml.delibabfc.JatekosAdapter {
  /*
  * Ezt az egyszerű, tipikusan szimpla logikai változókkal 
  * jelzett/megvalósított egyszerű reflexszerű "viselkedést" az Atan 
  * példa forrásaiból tanultuk (atan/java/sample/Simple.java stb).  
  */     
  protected boolean latomAFocit;
  protected double distanceFoci;
  protected double directionFoci;
  protected double distChangeFoci;
  protected double dirChangeFoci;
  /**/
  protected boolean latomASzelet;
  protected double distanceSzele;
  protected double directionSzele;
  protected double distChangeSzele;
  protected double dirChangeSzele;
  /**/
  java.util.Random random = new java.util.Random();

  @Override
  public void preInfo() {
    super.preInfo();
    latomAFocit = false;
    latomASzelet = false;
  }

  @Override
  public void postInfo() {

    if (kozepkezdes) {
      kozepkezdes();
    } else if (latomASzelet && distanceSzele < 10.0) {
      palyanMaradni();
    } else if (latomAFocit) {
      if (distanceFoci < 0.7) {
        rugdEsFuss();
      } else if (distanceFoci < 1.4) {
        becsuszas();
      } else if (distanceFoci < 15.0) {
        egyuttElaJatekkal(100);
      } else if (distanceFoci < 25.0) {
        egyuttElaJatekkal(70);
      } else if (distanceFoci < 30.0) {
        egyuttElaJatekkal(40);
      } else if (distanceFoci < 35.0) {
        egyuttElaJatekkal(20);
      } else {
        egyuttElaJatekkal(5);
      }
    } else {
      teblabol();
    }

  }

  protected void rugdEsFuss() {

    getPlayer().kick(35, directionFoci);
    getPlayer().turn(directionFoci);
    getPlayer().turnNeck(dirChangeFoci);
    getPlayer().dash(100);
  }

  protected void becsuszas() {

    getPlayer().kick(85, directionFoci + 90);
    getPlayer().turn(180);
    getPlayer().dash(40);

  }

  protected void egyuttElaJatekkal(int seb) {

    getPlayer().turnNeck(dirChangeFoci + random.nextInt(101 - seb) - (101 - seb) / 2);
    getPlayer().turn(directionFoci + dirChangeFoci);
    getPlayer().dash(seb);
  }

  /* 4-3-3 */
  @Override
  protected void kozepkezdes() {

    switch (getPlayer().getNumber()) {

      // Kapus
      case 1:
        getPlayer().move(-51, 0);
        break;

      // Tamadok

      case 2:
        getPlayer().move(-4, 10);
        break;

      case 3:
        getPlayer().move(-1, 1);
        break;

      case 4:
        getPlayer().move(-3, -11);
        break;

      // Kp.

      case 5:
        getPlayer().move(-18, -16);
        break;

      case 6:
        getPlayer().move(-17, 2);
        break;

      case 7:
        getPlayer().move(-18, 17);
        break;

      // Vedo

      case 8:
        getPlayer().move(-35, -19);
        break;

      case 9:
        getPlayer().move(-33, -10);
        break;

      case 10:
        getPlayer().move(-34, 12);
        break;

      case 11:
        getPlayer().move(-35, 24);
        break;

    }

  }

  @Override
  public void infoSeeBall(double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomAFocit = true;
    distanceFoci = distance;
    directionFoci = direction;
    dirChangeFoci = distChange;
    dirChangeFoci = dirChange;

  }

  protected void latomASzelet(double distance, double direction, double distChange, double dirChange) {

    latomASzelet = true;
    distanceSzele = distance;
    directionSzele = direction;
    dirChangeSzele = distChange;
    dirChangeSzele = dirChange;

  }

  @Override
  public void infoSeeFlagRight(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomASzelet(distance, direction, distChange, dirChange);

  }

  @Override
  public void infoSeeFlagLeft(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomASzelet(distance, direction, distChange, dirChange);

  }

  @Override
  public void infoSeeFlagOwn(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomASzelet(distance, direction, distChange, dirChange);

  }

  @Override
  public void infoSeeFlagOther(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomASzelet(distance, direction, distChange, dirChange);

  }

  protected void palyanMaradni() {

    getPlayer().turn(90);
    getPlayer().turnNeck(180);
    getPlayer().dash(40);

  }

  protected void teblabol() {

    getPlayer().turn(random.nextInt(5));
    getPlayer().turnNeck(random.nextInt(15));
    getPlayer().dash(20);

  }

  @Override
  public String getType() {
    return "Jatekosbol";
  }
}
                

A JatekosAdapter osztály

A JatekosAdapter osztály gyakorlatilag ugyanannyit fogyott, mint amennyit a Jatekos osztály hízott.

package hu.fersml.delibabfc;

public class JatekosAdapter implements atan.model.ControllerPlayer {

  protected atan.model.ActionsPlayer jatekos;
  protected boolean kozepkezdes;

  @Override
  public atan.model.ActionsPlayer getPlayer() {
    return jatekos;
  }

  @Override
  public void setPlayer(atan.model.ActionsPlayer jatekos) {
    this.jatekos = jatekos;

  }

  @Override
  public void preInfo() {
    kozepkezdes = false;
  }

  @Override
  public void postInfo() {

    if (kozepkezdes) {
      kozepkezdes();
    }
  }

  @Override
  public void infoHearPlayMode(atan.model.enums.PlayMode playMode) {

    switch (playMode) {
      case BEFORE_KICK_OFF:
      case KICK_OFF_L:
      case KICK_OFF_R:
        kozepkezdes = true;
        break;
    }

  }

  ...
  // a Csak a labda FC-től ugyanaz a /* 4-4-2 */ felállás. 
  ...

  protected void rugdEsFuss() {
  }

  protected void latomASzelet(double distance, double direction, double distChange, double dirChange) {
  }

  ...

  @Override
  public String getType() {
    return "JatekosAdapterbol";
  }
}
                

A Kapus osztály

A Kapus osztályt is átalakítottuk, már nem csupán rugdEsFuss módszerében polimorfiája a Jatekos (JatekosAdapter) osztálynak, hanem saját postInfo eljárást is kapott.

package hu.fersml.delibabfc;

public class Kapus extends hu.fersml.delibabfc.Jatekos {

  protected boolean mozoghat;

  @Override
  public void preInfo() {
    super.preInfo();
    mozoghat = false;
  }

  @Override
  public void postInfo() {

    if (kozepkezdes) {
      kozepkezdes();
    } else if (latomAFocit) {
      if (distanceFoci < 1.2) {
        getPlayer().catchBall(directionFoci);
      } else if (distanceFoci < 5.0) {
        egyuttElaJatekkal(60);
      } else if (distanceFoci < 10.0) {
        egyuttElaJatekkal(20);
      } else {
        egyuttElaJatekkal(1);
      }
    } else if (latomASzelet && distanceSzele < 10) {
      palyanMaradni();
    } else {
      teblabol();
    }
  }

  protected void egyuttElaJatekkal(int seb) {

    getPlayer().turnNeck(directionFoci + dirChangeFoci);
    getPlayer().turn(directionFoci + dirChangeFoci);

    if (mozoghat) {
      getPlayer().dash(seb);
    }
  }

  @Override
  public void infoSeeFlagPenaltyOwn(atan.model.enums.Flag flag, double distance, double direction, double distChange,
          double dirChange, double bodyFacingDirection, double headFacingDirection) {

    if (distance > 14.0) {
      mozoghat = true;
    }
  }

  @Override
  public void infoSeeFlagGoalOwn(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    if (distance > 4.0) {
      mozoghat = true;
    }
  }
}
                

Atan rangadók

Ebben a pontban három mérkőzést játszunk le. Vizsgáljuk a saját Délibáb FC viselkedését, de a két Atanos példa-csapat között is előjegyzünk egy találkozót.

[Megjegyzés]Atan Sample1 - Délibáb FC, 0:0

A Délibáb FC viselkedése közelít a A Csorda FC viselkedéséhez, ha a labda irányába, annak távolságától függő mozgásukat egyformán mohóra (100) állítjuk.

if (distanceFoci < 0.7) {
        rugdEsFuss();
      } else if (distanceFoci < 1.4) {
        becsuszas();
      } else if (distanceFoci < 15.0) {
        egyuttElaJatekkal(100);
      } else if (distanceFoci < 25.0) {
        egyuttElaJatekkal(100);
      } else if (distanceFoci < 30.0) {
        egyuttElaJatekkal(100);
      } else if (distanceFoci < 35.0) {
        egyuttElaJatekkal(100);
      } else {
        egyuttElaJatekkal(100);
      }
                    

Az eldöntetlen mérkőzés felvételét itt találja a kedves olvasó: 201109131854-Simple1_0-DelibabFC_0-100ak.rcg.

[Megjegyzés]Atan Sample1 - Délibáb FC, 1:1

A Délibáb FC viselkedésében látszólag nagyobb a taktikai fegyelem az eredeti értékekkel. Az ugyancsak eldöntetlen mérkőzés felvétele itt található: 201109140914-Simple1_1-DelibabFC_1-nem100ak.rcg

6.5. ábra - A Délibáb FC négyes számú hatalmas bombája 22-ről.

A Délibáb FC négyes számú játékosának hatalmas bombája 22-ről.


[Megjegyzés]Atan Sample1 - Atan Sample2, 0:0

A két példa-csapat egymás elleni viselkedését is megtekintjük ebben a pontban.

6.6. ábra - Pillanatkép az Atan Sample1 - Atan Sample2 mérkőzésről.

Pillanatkép az Atan Sample1 - Atan Sample2 mérkőzésről.


Az eldöntetlen mérkőzés felvételét itt találja a kedves olvasó: 201109141026-Simple1_0-Simple2_0.rcg.

HELIOS_base - Kékhalál FC, 10:0

A Kékhalál FC csapatánál elkezdjük a játékosok pozíciója szerinti differenciált viselkedésének megvalósítását, azzal, hogy külön osztályokban absztraháljuk a védők, a középpályások és a támadók viselkedését. (A csapat névadója volt, hogy bár eleinte taktikailag fegyelmezett társulat látszatát keltik, ami azonban hirtelen visszazuhan a Délibáb FC szintjére.)

Fejlesztői szándékunk (és a következő ábrán láthatóvá tett trajektória) szerint a védőink a saját térfelükön maradnak.

6.7. ábra - A 9-es mezt viselő védő egy félidőnyi trajektóriája.

A 9-es mezt viselő védő egy félidőnyi trajektóriája.


A középpályásaink tizenhatostól-tizenhatosig vannak jelen a pályán.

6.8. ábra - Az 5-ös mezt viselő középpályás egy félidőnyi trajektóriája.

Az 5-ös mezt viselő középpályás egy félidőnyi trajektóriája.


A csatárokat az egész pályán hagyjuk érvényesülni, ezt az is indokolja, hogy a további csapatoknál majd rájuk bízzuk a rögzített szituációk (például a bedobás, szöglet, szabadrúgás) megoldását.

6.9. ábra - A 4-es mezt viselő csatár egy félidőnyi trajektóriája.

A 4-es mezt viselő csatár egy félidőnyi trajektóriája.


Ha eredményességben nem is, de koncepcióban már több van a Kékhalál FC-ben, mint az Atan példacsapatában, ahol a játékosok, a következő ábra által is jól mutatott: „figyeld a labdát, fuss és rúgd” algoritmust követik.

6.10. ábra - Az Atan-os példa-csapat 11-es mezt viselő játékosának egy félidőnyi trajektóriája.

Az Atan-os példa-csapat 11-es mezt viselő játékosának egy félidőnyi trajektóriája.


Az imént készült pillanatfelvételek a következő mérkőzésen készültek: 201109151049-Simple1_0-KekhalalFC_1-kezdesbol.rcg.

A Kékhalál FC osztályai

A fejlesztendő csapatot absztraháló osztályokat a hu.fersml.bsodfc (Blue Screen of Death) csomagba helyezzük. A csapatot felkészítjük a saját rögzített szituációk, mint például a saját szöglet (játékállapotban CORNER_KICK_OWN), a saját bedobás (KICK_IN_OWN) és a saját szabad (FREE_KICK_OWN) elvégzésére.

A KekhalalFC osztály

Az osztály a Délibáb FC indító osztályához képes annyiban változott, hogy a atan.model.ControllerPlayer ágens objektumokat nem csupán a Jatekos és Kapus osztályokból származtattuk, hanem a Vedo, Fedezet, Tamado és Kapus osztályokból, illetve az Atan logolását kikapcsoltuk, hogy jobban szem előtt legyenek majd a saját osztályainkból ennél a csapatnál már meginduló naplózó üzenetek.

package hu.fersml.bsodfc;

public class KekhalalFC extends atan.model.AbstractTeam {

  public KekhalalFC(String team, int port, String host) {

    super(team, port, host);

  }

  @Override
  public atan.model.ControllerPlayer getNewController(int number) {

    hu.fersml.bsodfc.Jatekos jatekos = null;

    switch (number) {

      case 0:
        jatekos = new hu.fersml.bsodfc.Kapus();
        break;

      case 10:
      case 9:
      case 8:
      case 7:
        jatekos = new hu.fersml.bsodfc.Vedo();
        break;

      case 6:
      case 5:
      case 4:
        jatekos = new hu.fersml.bsodfc.Fedezet();
        break;

      case 3:
      case 2:
      case 1:
        jatekos = new hu.fersml.bsodfc.Tamado();
        break;

    }

    return jatekos;
  }

  public static void main(String[] args) {

    org.apache.log4j.BasicConfigurator.configure();
    org.apache.log4j.Logger.getLogger("atan.model").setLevel(org.apache.log4j.Level.OFF);

    if (args.length == 1) {
      new KekhalalFC(args[0], 6000, "localhost").connectAll();
    } else {
      new KekhalalFC("KekhalalFC", 6000, "localhost").connectAll();
    }
  }
}
                  

A JatekosAdapter osztály

Az osztály a Délibáb FC ugyanilyen nevű osztályához képest nem változott.

A Jatekos osztály

Az eddigiek során azt az egyszerű [ATAN] modellt követtük, hogy a preInfo metódusban olyan logikai változókat nulláztunk, amelyek tipikusan valamilyen érzékelést jeleztek, például: „látom a labdát”, „látok egy zászlót”. Ha a preInfo metódusbeli nullázást követően a szóban forgó szimulációs ciklus alatt valamelyik érző metódus átállította a megfelelő logikai változót, akkor a postInfo módszerben végrehajtottuk az érzékelésnek megfelelő reakciót.

Ez a szimpla megközelítés a kezelendő rögzített események megoldására alkalmatlan, hiszen ha például következik egy saját bedobás szituáció, akkor egy/az adott játékosnak oda kell mennie és elvégeznie. Ám amíg az oldalvonalhoz ér, egészen biztosan sok szimulációs (akár a szerveren értelmezett, akár az Atanban értelmezett) ciklus fog lefutni és az eddigi, most részletezett megközelítéssel már a bedobás szükségességét felismerő ciklus után a következő ciklusra el is felejti az ágens, hogy tulajdonképpen ő dobni indult.

Megoldás, hogy a kezelendő saját rögzített szituációkat jelző változókat nem nullázzuk minden ciklus feldolgozása esetén, hanem csak akkor, ha a játékszer újra játékba (PLAY_ON) került, amikor is a jatekban függvény hívódik majd a infoHearPlayMode függvényből.

  protected void jatekban() {

    bedobas = false;
    szoglet = false;
    szabad = false;
    kirugas = false;

  }
                  

Legyen tehát a labda játékban, majd következzen egy bedobás!

@Override
  public void infoHearPlayMode(atan.model.enums.PlayMode playMode) {

    super.infoHearPlayMode(playMode);

    switch (playMode) {
    ...
      case KICK_IN_OWN:
        bedobas = true;
        break;
    ...
                  

A postInfo módszerben pedig figyelünk erre:

@Override
  public void postInfo() {

    if (bedobas) {
      bedobashoz();
    } else if (szabad) {
      szabadhoz();
    } else if (szoglet) {
      szoglethez();
    } else if (kirugas) {
      kirugashoz();
    } else if (kozepkezdes) {
      kozepkezdes();
    } else {
      jatekbanVezerles();
    }
  }
                  

ahonnan a bedobashoz függvény hívódik, amelyben ha nem látjuk a labdát, keressük; ha látjuk megyünk felé, s ha elég közel van, bedobjuk.

protected void bedobashoz() {

    if (latomAFocit) {

      if (distanceFoci < 0.9) {
        bedobas();
      } else {
        getPlayer().turn(directionFoci);
        getPlayer().dash(100);
      }

    } else {

      getPlayer().turnNeck(25);
    }
  }
                  

A bedobást megvalósító függvényünk a játékos a bedobáshoz érkezés szögétől függően több esetben már valóban sikerrel tud bedobni, az alábbi kód szerint:

protected void bedobas() {

    double bedobasSzoge = -180.0 + directionFoci;
    if (directionFoci < 0.0) {
      bedobasSzoge = 180.0 + directionFoci;
    }

    logger.info("BEDOBAS "
            + getPlayer().getNumber()
            + " foci tavolsaga = " + distanceFoci
            + " iranya = " + directionFoci
            + " bedobas iranya = " + bedobasSzoge);

    getPlayer().kick(45, bedobasSzoge);
    getPlayer().turn(bedobasSzoge);

  }
                  

Az osztály teljes kódja a következő.

package hu.fersml.bsodfc;

public class Jatekos extends hu.fersml.bsodfc.JatekosAdapter {
  /**/

  private static org.apache.log4j.Logger logger =
          org.apache.log4j.Logger.getLogger(Jatekos.class);
      
  ...
  // itt vannak a szokásos logikai és tároló változóink
  ...
  
  /**/
  protected boolean bedobas = false;
  protected boolean szoglet = false;
  protected boolean szabad = false;
  protected boolean kirugas = false;

  /**/
  java.util.Random random = new java.util.Random();

  @Override
  public void preInfo() {

    super.preInfo();
    latomAFocit = false;
    latomASzelet = false;
    latomASajatKaput = false;
    latomAMasikKaput = false;

  }

  protected void jatekban() {

    bedobas = false;
    szoglet = false;
    szabad = false;
    kirugas = false;

  }

  @Override
  public void postInfo() {

    if (bedobas) {
      bedobashoz();
    } else if (szabad) {
      szabadhoz();
    } else if (szoglet) {
      szoglethez();
    } else if (kirugas) {
      kirugashoz();
    } else if (kozepkezdes) {
      kozepkezdes();
    } else {
      jatekbanVezerles();
    }
  }

  protected void jatekbanVezerles() {

    if (latomASzelet && distanceSzele < 7.5) {
      palyanMaradni();
    } else if (latomAFocit) {

      if (distanceFoci < 0.9) {
        rugdEsFuss();
      } else if (distanceFoci < 35.0) {
        egyuttElaJatekkal(100);
      } else {
        egyuttElaJatekkal(80);
      }
    } else {
      teblabol();
    }

  }

  @Override
  protected void rugdEsFuss() {

    if (latomASajatKaput) {
      logger.info("LATOM A SAJAT KAPUT "
              + getPlayer().getNumber()
              + " tavolsaga = " + distanceFoci
              + " iranya = " + directionFoci);
      becsuszas();
    } else if (latomAMasikKaput) {
      logger.info("LATOM A MASIK KAPUT "
              + getPlayer().getNumber()
              + " tavolsaga = " + distanceFoci
              + " iranya = " + directionFoci);
      getPlayer().kick(100, directionMasikKapu);
    } else {
      logger.info("EGYIK KAPUT SEM LATOM "
              + getPlayer().getNumber()
              + " tavolsaga = " + distanceFoci
              + " iranya = " + directionFoci);

      getPlayer().kick(70, 0);
    }

    getPlayer().turnNeck(dirChangeFoci);
    getPlayer().dash(100);
  }

  protected void becsuszas() {

    getPlayer().kick(85, directionFoci + 90);
    getPlayer().turn(180);
    getPlayer().dash(40);

  }

  protected void egyuttElaJatekkal(int seb) {

    getPlayer().turnNeck(dirChangeFoci + random.nextInt(101 - seb) - (101 - seb) / 2);
    getPlayer().turn(directionFoci + dirChangeFoci);
    getPlayer().dash(seb);
  }

  ...
  // a Délibáb FC-től ugyanaz a /* 4-3-3 */ felállás. 
  ...

  @Override
  public void infoSeeBall(double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomAFocit = true;
    distanceFoci = distance;
    directionFoci = direction;
    dirChangeFoci = distChange;
    dirChangeFoci = dirChange;
    this.bodyFacingDirection = bodyFacingDirection;
    this.headFacingDirection = headFacingDirection;

  }

  protected void latomASzelet(double distance, double direction, double distChange, double dirChange) {

    latomASzelet = true;
    distanceSzele = distance;
    directionSzele = direction;
    dirChangeSzele = distChange;
    dirChangeSzele = dirChange;

  }

  @Override
  public void infoSeeFlagRight(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomASzelet(distance, direction, distChange, dirChange);

  }

  @Override
  public void infoSeeFlagLeft(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomASzelet(distance, direction, distChange, dirChange);

  }

  @Override
  public void infoSeeFlagOwn(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomASzelet(distance, direction, distChange, dirChange);

  }

  @Override
  public void infoSeeFlagOther(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomASzelet(distance, direction, distChange, dirChange);

  }

  protected void palyanMaradni() {

    getPlayer().turn(180);
    getPlayer().turnNeck(30);
    getPlayer().dash(40);

  }

  protected void teblabol() {

    getPlayer().turn(random.nextInt(15));
    getPlayer().turnNeck(random.nextInt(25));
    getPlayer().dash(20);

  }

  protected void sarkonFordul() {

    getPlayer().turn(180);
    getPlayer().dash(40);

  }

  protected void sajatKapuFele(atan.model.enums.Flag flag, double distance, double direction, double distChange,
          double dirChange, double bodyFacingDirection, double headFacingDirection) {

    latomASajatKaput = true;
    distanceSajatKapu = distance;
    directionSajatKapu = direction;
    dirChangeSajatKapu = distChange;
    dirChangeSajatKapu = dirChange;
  }

  protected void masikKapuFele(atan.model.enums.Flag flag, double distance, double direction, double distChange,
          double dirChange, double bodyFacingDirection, double headFacingDirection) {

    latomAMasikKaput = true;
    distanceMasikKapu = distance;
    directionMasikKapu = direction;
    dirChangeMasikKapu = distChange;
    dirChangeMasikKapu = dirChange;
  }

  @Override
  public void infoSeeFlagPenaltyOwn(atan.model.enums.Flag flag, double distance, double direction, double distChange,
          double dirChange, double bodyFacingDirection, double headFacingDirection) {

    sajatKapuFele(flag, distance, direction, distChange,
            dirChange, bodyFacingDirection, headFacingDirection);
  }

  @Override
  public void infoSeeFlagPenaltyOther(atan.model.enums.Flag flag, double distance, double direction, double distChange,
          double dirChange, double bodyFacingDirection, double headFacingDirection) {

    masikKapuFele(flag, distance, direction, distChange,
            dirChange, bodyFacingDirection, headFacingDirection);
  }

  @Override
  public void infoSeeFlagGoalOwn(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    sajatKapuFele(flag, distance, direction, distChange,
            dirChange, bodyFacingDirection, headFacingDirection);
  }

  @Override
  public void infoSeeFlagGoalOther(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    masikKapuFele(flag, distance, direction, distChange,
            dirChange, bodyFacingDirection, headFacingDirection);
  }

  @Override
  public String getType() {
    return "Jatekosbol";
  }

  @Override
  public void infoHearPlayMode(atan.model.enums.PlayMode playMode) {

    super.infoHearPlayMode(playMode);

    switch (playMode) {
      case CORNER_KICK_OWN:
        szoglet = true;
        break;
      case GOAL_KICK_OWN:
        kirugas = true;
        break;
      case FREE_KICK_OWN:
      case FREE_KICK_FAULT_OWN:
        szabad = true;
        break;
      case KICK_IN_OWN:
        bedobas = true;
        break;
      case PLAY_ON:
        jatekban();
        break;
      case BEFORE_KICK_OFF:
      case GOAL_L:
      case GOAL_R:
      case GOAL_OWN:
      case GOAL_OTHER:
        kozepkezdes = true;
        break;
    }

  }

  protected void bedobashoz() {

    if (latomAFocit) {

      if (distanceFoci < 0.9) {
        bedobas();
      } else {
        getPlayer().turn(directionFoci);
        getPlayer().dash(100);
      }

    } else {

      getPlayer().turnNeck(25);
    }
  }

  protected void szoglethez() {

    if (latomAFocit) {

      if (distanceFoci < 0.9) {
        szoglet();
      } else {
        getPlayer().turn(directionFoci);
        getPlayer().dash(100);
      }

    } else {
      getPlayer().turnNeck(25);
    }
  }

  protected void szabadhoz() {

    if (latomAFocit) {

      if (distanceFoci < 0.9) {
        szabad();
      } else {
        getPlayer().turn(directionFoci);
        getPlayer().dash(100);
      }

    } else {
      getPlayer().turnNeck(25);
    }
  }

  protected void kirugashoz() {

    if (latomAFocit) {

      if (distanceFoci < 0.9) {
        kirugas();
      } else {
        getPlayer().turn(directionFoci);
        getPlayer().dash(100);
      }

    } else {
      getPlayer().turnNeck(25);
    }
  }

  protected void bedobas() {

    double bedobasSzoge = -180.0 + directionFoci;
    if (directionFoci < 0.0) {
      bedobasSzoge = 180.0 + directionFoci;
    }

    logger.info("BEDOBAS "
            + getPlayer().getNumber()
            + " foci tavolsaga = " + distanceFoci
            + " iranya = " + directionFoci
            + " bedobas iranya = " + bedobasSzoge);

    getPlayer().kick(45, bedobasSzoge);
    getPlayer().turn(bedobasSzoge);

  }

  protected void szoglet() {

    getPlayer().kick(100, bodyFacingDirection + 90);
    getPlayer().turn(bodyFacingDirection + 90);

  }

  protected void szabad() {

    if (latomASajatKaput) {
      becsuszas();
    } else if (latomAMasikKaput) {
      getPlayer().kick(100, directionMasikKapu);
    } else {
      getPlayer().kick(70, 0);
    }
  }

  protected void kirugas() {

    if (latomASajatKaput) {
      becsuszas();
    } else {
      getPlayer().kick(100, headFacingDirection);
    }
  }
}
                  

A Tamado osztály

A Délibáb FC ezt az osztályt nem tartalmazta, így összehasonlítani az ősével tudjuk. A ciklus előfeldolgozása egyáltalán nem változik, s bár az utófeldolgozásban is az ős megfelelő metódusát hívjuk, de polimorf módon az az hívta jatekbanVezerles felüldefiniáljuk.

package hu.fersml.bsodfc;

public class Tamado extends hu.fersml.bsodfc.Jatekos {

  private static org.apache.log4j.Logger logger =
          org.apache.log4j.Logger.getLogger(Tamado.class);

  @Override
  public void preInfo() {
    super.preInfo();
  }

  @Override
  public void postInfo() {
    super.postInfo();
  }

  @Override
  protected void jatekbanVezerles() {

    if (latomASzelet && distanceSzele < 7.5) {
      palyanMaradni();
    } else if (latomAFocit) {

      if (distanceFoci < 0.9) {
        logger.info("KOZEL A FOCI "
                + getPlayer().getNumber()
                + " tavolsaga = " + distanceFoci
                + " iranya = " + directionFoci);
        rugdEsFuss();
      } else if (distanceFoci < 35.0) {
        egyuttElaJatekkal(100);
      } else if (distanceFoci < 45.0) {
        egyuttElaJatekkal(30);
      } else {
        egyuttElaJatekkal(10);
      }
    } else {
      teblabol();
    }

  }
}
                  

A Fedezet osztály

A fedezet mozgásánál arra törekszünk, hogy a két tizenhatos között maradjanak, ehhez bevezetjük az alábbi két csokor változót, amelyeket a szimpla megközelítés szerint a preInfo metódusban nullázunk, majd a szóban forgó ciklus alatt valamelyik érző metódusban állítunk vagy nem állítunk, majd a postInfo módszerben megtesszük amit kell, ha állítottunk.

package hu.fersml.bsodfc;

public class Fedezet extends hu.fersml.bsodfc.Jatekos {
  /**/

  protected boolean latomASajat16ost;
  protected double distanceSajat16os;
  protected double directionSajat16os;
  protected double distChangeSajat16os;
  protected double dirChangeSajat16os;
  /**/
  protected boolean latomAVendeg16ost;
  protected double distanceVendeg16os;
  protected double directionVendeg16os;
  protected double distChangeVendeg16os;
  protected double dirChangeVendeg16os;

  @Override
  public void preInfo() {
    super.preInfo();
    latomAVendeg16ost = false;
    latomASajat16ost = false;
  }

  @Override
  public void postInfo() {

    if (kozepkezdes) {
      kozepkezdes();
    } else {

      jatekbanVezerles();
    }
  }

  @Override
  protected void jatekbanVezerles() {

    if (latomAVendeg16ost && distanceVendeg16os < 2.0) {
      sarkonFordul();
    } else if (latomASajat16ost && distanceSajat16os < 2.0) {
      sarkonFordul();
    } else if (latomASzelet && distanceSzele < 7.5) {
      palyanMaradni();
    } else if (latomAFocit) {
      if (distanceFoci < 0.7) {
        rugdEsFuss();
      } else if (distanceFoci < 1.4) {
        becsuszas();
      } else if (distanceFoci < 15.0) {
        egyuttElaJatekkal(100);
      } else if (distanceFoci < 25.0) {
        egyuttElaJatekkal(80);
      } else if (distanceFoci < 30.0) {
        egyuttElaJatekkal(60);
      } else if (distanceFoci < 35.0) {
        egyuttElaJatekkal(40);
      } else {
        egyuttElaJatekkal(5);
      }
    } else {
      teblabol();
    }
  }

  @Override
  public void infoSeeFlagPenaltyOwn(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomASajat16ost = true;
    distanceSajat16os = distance;
    directionSajat16os = direction;
    dirChangeSajat16os = distChange;
    dirChangeSajat16os = dirChange;

  }

  @Override
  public void infoSeeFlagPenaltyOther(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomAVendeg16ost = true;
    distanceVendeg16os = distance;
    directionVendeg16os = direction;
    dirChangeVendeg16os = distChange;
    dirChangeVendeg16os = dirChange;

  }

  /* 4-3-3 */
  @Override
  protected void kozepkezdes() {

    switch (getPlayer().getNumber()) {

      // Kp.

      case 5:
        getPlayer().move(-18, -16);
        break;

      case 6:
        getPlayer().move(-17, 2);
        break;

      case 7:
        getPlayer().move(-18, 17);
        break;

    }
  }
}
                  

Észrevehetjük, hogy a fedezet már magát helyezi el a pályán a középkezdéshez (ez egy átmeneti helyzet és ennek megfelelően nem a defenzív programozási taktika, mert ha az ágens száma nem a várt 5, 6, vagy 7, akkor olyan hibát kapunk, amit nem könnyen tudunk kideríteni, az lehet árulkodó, hogy a szóbanforgó játékos ágens nem áll fel a középkezdéshez.)

A Vedo osztály

A védőknél a fedezethez hasonlóan járunk el, de itt arra figyelünk, hogy az ágens a saját térfelén maradjon.

package hu.fersml.bsodfc;

public class Vedo extends hu.fersml.bsodfc.Jatekos {

  /**/
  protected boolean latomASajatGolvonalat;
  protected double distanceSajatGolvonal;
  protected double directionSajatGolvonal;
  protected double distChangeSajatGolvonal;
  protected double dirChangeSajatGolvonal;
  /**/
  protected boolean latomAKozepet;
  protected double distanceKozep;
  protected double directionKozep;
  protected double distChangeKozep;
  protected double dirChangeKozep;
  /**/
  protected boolean mozoghat;

  @Override
  public void preInfo() {
    super.preInfo();
    latomAKozepet = false;
    latomASajatGolvonalat = false;
    mozoghat = false;
  }

  @Override
  public void postInfo() {

    if (latomAFocit && distanceFoci < 4.0) {
      mozoghat = true;
    }

    if (kirugas) {
      kirugashoz();
    } else if (kozepkezdes) {
      kozepkezdes();
    } else {

      jatekbanVezerles();
    }
  }

  protected void jatekbanVezerles() {

    if (latomAKozepet && distanceKozep < 10.0) {
      sarkonFordul();
    } else if (latomASajatGolvonalat && distanceSajatGolvonal < 0.5) {
      sarkonFordul();
    } else if (latomASzelet && distanceSzele < 5.5) {
      palyanMaradni();
    } else if (latomAFocit) {
      if (distanceFoci < 0.7) {
        rugdEsFuss();
      } else if (distanceFoci < 1.4) {
        becsuszas();
      } else if (distanceFoci < 15.0) {
        egyuttElaJatekkal(100);
      } else if (distanceFoci < 25.0) {
        egyuttElaJatekkal(80);
      } else if (distanceFoci < 30.0) {
        egyuttElaJatekkal(70);
      } else if (distanceFoci < 35.0) {
        egyuttElaJatekkal(40);
      } else {
        egyuttElaJatekkal(5);
      }
    } else {
      teblabol();
    }
  }

  @Override
  public void infoSeeFlagGoalOwn(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomASajatGolvonalat = true;
    distanceSajatGolvonal = distance;
    directionSajatGolvonal = direction;
    dirChangeSajatGolvonal = distChange;
    dirChangeSajatGolvonal = dirChange;
    if (distance > 4.0) {
      mozoghat = true;
    }

  }

  @Override
  public void infoSeeFlagCenter(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomAKozepet = true;
    distanceKozep = distance;
    directionKozep = direction;
    dirChangeKozep = distChange;
    dirChangeKozep = dirChange;
    if (distance > 16.0) {
      mozoghat = true;
    }

  }

  /* 4-3-3 */
  @Override
  protected void kozepkezdes() {

    switch (getPlayer().getNumber()) {

      // Vedo

      case 8:
        getPlayer().move(-35, -19);
        break;

      case 9:
        getPlayer().move(-33, -10);
        break;

      case 10:
        getPlayer().move(-34, 12);
        break;

      case 11:
        getPlayer().move(-35, 24);
        break;

    }

  }

  @Override
  protected void egyuttElaJatekkal(int seb) {

    if (mozoghat) {
      super.egyuttElaJatekkal(seb);
    }

  }

  @Override
  protected void rugdEsFuss() {

    if (mozoghat) {
      super.rugdEsFuss();
    }
  }
}
                  

A Kapus osztály

Az osztály a Délibáb FC ugyanilyen nevű osztályához képest nem változott, ezért itt sem közöljük újra.

Merre az arra?

Bosszantóan sok öngólt tud „szerezni” a csapat, ez következménye a fuss és rúgd megközelítésnek, amit eddig a kezdetektől kezdve folyamatosan használtunk. Kicsit finomítunk ezen a modellen, az alábbi kód szerint, ahol egyszerűen arra figyelünk, hogy a játékos látja-e a saját vagy a másik kaput, illetve naplózzuk is ezt.

@Override
  protected void rugdEsFuss() {

    if (latomASajatKaput) {
      logger.info("LATOM A SAJAT KAPUT "
              + getPlayer().getNumber()
              + " tavolsaga = " + distanceFoci
              + " iranya = " + directionFoci);
      becsuszas();
    } else if (latomAMasikKaput) {
      logger.info("LATOM A MASIK KAPUT "
              + getPlayer().getNumber()
              + " tavolsaga = " + distanceFoci
              + " iranya = " + directionFoci);
      getPlayer().kick(100, directionMasikKapu);
    } else {
      logger.info("EGYIK KAPUT SEM LATOM "
              + getPlayer().getNumber()
              + " tavolsaga = " + distanceFoci
              + " iranya = " + directionFoci);

      getPlayer().kick(70, 0);
    }

    getPlayer().turnNeck(dirChangeFoci);
    getPlayer().dash(100);
  }
                    

Tehát naplóztunk is, (itt nem látható, de rögtön a középkezdés után) két villámgyors gól krónikája volt a következő logolás.

Kekhalal FC

0 [Thread-2] INFO hu.fersml.bsodfc.Tamado  - KOZEL A FOCI 3 tavolsaga = 0.4 iranya = -1.0
1 [Thread-2] INFO hu.fersml.bsodfc.Jatekos  - LATOM A MASIK KAPUT 3 tavolsaga = 0.4 iranya = -1.0

KekhalalB

0 [Thread-2] INFO hu.fersml.bsodfc.Tamado  - KOZEL A FOCI 3 tavolsaga = 0.7 iranya = 0.0
2 [Thread-2] INFO hu.fersml.bsodfc.Jatekos  - LATOM A MASIK KAPUT 3 tavolsaga = 0.7 iranya = 0.0
23868 [Thread-2] INFO hu.fersml.bsodfc.Tamado  - KOZEL A FOCI 3 tavolsaga = 0.4 iranya = 0.0
23868 [Thread-2] INFO hu.fersml.bsodfc.Jatekos  - LATOM A MASIK KAPUT 3 tavolsaga = 0.4 iranya = 0.0
                      

Az első két sort a Kékhalál FC-t indító ablakból, a másik négyet az ugyanazt a programot futtató, de KékhalálB néven bejelentkező parancsablakból vágtuk ki.

Szöglet, bedobás, szabad, kirúgás

A szóban forgó rögzített (saját, azaz amikor a mi csapatunknak kell elvégeznie) játékhelyzetek lereagálását a Jatekos osztályba építettük be és ott részleteztük. A saját bedobást, szögletet, szabadot és a kirúgást most teljesen egy kaptafán kezeltük. Ezek közül a bedobás a működőképes az előforduló esetek egy részében. Fő feladat a másik három elem kezelése, hiszen vegyük például a szögletet, ahol a csapataink jelenlegi absztrakciós szintjén legalábbis be kellene rúgni a kapu elé. A jelenlegi kód a bodyFacingDirection változót használja

                    
  protected void szoglet() {

    getPlayer().kick(100, bodyFacingDirection + 90);
    getPlayer().turn(bodyFacingDirection + 90);

  }
                    

mely eleve csak akkor töltődik fel, ha az ágens egy másik játékost lát, most nekünk pedig ezt az infoSeeBall tölti fel, ahol tehát a protokoll szerint http://sourceforge.net/projects/sserver/files/rcssmanual/ ez az érték nem is fog rendelkezésre állni.

@Override
  public void infoSeeBall(double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomAFocit = true;
    distanceFoci = distance;
    directionFoci = direction;
    dirChangeFoci = distChange;
    dirChangeFoci = dirChange;
    this.bodyFacingDirection = bodyFacingDirection;
    this.headFacingDirection = headFacingDirection;

  }
                    

6.11. ábra - A 4-es mezt viselő csatár fut elvégezni a szögletet.

A 4-es mezt viselő csatár fut elvégezni a szögletet.


A szöglet megvalósítását a Marvellous Magyars FC csapatnál fogjuk javítani, ahol majd azt egy pompás szögletből szerzett góllal demonstáljuk.

HELIOS_base - Aranycsapat FC, 9:0

 

„Alighogy átszállt a határon a győzelem, az a hat-három s fáradtan a nagy drukkolástól ledőlnék, egyszer csak rámszól a rádió és arra bíztat, hogy verset írjak... Hát írok is.”

 
 --Zelk Zoltán Rímes üdvözlő távirat

Az Aranycsapat FC egy olyan váz kialakítását adja hozzá a korábbi Kékhalál FC csapatához, amelyben minden játékost külön osztály absztrahál. Az osztályok nevét, a pályán betöltött pozíciójuk alapján, az évszázad mérkőzésén kezdők nevéből származtattuk, ezzel is tisztelegve az Aranycsapat legendái előtt. Objektum orientált szempontból ezt úgy tekintsük, hogy össze tudunk majd állítani olyan védelmet, amelynek ágenseit például mind a Buzánszky osztályból származtatjuk.

6.12. ábra - A w63 csomag osztályainak szervezése.

A w63 csomag osztályainak szervezése.


Az Aranycsapat FC osztályai

A fejlesztendő csapatot absztraháló osztályokat a hu.fersml.aranyfc csomagba helyezzük. A projekt (AranycsapatFCa043-0.0.1) a jegyzet mellékletéből vagy a kurzus blogjának kapcsolódó posztjáról is letölthető Maven 3 projektként és megtalálható a kurzus svn tárolójában is.

A AranycsapatFC osztály

Ennek a csapatnak minden ágense külön osztályból példányosodik.

[Megjegyzés]

Nagy a kísértés, hogy a játékos ágensek a csapat programján belül, ám az RCSS szervert megkerülve kommunikálnak egymással. Ezt kerüljük el! A robotfociban nem megengedett. Ennek kapcsán lásd majd még a későbbi Klón FC feladatot. Most azon gondolkodj el, hogyan tudod azt megvalósítani, hogy a csapat tagjai mind külön programként csatlakozzanak, külön JVM-ekben.

package hu.fersml.aranyfc;

public class AranycsapatFC extends atan.model.AbstractTeam {

  public AranycsapatFC(String team, int port, String host) {

    super(team, port, host);

  }

  @Override
  public atan.model.ControllerPlayer getNewController(int number) {

    hu.fersml.aranyfc.Jatekos jatekos = null;

    switch (number) {

      case 0:
        jatekos = new hu.fersml.aranyfc.w63.Grosics();
        break;

      case 10:
        jatekos = new hu.fersml.aranyfc.w63.Buzanszky();
        break;

      case 9:
        jatekos = new hu.fersml.aranyfc.w63.Lorant();
        break;

      case 8:
        jatekos = new hu.fersml.aranyfc.w63.Lantos();
        break;

      case 7:
        jatekos = new hu.fersml.aranyfc.w63.Bozsik();
        break;

      case 6:
        jatekos = new hu.fersml.aranyfc.w63.Zakarias();
        break;

      case 5:
        jatekos = new hu.fersml.aranyfc.w63.Budai();
        break;

      case 4:
        jatekos = new hu.fersml.aranyfc.w63.Kocsis();
        break;

      case 3:
        jatekos = new hu.fersml.aranyfc.w63.Hidegkuti();
        break;

      case 2:
        jatekos = new hu.fersml.aranyfc.w63.Puskas();
        break;

      case 1:
        jatekos = new hu.fersml.aranyfc.w63.Czibor();
        break;

    }

    return jatekos;
  }

  public static void main(String[] args) {

    org.apache.log4j.BasicConfigurator.configure();
    org.apache.log4j.Logger.getLogger("atan.model").setLevel(org.apache.log4j.Level.OFF);

    if (args.length == 1) {
      new AranycsapatFC(args[0], 6000, "localhost").connectAll();
    } else {
      new AranycsapatFC("AranycsapatFC", 6000, "localhost").connectAll();
    }
  }
}
                  

A JatekosAdapter osztály

Az osztály a Kékhalál FC ugyanilyen nevű osztályához képest nem változott.

A Jatekos osztály

Az osztály a Kékhalál FC ugyanilyen nevű osztályához képest nem változott.

A Kapus osztály

Az osztály a Kékhalál FC ugyanilyen nevű osztályához képest nem változott.

A Vedo osztály

Az osztály a Kékhalál FC ugyanilyen nevű osztályához képest nem változott.

A Fedezet osztály

Az osztály a Kékhalál FC ugyanilyen nevű osztályához képest nem változott.

A Tamado osztály

Az osztály a Kékhalál FC ugyanilyen nevű osztályához képest nem változott.

A w63 alcsomag osztályai

A w63 alcsomag osztályainak kódja szinte megegyezik, különbség a mezszámokban és az ősosztályban van, ezért csak egyet-egyet közlünk.

A Buzanszky osztály

Az osztály példányai egyelőre a saját középkezdéskori pozíciójukat tudják.

package hu.fersml.aranyfc.w63;

public class Buzanszky extends hu.fersml.aranyfc.Vedo {

  public static final int MEZSZAM = 11;
  private static org.apache.log4j.Logger logger =
          org.apache.log4j.Logger.getLogger(Buzanszky.class);

  @Override
  protected void kozepkezdes() {

    logger.info("KOZEPKEZDES "
            + getPlayer().getNumber()
            + " tavolsaga = " + distanceFoci
            + " iranya = " + directionFoci);

    switch (getPlayer().getNumber()) {

      case MEZSZAM:
        getPlayer().move(-30, 23);
        break;

    }
  }
}
                      

A Klón FC feladat mutatja majd, milyen problémákat okoz a behuzalozott mezszám, itt például a MEZSZAM = 11.

A Zakarias osztály

package hu.fersml.aranyfc.w63;

public class Zakarias extends hu.fersml.aranyfc.Fedezet {

  public static final int MEZSZAM = 7;

  @Override
  protected void kozepkezdes() {

    switch (getPlayer().getNumber()) {

      case MEZSZAM:
        getPlayer().move(-22, -12);
        break;

    }
  }
}
                      

A Puskas osztály

package hu.fersml.aranyfc.w63;

public class Puskas extends hu.fersml.aranyfc.Tamado {

  public static final int MEZSZAM = 3;

  @Override
  protected void kozepkezdes() {

    switch (getPlayer().getNumber()) {

      case MEZSZAM:
        getPlayer().move(-7, -10);
        break;

    }
  }

  @Override
  public String getType() {
    return "Puskas";
  }
}
                      

7. fejezet - Atan 1.0 alapú csapatok

Ebben a fejezetben a korábbiban kifejlesztett Aranycsapat FC

  • Atan 1.0-ra alapozott változatát ismered meg,

  • amelyet tovább is fejlesztünk Marvellous Magyars FC[2] néven.

  • A Marvellous Magyars FC együttesben megjelenik az edző, s a csapat megtanul okosabban bedobni.

  • A Mighty Magyars FC pedig már tudatosan próbál passzolni, e mellett játékosai igyekeznek meghatározni helyzetüket a pályán.

  • A Golden Team FC 0.0.2 változatától módszertant váltunk, s megalapozzuk a már majd erre az új csapatra épülő további, például a Magical Magyars FC, vagy a Magnificent Magyars FC csapatainkat.

Immár hagyományosan a következő, adott csapatokhoz kötődő fejezetek címében tehát megintcsak a HELIOS_base csapattal vívott mérkőzések eredménye is megjelenik. E névadó mérkőzéseket most is elmentettük, a következők voltak:

„The 600 series had rubber skin. We spotted them easy. But these are new. They look human. Sweat, bad breath, everything. Very hard to spot.”

REESE [TERMINATOR] T E R M I N A T O R

HELIOS_base - Aranycsapat FC (Atan 1.0), 32:0

 

„Kis pénz, kis foci, nagy pénz, nagy foci.”

 
 --Puskás Ferenc 1952, Svájc - Magyarország (2:4)[3]

Ebben a projektben nincs a csapat programozását illetően különbség az előző, az Atan 0.4.3 alapú ugyanezt az Aranycsapat FC nevet viselő csapathoz képest. Ami változott, az az Atan 1.0-beli bővebb interfészek és absztrakt osztályok megvalósítása miatt változott. Ennek megfelelően most már csak azokat a kódokat közöljük, amelyekben eszközöltük a szóban forgó módosításokat. Természetesen a projekt teljes kódja szerepel a jelen jegyzet mellékletében.

Az Aranycsapat FC osztályai

Az AranycsapatFC és a JatekosAdapter osztályokat kellett módosítanunk, az alábbiak szerint. Az AranycsapatFC-ban a atan.model.AbstractTeam osztály új, egy negyedik formális paraméterrel. Nevezetesen a „van vagy nincs edző”-t jelző logikaival bővült konstruktorát kell hívnunk.

Mielőtt azonban ezeket részletezzük, vessünk egy pillantást arra, hogyan menedzseljük a továbbiakban az Aranycsapat FC projektet.

A pom.xml állomány

Eddig tipikusan vagy parancssorból, vagy a NetBeans integrált fejlesztői környezet felhasználásával dolgoztunk. Innen viszont IDE függetlenek leszünk „le kell jönni a szerről”, áttérünk a Maven használatára. Ez praktikus okokból is rögtön gyümölcsöző, hiszen eddig nagyon fárasztó volt, figyelni hol van az Atan 0.4.3 vagy a log4j jar állománya a fájlrendszerben. Most azzal kezdjük, hogy az Atan 1.0 jar állományát elkészítjük és még az ezt végző parancssorban installáljuk is a lokális tárolónkba, így az Atan 1.0 alapú Aranycsapat FC-nek már megadhatjuk majd egyszerűen, mint egy függőséget. Természetesen, ha ezt korábban már megtetted, akkor azonnal elkezdhetsz az Aranycsapat FC-vel dolgozni.

A projektet annak gyökerében, tehát a pom.xml állománnyal egy szinten, a AranycsapatFC-0.0.1 könyvtárban kiadott

[norbert@matrica AranycsapatFC-0.0.1]$ mvn3 package
                    

paranccsal tudod felépíteni, ennek hatására létrejön a tárgy, a AranycsapatFC-0.0.1.jar állomány az innen nyíló, most létrejött target könyvtárban. Csapatod futtatásához, a korábban elkészített atan-1.0.0.jar-t és letöltött log4j-1.2.16.jar-t hivatkozva ezt a parancsot kell kiadnod:

[norbert@matrica AranycsapatFC-0.0.1]$ java -cp target/AranycsapatFC-0.0.1.jar:../atan-1.0.0/target/atan-1.0.0.jar:/home/norbert/Downloads/apache-log4j-1.2.16/log4j-1.2.16.jar hu.fersml.aranyfc.AranycsapatFC
                    

a következő csapatnál ez az indítás leegyszerűsödik, sőt az ott leírt indítási módszer már itt is használható lenne, de kezdőknek eleinte nem árt kézzel összerakni a parancssort.

A AranycsapatFC osztály

Szokás szerint csak a módosított részeket közöljük.

package hu.fersml.aranyfc;

public class AranycsapatFC extends atan.model.AbstractTeam {

  public AranycsapatFC(String team, int port, String host) {

    super(team, port, host, false);

  }

  @Override
  public atan.model.ControllerCoach getNewControllerCoach() {
    return null;
  }

  @Override
  public atan.model.ControllerPlayer getNewControllerPlayer(int number) {

    hu.fersml.aranyfc.Jatekos jatekos = null;

    switch (number) {

      ... 
      // Ugyanúgy példányosítunk az Aranycsapat kezdőiből, mint az előző csapatnál
      ...

    }

    return jatekos;
  }

  public static void main(String[] args) {

    org.apache.log4j.BasicConfigurator.configure();
    org.apache.log4j.Logger.getLogger("atan.model").setLevel(org.apache.log4j.Level.OFF);

    if (args.length == 1) {
      new AranycsapatFC(args[0], 6000, "localhost").connectAll();
    } else {
      new AranycsapatFC("AranycsapatFC", 6000, "localhost").connectAll();
    }
  }
}
                  

A JatekosAdapter osztály

A JatekosAdapter osztályban implementálnunk kell az Atan 1.0-ban megjelent új interfész módszereket. Ezek a következők: infoCPTOwn, infoCPTOther, infoPlayerType, infoPlayerParam, infoServerParam. Teljesen ugyanúgy járunk el, mint korábban: az atan/model/ControllerPlayer.java forrásból kimásoljuk ezeket a metódusneveket, s egy üres testet írunk a nevük után a pontosvessző helyett, azaz megadunk egy üres testes implementációt. Ezért megtehetjük, hogy az osztály kódját nem is közöljük.

Programozási feladatok

Ezeknél a feladatoknál már a jegyzet csapatainak Maven projektjeivel dolgozz!

Klón FC

Készíts egy olyan Atan 1.0 alapú programot, amelyben a védelmet a Buzánszky osztálybeli objektumok alkotják, a középpályán legyenek Zakariás objektumok, a támadó alakzatban pedig a Puskás típusbeli ágensek sorakozzanak fel.

Ha még nem tetted meg, most telepítsd az atan-1.0.0 artifact tárgyat a lokális Maven repódba.

  1. Természetesen a példányosítás az első lépés:

    case 10:
            jatekos = new hu.fersml.magyarfc.w63.Buzanszky();
            break;
    
          case 9:
            jatekos = new hu.fersml.magyarfc.w63.Buzanszky();
            break;
    
          case 8:
            jatekos = new hu.fersml.magyarfc.w63.Buzanszky();
            break;
                                    
  2. Ám ez még nem elegendő, hiszen a használt osztályokban módosítanod kell a „mezszám” kezelését, mert különben a játékosok nem foglalnak el megfelelő pozíciót a középkezdésnél.

Más porton...

Számos olyan eset lehet, hogy az RCSS szervert nem az alapértelmezett porton futtatod, vagy eleve más gépen fut, illetve csak ugyanazt a programot, más csapatnéven akarod egymással játszatni. Ennek megfelelően módosítsd úgy a csapatodat, hogy

  • ha nem kap parancssorargumentumokat, akkor előre huzalozott (például 6000) portszámmal és csapatnévvel a localhost-hoz csatlakozzon,

  • ha egy parancssorargumentumot kap, akkor azt a csapat neveként értelmezze,

  • ha két parancssorargumentumot kap, akkor azt portszám és csapatnévként értelmezze,

  • ha három parancssorargumentumot kap, akkor azt hosztnév, portszám és csapatnévként értelmezze

előbbi kettőt tudja az AranycsapatFC, az utóbbi két esetet programozd be kivételkezeléssel, azaz például a localhost 6000a team (portszámban elgépelt) argumentum listával is működjön a foci.

  • Megoldásod vesd össze az enyémmel, amit a Marvellous Magyars FC csapat megfelelő forrásában találsz meg.

Egyedül nem megy...

Készíts egy saját Subversion tárolót, amelyben egy társaddal közösen tartjátok karban a továbbiakban a csapatotok forrásait.

Használhatod valamelyik egyetemi szervert, például a shrek.unideb.hu vagy a hallg.inf.unideb.hu gépet is hosztolni a verziókezelőt.

  1. A Subversion tároló elkészítése:

    nbatfai@hallg:~$ mkdir /home/nbatfai/demo
    nbatfai@hallg:~$ mkdir /home/nbatfai/demo/arany
    nbatfai@hallg:~$ svnadmin create /home/nbatfai/demo/arany
                                    
  2. Itt a csapatod, melyen eddig magányosan dolgoztál:

    nbatfai@hallg:~$ mkdir /home/nbatfai/eddigmagam
    nbatfai@hallg:~$ echo "Ez az Arany.java forrása." > /home/nbatfai/eddigmagam/Arany.java
    nbatfai@hallg:~$ more /home/nbatfai/eddigmagam/Arany.java
    Ez az Arany.java forrása.
                                    
  3. Most feltöltöd az első lépésben elkészített repóba:

    nbatfai@hallg:~$ svn import eddigmagam file:///home/nbatfai/demo/arany/eddigmagam -m "Initial hack"
    Adding eddigmagam/Arany.java
                                    
  4. Elindítod a szerver folyamatot (és természetesen hagyod is futni folyamatosan)

    nbatfai@hallg:~$ svnserve --daemon --listen-port=2011 --root=/home/nbatfai/demo/arany
                                    
  5. Az érdeklődők innentől már leránthatják munkapéldányaikat:

    nbatfai@hallg:~$ svn co svn://hallg.inf.unideb.hu:2011
    A hallg.inf.unideb.hu:2011/eddigmagam
    A hallg.inf.unideb.hu:2011/eddigmagam/Arany.java
    Checked out revision 1. 
                                    
  6. Állíts még be magadnak és fejlesztőtársadnak egy fejlesztői login/jelszó párost, amelyet a /home/nbatfai/demo/arany/conf/passwd állományban adj meg, mielőtt a /home/nbatfai/demo/arany/conf/svnserve.conf fájlban a password-db = passwd sort kiveszed kommentből.

  7. Aztán a fejlesztők módosítás után commitolhatják is munkájukat:

    svn commit -m "ezt azt csináltam" Arany.java
                                    

HELIOS_base - Marvellous Magyars FC, 29:0

 

„Játssz nyugodtan.”

 
 --Puskás Ferenc Puskás Hungary

A Marvellous Magyars FC állományai és osztályai

A pom.xml állomány

A pom.xml fájlt úgy bővítettük, hogy a maven-assembly-plugin segítségével olyan jar fájlt is készítünk, amelybe minden olyan más jar fájlt becsomagolunk, amelytől a mi tárgyunk, azaz most a MarvellousMagyarsFC-0.0.1.jar állomány függ. Ezzel nagyban leegyszerűsödik a csapatunk futtatása, ami mindössze ennyi lesz:

[norbert@matrica MarvellousMagyarsFC-0.0.1]$ java -jar target/site/MarvellousMagyarsFC-0.0.1-jar-with-dependencies.jar
                      

vegyük majd észre, hogy ennek a jar állománynak a mérete jóval nagyobb, mint az eddig megszokot néhányszor 10 kilobájt, majdnem két mega, hiszen minden további jar-t is tartalmaz, melyekre eddig a parancssorban hivatkoztunk például.

A pom.xml állomány bővítése tehát az alábbi módosításban merül ki.

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>project</descriptorRef>
			<descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <outputDirectory>${project.reporting.outputDirectory}</outputDirectory>
                    <archive>
                        <manifest>
                            <mainClass>hu.fersml.magyarfc.MarvellousMagyarsFC</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
                    

aminek megfelelően az eddigi, megszokott jar állományunk ott lesz a target könyvtárban, de a target/site mappában létrejön a szóban forgó MarvellousMagyarsFC-0.0.1-jar-with-dependencies.jar állomány.

A MarvellousMagyarsFC osztály

A korábbi, kivételkezeléses feladatot oldja meg a csapat MarvellousMagyarsFC osztálya, ez ugye már valódi feladat volt, mert már tipikusan nem csak úgy futtatjuk csapatunkat, hogy ugyanazt két néven, hanem sokszor más gépre (és portra) is akarunk e mellett csatlakozni, hogy összemérjük csapataink erejét a társainkéval. Ennek megfelelően az alábbi négy módon futtatható a Marvellous Magyars FC programja:

  • paraméter nélkül a "MarvellousFC" néven a 6000-es porton a localhostra csatlakozik;

  • egy paraméterrel indítva, csapatnévnek veszi azt;

  • két paraméterrel indítva portszám csapatnév sorrendben várja azokat;

  • hárommal a hosztnév portszám csapatnév sorrendet feltételezi.

A Marvellous Magyars FC további újdonsága, hogy a atan.model.AbstractTeam ős konstruktorának hívásában már valódi edzőt állít be.

package hu.fersml.magyarfc;

public class MarvellousMagyarsFC extends atan.model.AbstractTeam {

    private static org.apache.log4j.Logger logger =
            org.apache.log4j.Logger.getLogger(MarvellousMagyarsFC.class);

    public MarvellousMagyarsFC(String team, int port, String host) {

        super(team, port, host, true);
        logger.info(host + ":" + port + " " + team);

    }

    @Override
    public atan.model.ControllerCoach getNewControllerCoach() {
        return new hu.fersml.magyarfc.w63.Sebes();
    }

    @Override
    public atan.model.ControllerPlayer getNewControllerPlayer(int number) {

        hu.fersml.magyarfc.Jatekos jatekos = null;

        switch (number) {
        
          ... 
          // Ugyanúgy példányosítunk az Aranycsapat kezdőiből, 
          // mint az előző csapatnál
          ...
        
        }

        return (atan.model.ControllerPlayer) jatekos;
    }

    public static void main(String[] args) {

        org.apache.log4j.BasicConfigurator.configure();
        org.apache.log4j.Logger.getLogger("atan.model").setLevel(org.apache.log4j.Level.OFF);

        if (args.length == 3) {
            // host port team      
            int port = 6000;
            try {
                port = Integer.parseInt(args[1]);
            } catch (java.lang.NumberFormatException e) {
                port = 6009;
            }
            new MarvellousMagyarsFC(args[2], port, args[0]).connectAll();

        } else if (args.length == 2) {
            // port team
            int port = 6000;
            try {
                port = Integer.parseInt(args[0]);
            } catch (java.lang.NumberFormatException e) {
                port = 6009;
            }
            new MarvellousMagyarsFC(args[1], port, "localhost").connectAll();

        } else if (args.length == 1) {
            new MarvellousMagyarsFC(args[0], 6000, "localhost").connectAll();

        } else {
            new MarvellousMagyarsFC("MarvellousFC", 6000, "localhost").connectAll();

        }
    }
}
                    

A JatekosAdapter osztály

Az osztály az Aranycsapat FC ugyanilyen nevű osztályához képest nem változott.

A Jatekos osztály

A játékos osztályban javítottunk az eddig teljesen a bedobás mintájára megvalósított szöglet implementációján. A vázat meghagytuk, de amikor a szoglethez módszerében a játékos közel kerül a labdához és az eddigi kód szerint rúgna, most e helyett hívja a szogletnel függvényt

    protected void szoglethez() {

        if (latomAFocit) {

            if (distanceFoci < 0.9) {
                szogletnel();
            } else {
                getPlayer().turn(directionFoci);
                getPlayer().dash(100);
            }

        } else {
            getPlayer().turnNeck(25);
        }
    }
                    

amelyben addig nem rúgja el a labdát, amíg nem látja a másik (azaz az ellenfél) kapuját, mert eddig ugye ahogy odaért, rúgott, tipikusan rossz helyre. Most az az elképzelésünk, hogy odaér, a kapu felé fordul és csak eztán végzi el a szögletet

    protected void szogletnel() {

        logger.info("SZOGLETNEL "
                + getPlayer().getNumber()
                + " foci tavolsaga = " + distanceFoci
                + " iranya = " + directionFoci
                + " kapu tavolsaga = " + distanceMasikKapu
                + " kapu iranya = " + directionMasikKapu);

        if (latomAMasikKaput) {
            szoglet();
        } else {
            getPlayer().turn(50);
        }

    }
                    

a szoglet függvény hívásával, a kapu irányába:

    protected void szoglet() {

        getPlayer().kick(100, directionMasikKapu);

        logger.info("SZOGLET ELVEGEZVE "
                + getPlayer().getNumber()
                + " foci tavolsaga = " + distanceFoci
                + " iranya = " + directionFoci
                + " kapu tavolsaga = " + distanceMasikKapu
                + " kapu iranya = " + directionMasikKapu);

    }
                    

Szögletből szerzett pompás gól

Demonstáljuk az iménti kód működését! Finomabb lenne ha a rögzített szituációk kezelésére kijelölt csatárok nemcsak a labdát követnék, hanem az adott szituációnak megfelelően is működnének. Esetünkben tehát nemcsak a labdát figyelnék, hanem a szögletzászlót is. Ez egy lehetséges továbbfejlesztési iránya a Jatekos osztályban most kifejtett kódnak.

7.1. ábra - A játékos a labdához megy elvégezni a szögletet.

A játékos a labdához megy elvégezni a szögletet.

A szogletnel függvénynek megfelelően a játékos a kapu felé fordul.

7.2. ábra - A játékos a kapu felé fordul a szöglet elvégzése előtt.

A játékos a kapu felé fordul a szöglet elvégzése előtt.

Majd a szoglet függvénynek megfelelően a a kapu irányába lövi el a labdát, ami most egy szerencsés gólt is hoz éppen!

7.3. ábra - Szerencsés gól szögletből.

Szerencsés gól szögletből.

A Kapus osztály

Az osztály az Aranycsapat FC ugyanilyen nevű osztályához képest nem változott.

A Vedo osztály

Az osztály az Aranycsapat FC ugyanilyen nevű osztályához képest nem változott.

A Fedezet osztály

Az osztály az Aranycsapat FC ugyanilyen nevű osztályához képest nem változott.

A Tamado osztály

Az osztály az Aranycsapat FC ugyanilyen nevű osztályához képest nem változott.

A w63 alcsomag osztályai

A w63 alcsomag osztályai az Aranycsapat FC ugyanilyen nevű osztályához képest nem változtak.

Einstein gyorsuló liftjében

Az eddigi izometrikusan felépített példák valószínűleg már kialakították a gép mellett ülő és billentyűző olvasókban az ágens készítés, ágens programozás intuitív fogalmát. A csapat persze még mindig csetlik-botlik, de már akár élvezhető pillanatai is vannak a „játékának”. Amit eddig kidolgoztunk az egy szoftveres váz, ez volt a Magas szintű programozási nyelvek 2 labor támogatásának a fő célja. A ráépülő mesterséges intelligencia, robotika témájú tárgyak sikeres hallgatásában kamatoztathatod majd, amit eddig tanultál.

Mostanra tehát jól érezhetővé, érzékelhetővé vált, hogy csapatunk és játékosaink játékának, pontosabban annak hiányának oka, hogy a játékosok „nem tudnak magukról a pályán”, nem rendelkeznek sem a pálya, sem a játék semminemű belső reprezentációjával. Programozásuk még nincs olyan kifinomult, komplex vagy részletes, hogy egy ilyen reprezentáció nyomait felleljük benne. A következőkben már ebbe az iránya teszünk kezdőlépéseket, amit elvben a Bevezetés a robotikába, vagy a A mesterséges intelligencia alapjai folytathatsz majd.

HELIOS_base - Mighty Magyars FC, 36:0

 

„Here we pass the ball, you understand that? We're a unit, not a one-man show. The name on the front of the shirt is more important than the one on the back.”

 
 --Erik Dornhelm [GOAL]

A Mighty Magyars FC osztályai

A pom.xml állomány

A projekt modellje nem változott, s használata is megegyezik a korábbi csapatéval. A forrásokban történő módosítás után a

                                 
[norbert@matrica MightyMagyarsFC-0.0.1]$ mvn3 package assembly:single
                    

lefordítja a projektet és a maven-assembly-plugin bővítmény a target/site könyvtárba helyezi azt a jar fájlt, amelybe minden függőséget is belepakol, miután egyetlen paranccsl futtatható a projekt:

                                 
[norbert@matrica MightyMagyarsFC-0.0.1]$ java -jar target/site/MightyMagyarsFC-0.0.1-jar-with-dependencies.jar
                    

A MightyMagyarsFC osztály

Az osztály a Marvellous Magyars FC ugyanilyen nevű osztályához képest érdemben nem változott.

A JatekosAdapter osztály

Az osztályt a belsoIdo változóval bővítettük, ami időzítési célokat szolgál, de nincs szinkronizálva a szimulációban időt mérő és a szerver see avagy sense_body válaszaiban érkező valódi idővel.

package hu.fersml.magyarfc;

public class JatekosAdapter implements atan.model.ControllerPlayer {

  protected atan.model.ActionsPlayer jatekos;
  protected boolean kozepkezdes;
  public static final int BELSO_IDO_START = 42;
  protected int belsoIdo;
  
...

  @Override
  public void preInfo() {
    kozepkezdes = false;
    ++belsoIdo;
  }
                    

A növeléséről vagy a leszármazott gondoskodik, vagy ellenőrizzük, hogy ez a preInfo hívódik a leszármazottból.

A Jatekos osztály

Számos új dolgot vezettünk be ebben az osztályban, időzítési feladatoknál használja az előző osztály belsoIdo változóját, például a bevezetett passz függvényben (mint ahogyan az osztály minden valószínűleges labda érintésében, azaz a kick hívásai után) az enyemVoltIdo változóban tárolja, hogy mikor ért labdához.

    public void passz() {

        int tars = vanTisztaTars();

        if (tars > 0) {
            getPlayer().kick(tavhozEro(sajat[tars].getDistance()), sajat[tars].getDirection());
            enyemVoltIdo = belsoIdo;
                    

triviális használata például, amikor a középkezdéskor eldöntjük, hogy nem vezetgetni kell a játékszert, hanem passzolni:

        if (belsoIdo - enyemVoltIdo < 3) {

            getPlayer().kick(5, 0);
            enyemVoltIdo = belsoIdo;

            logger.info("KIS POCC VEZETEM ");

        } else {

            tarsatKeres();

        }
                    

mert ugye kezdéskor garantált, hogy nem teljesül az labdát maga előtt vezető ág feltétele.

Helyzetmeghatározás

A csapatnál bevezetett fő újdonság a játékosok helymeghatározása.

A LatottZaszlo osztály

A helymeghatározást támogatja a LatottZaszlo osztály. Már tárgyaltuk, hogy a pályán és annak környezetében 58 zászló van elhelyezve, ez az osztály egy ilyen látott zászlót reprezentál.

package hu.fersml.magyarfc;

public class LatottZaszlo {

    private int ido;
    private double x;
    private double y;
    private double distance;
    private double direction;
    private double distChange;
    private double dirChange;
    private double bodyFacingDirection;
    private double headFacingDirection;
    
    public LatottZaszlo(double x, double y) {
        this.ido = -1;
        this.x = x;
        this.y = y;        
    }
    
    public void setTulajd(int ido, double distance, double direction, double distChange,
            double dirChange, double bodyFacingDirection, double headFacingDirection) {
    ...
                        

Ha a játékos a zászlók közül egy adottat lát, akkor a Jatekos osztaly

    LatottZaszlo zaszlo[] = new LatottZaszlo[]{
        // kozepvonal
        new LatottZaszlo(0.0, 34.0),
        new LatottZaszlo(0.0, 0.0),
        new LatottZaszlo(0.0, -34.0),
        // keleti golvonal
        new LatottZaszlo(52.5, 7.0),
        new LatottZaszlo(52.5, 0.0),
        new LatottZaszlo(52.5, -7.0),
                        

tömb adattagjában rögzíti, azaz a következő hívást végzi el a megfelelő érző interfész-metódusában, például adott esetben a infoSeeFlagLeft függvényben:

  @Override
  public void infoSeeFlagLeft(atan.model.enums.Flag flag, double distance, double direction, double distChange, double dirChange,
          double bodyFacingDirection, double headFacingDirection) {

    latomASzelet(distance, direction, distChange, dirChange);

    if (!getPlayer().isTeamEast()) {

      switch (flag) {

        case OTHER_50:
          zaszlo[33].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OTHER_40:
          zaszlo[34].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OTHER_30:
          zaszlo[35].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OTHER_20:
          zaszlo[36].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OTHER_10:
          zaszlo[37].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case CENTER:
          zaszlo[38].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OWN_10:
          zaszlo[39].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OWN_20:
          zaszlo[40].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OWN_30:
          zaszlo[41].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OWN_40:
          zaszlo[42].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OWN_50:
          zaszlo[43].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;

      }

    } else {

      switch (flag) {

        case OTHER_50:
          zaszlo[44].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OTHER_40:
          zaszlo[45].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OTHER_30:
          zaszlo[46].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OTHER_20:
          zaszlo[47].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OTHER_10:
          zaszlo[48].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case CENTER:
          zaszlo[49].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OWN_10:
          zaszlo[50].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OWN_20:
          zaszlo[51].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OWN_30:
          zaszlo[52].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OWN_40:
          zaszlo[53].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OWN_50:
          zaszlo[54].setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;

      }
    }
  }
                        

ami a látott zászló egy időbélyeggel is kiegészített tulajdonságait rögzíti a játékos memóriájában, azaz a látott zászlók tömbjében, amelyet a minden ciklusban futtatott helymeghatarozas függvényben fog felhasználni.

[Tipp]Relatív érző metódusok

Az érző metódusok relatívak ahhoz képest, hogy a keleti, vagy a nyugati csapatbeli ágensre vonatkoznak-e. Az alábbi ábra vázolja, hogy melyik zászlót hogy kell értelmezni, ha a csapat először keleti, alul pedig nyugati és hívódott infoSeeFlagLeft érző metódus.

7.4. ábra - A relatív érző metódusok értelmezése.

A relatív érző metódusok értelmezése.

A kapcsolódó előadások fóliát között számos hasonló magyarázó ábrát is találsz.

    @Override
    public void postInfo() {

        helymeghatarozas();

                        

Egyszerűen kiválaszt néhány látott zászlót és mivel azok koordinátái pontosan ismertek, zajjal terhelve pedig ismertek a távolságaik is, így a játékos köröket rajzolhat ezzel az érzékelt sugárral a zászlók középpontja origóval, s a játékos pozíciója ekkor a vizsgált körök metszéspontja.

Az egyszerűség kedvéért a kód most csak két kört vizsgál, a legfrissebben (amiben az időbélyeg a legnagyobb, pontosabban csak azt nézzük, hogy 3 belső óraütésnél ne legyen régebbi) látott két zászló

  protected void helymeghatarozas() {

    int legkisebb = 1000;
    int masodikLegkisebb = 1000;

    for (int i = 0; i < zaszlo.length; ++i) {

      int iido = zaszlo[i].getIdo();
      if (belsoIdo - iido < 3) {

        masodikLegkisebb = legkisebb;
        legkisebb = i;
      }

    }

    if (masodikLegkisebb != 1000) {
      gps(zaszlo[legkisebb].getX(), zaszlo[legkisebb].getY(), zaszlo[legkisebb].getDistance(),
              zaszlo[masodikLegkisebb].getX(), zaszlo[masodikLegkisebb].getY(),
              zaszlo[masodikLegkisebb].getDistance());


      if (dbecsultx != becsultx || dbecsulty != becsulty) {

        if (becsultx == 0.0) {
          becsultx = 0.001;
        }

        becsultszog = Math.toDegrees(Math.asin((becsulty - dbecsulty) / (becsultx - dbecsultx)));

        dbecsultx = becsultx;
        dbecsulty = becsulty;

      }

    }

  }
                        

köré rajzolt körök (most két kör) metszéspontját keresi, azaz ezekkel paraméterezi fel a gps függvénye hívását.

    public void gps(double u, double v, double r, double a, double b, double m) {

        double K = r * r - m * m + a * a + b * b - u * u - v * v;

        double oszto = 2.0 * b - 2.0 * v;
        if (oszto == 0.0) {
            return;
        }

        double Z = K / oszto;
        double H = (2.0 * a - 2.0 * u) / oszto;
        double A = 1.0 + H * H;
        double B = 2.0 * v * H - 2.0 * u - 2.0 * Z * H;
        double C = u * u + Z * Z - 2.0 * Z * v + v * v - m * m;

        double diszk = B * B - 4.0 * A * C;
        if (diszk >= 0.0) {

            double gyokalatt = Math.sqrt(B * B - 4.0 * A * C);

            if (2.0 * A == 0.0) {
                return;
            }

            double x1 = (-B + gyokalatt) / (2.0 * A);
            double x2 = (-B - gyokalatt) / (2.0 * A);

            double y1 = Z - H * x1;
            double y2 = Z - H * x2;

            //

            double tav1 = (becsultx - x1) * (becsultx - x1) + (becsulty - y1) * (becsulty - y1);
            double tav2 = (becsultx - x2) * (becsultx - x2) + (becsulty - y2) * (becsulty - y2);

            if (tav1 < tav2) {

                becsultx = x1;
                becsulty = y1;

            } else {

                becsultx = x2;
                becsulty = y2;

            }
        }
    }

                        

ahol az (x1, y1), avagy meglehet, hogy az (x2, y2) metszéspont kiszámításra került. A kód szinte olvashatatlan, de mindössze annyi történik, hogy papíron felírtam a két kör egyenletét, azokat kivontam egymásból, így egy egyenes egyenlete adódott, amelyet bemetszettem az egyik köregyenlettel, azaz visszahelyettesítettem abba, amivel másodfokú egyenletet kaptam. Annyi trükk van a kód végén, hogy ha a diszkrimináns azt mutatja, hogy két valós gyök van, akkor azt választom ki, amelyik közelebb van az eddig tárolt (becsultx, becsulty) koordinátához.

Visszatérve a gps függvényt hívó helymeghatarozas függvényhez, annak teljes kódja a következő, figyeljük meg, hogy a játékos ágens becsült szögét, a becsultszog tagban szögben és nem radiánban tároljuk, annak megfelelően, hogy a (turn) ilyen paramétert vár.

  protected void helymeghatarozas() {

    int legkisebb = 1000;
    int masodikLegkisebb = 1000;

    for (int i = 0; i < zaszlo.length; ++i) {

      int iido = zaszlo[i].getIdo();
      if (belsoIdo - iido < 3) {

        masodikLegkisebb = legkisebb;
        legkisebb = i;
      }

    }

    if (masodikLegkisebb != 1000) {
      gps(zaszlo[legkisebb].getX(), zaszlo[legkisebb].getY(), zaszlo[legkisebb].getDistance(),
              zaszlo[masodikLegkisebb].getX(), zaszlo[masodikLegkisebb].getY(),
              zaszlo[masodikLegkisebb].getDistance());


      if (dbecsultx != becsultx || dbecsulty != becsulty) {

        if (becsultx == 0.0) {
          becsultx = 0.001;
        }

        becsultszog = Math.toDegrees(Math.asin((becsulty - dbecsulty) / (becsultx - dbecsultx)));

        dbecsultx = becsultx;
        dbecsulty = becsulty;

      }

    }

  }
                        
A helyzetmeghatározás működése

A következő ábrán egy helyzetmeghatározási szituációt látunk. A kép után közölt naplózási üzenet blokk mutatja, hogy az ábráról éppen leolvasható 48.87 és 30.29 valódi koordinátákra a kapott 47.5363, 29.9286 értékpár nagyon jó közelítés ebben a szituációban.

7.5. ábra - A játékos GPS eszköze.

A játékos GPS eszköze.

                                 
40379 [MightyFC Player # 4] INFO hu.fersml.magyarfc.w63.Hidegkuti  - GPS 4 x = 48.20384152794496 y = 29.773201273287505
40529 [MightyFC Player # 4] INFO hu.fersml.magyarfc.w63.Hidegkuti  - GPS 4 x = 47.71705857214796 y = 29.881437699012224
40679 [MightyFC Player # 4] INFO hu.fersml.magyarfc.w63.Hidegkuti  - GPS 4 x = 47.53632085314249 y = 29.928600710952082

                        
A LatottJatekos osztály

A helyzetmeghatározáshoz is kapcsolódó osztály a LatottJatekos osztály, felépítésben és használatában hasonló az előző LatottZaszlo osztályhoz, az azonban lényeges különbség, hogy a zászlóknak volt ismert pozíciója, a játékosnak ilyen nincs. A passz függvényben használjuk intenzíven annak eldöntésére, hogy kinek passzoljunk. Megfigyelhető, hogy ennek az osztálynak a használata észrevehető változásokat hoz a csapat eddigi játékához képest.

Az XPMImageFerSMLLogo osztály

Az edző, aki maga egy atan.model.ControllerCoach objektum képes beállítani a csapat címerét, amelyet a szimulációt megjelenítő programok képesek kirajzolni.

7.6. ábra - A FerSML projekt logójának előkészítése.

>A FerSML projekt logójának előkészítése.

Példaképpen a FerSML projekt egyik logóját állítjuk be a hu.fersml.magyarfc.w63.Sebes osztályból:

    @Override
    public void infoServerParam(java.util.HashMap<atan.model.enums.ServerParams, Object> info) {
        logger.info("CIMER BEALLITASA");
        getCoach().teamGraphic(cimer);
    }
                    

A [RCSSMANUAL] protokoll szerint a 256x64 méretű XPM (X Pixmap) kép a (team graphic) paranccsal küldhető a következő formában:

(team graphic (X Y ”XPM line” ... ”XPM line”))
                    

minden egyes parancsban az eredeti kép egy 8x8-as méretű csempéje mehet, az X azt mondja meg, hogy a szóban forgó csempe hányadik oszlop a csempézésben nullától, analóg módon az Y a sor. Természetesen a XPMImageFerSMLLogo osztályban ezt forrásban építjük fel:

        setTile(0, 2, " \"8 8 5 1\" \" 	c None\" \".	c #141512\" \"+	c #1F1F1A\" \"@	c #2A2A24\" \"#	c #36362F\" \"    @@@@\" \"    @@++\" \"   @++..\" \"   +..@@\" \"   ..@@+\" \"   .+#@+\" \"  +.+@@+\" \" @+.+@@+\" ");
        setTile(1, 2, " \"8 8 8 1\" \" 	c None\" \".	c #141512\" \"+	c #1F1F1A\" \"@	c #2A2A24\" \"#	c #36362F\" \"$	c #434239\" \"%	c #504E44\" \"&	c #5C594E\" \"+++.+#$%\" \"..@#$%&&\" \"@##$%%&&\" \"@##$$%%&\" \"@##$$%%&\" \"@##$$%%&\" \"@##$$%%&\" \"@##$$%%&\" ");
        setTile(2, 2, " \"8 8 8 1\" \" 	c None\" \".	c #1F1F1A\" \"+	c #2A2A24\" \"@	c #36362F\" \"#	c #434239\" \"$	c #504E44\" \"%	c #5C594E\" \"&	c #666457\" \"%&%###++\" \"&&&###++\" \"&&&###++\" \"&&%##@++\" \"&&%##@++\" \"%&%##@.+\" \"%&%##@.+\" \"%&$##@.+\" ");
        setTile(3, 2, " \"8 8 9 1\" \" 	c None\" \".	c #434239\" \"+	c #504E44\" \"@	c #5C594E\" \"#	c #666457\" \"$	c #767365\" \"%	c #8B8778\" \"&	c #9F9B89\" \"*	c #B5B29E\" \"+#%*%$%#\" \"+#%&%$$#\" \"+#%*%$$#\" \".#%&%$$$\" \".#%&%$$@\" \".@$&$$$#\" \".@%&%$$#\" \"+#%&%$$$\" ");
        setTile(4, 2, " \"8 8 7 1\" \" 	c None\" \".	c #666457\" \"+	c #767365\" \"@	c #8B8778\" \"#	c #9F9B89\" \"$	c #B5B29E\" \"%	c #C8C6B2\" \"+@####$$\" \".@@###$$\" \"+@@###$$\" \"+@@###$%\" \".@@###$%\" \".@@###$%\" \"+@@@##$%\" \"+@@@##$%\" ");
        setTile(5, 2, " \"8 8 8 1\" \" 	c None\" \".	c #5C594E\" \"+	c #767365\" \"@	c #8B8778\" \"#	c #9F9B89\" \"$	c #B5B29E\" \"%	c #C8C6B2\" \"&	c #ECEADC\" \"%%%%%@+.\" \"%%%&&#@+\" \"%%&&&$@+\" \"%&&&&%#@\" \"%&&&&&$#\" \"&&&&&&%$\" \"&&&&&&&%\" \"%&&&&&&%\" ");
        setTile(6, 2, " \"8 8 12 1\" \" 	c None\" \".	c #36362F\" \"+	c #434239\" \"@	c #504E44\" \"#	c #5C594E\" \"$	c #666457\" \"%	c #767365\" \"&	c #8B8778\" \"*	c #9F9B89\" \"=	c #B5B29E\" \"-	c #C8C6B2\" \";	c #ECEADC\" \".+@@#%&&\" \"@@&**%@#\" \"#@%=;-=*\" \"#@%*----\" \"$@%*--==\" \"%@%*--==\" \"&#%&--==\" \"&#%&--==\" ");
        setTile(7, 2, " \"8 8 9 1\" \" 	c None\" \".	c #504E44\" \"+	c #5C594E\" \"@	c #666457\" \"#	c #767365\" \"$	c #8B8778\" \"%	c #9F9B89\" \"&	c #B5B29E\" \"*	c #C8C6B2\" \"@+*&    \" \"++**    \" \"@.%*&   \" \"*%##%   \" \"***$#$  \" \"****%#  \" \"*****&  \" \"&&*&&&  \" ");

                    

ez a kódcsipet tehát a kép 3. sora, azaz most a 8 darab 8x8-as csempe, ami a csempézésben a harmadik sort alkotja. Ha minden jól megy, akkor az RCSS szerver csempénként (ok) paranccsal nyugtáz, amit az edzőben a infoHearOk interfész függvény felüldefiniálásával most ki is íratunk, s íme az eredmény:

16431 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16431 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16432 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16432 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16432 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16432 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16432 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16432 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16432 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16432 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16432 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16433 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16433 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16433 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16433 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16433 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16433 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16433 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
16433 [MightyFC Coach] INFO hu.fersml.magyarfc.w63.Sebes  - OKTEAM_GRAPHIC
...
                             

A beépített Atan-os példától annyiban tértünk el, hogy mi a játékosaink mintájára itt is adapter osztályt használunk, s a „Sebes Gusztáv” objektumban a kép tagként szerepel, amit a kommunikáció elején (infoServerParam) az Atan-os példának megfelelően küldünk be a szimulációba.

[Tipp]Készítsd el saját csapatod címerét!

Odafigyelést igényel, de egyébként elvben egyszerű a dolgod, ha olyan képfeldolgozó programot használsz, mint például a GNU Gimp (GNU Image Manipulation Program) program, mert ez tud XPM-ben menteni. Ami egy egyszerű szöveges formátum, amelyből a atan.model.XPMImage.setTile() aktuális paraméterének sztring literálokat kell építened, gyakorlatilag levédett idézőjelek bőséges beszúrásával.

A Mighty Magyars FC értékelése

Annak ellenére, hogy bár a csapat játéka sok momentumában javult, eredményességben mégsem nő a jegyzet korábbi csapatai fölé a Mighty Magyars FC. Pedig a játék bíztatóan indul, a középkezdés után már-már „barcásnak” nevezhető passzokkal játszák meg a labdát a játékos ágensek. Sőt van olyan passzsorozat, ahol szinte flipperszerűen pattognak az átadások. Mindez persze egyszerűen a passz függvény megjelenésének köszönhető, amiből rögtön 3 változat is található a forrásban, az aktuális éppen a látott társak közül a távolabbit igyekszik megjátszani. Mindössze ennyi heurisztika van beleépítve, ami még kevés, hiszen ez a saját kapu irányába nem egy öngól forrása is, másrészt az itteni passz függvény általános jellemzője a kapott labda azonnali visszaadása, visszapassza. Ez a fő oka annak, hogy a csapat kapura nem annyira veszélyes, de ha eljut a kapu előterébe, akkor már tipikusan nem hibázik. Elvben tehát a játékerő effektív emeléséhez nem lenne más dolgunk, mint a játékosokat pozíciójukban tartani a pályán, hiszen ekkor a jó passzolási képesség, ha azzal is párosul, hogy előnyben részesíti a másik kapu felé történő passzolást, a jegyzet csapataival szembeni szép sikerekkel kecsegtet.

További új „viselkedés” csírákat is megfigyelhetünk a Mighty játékában, például a „labdatartást és vezetést”. A következő találkozón „Hidegkuti” Maradona-s gólját láthatjuk 3922-től egészen 4264-ig, miközben „Hidegkuti” plusz egy félpályányi kört vezeti a labdát MaradonasGol201110251633-MightyFC_2-vs-MarvellousFC_2.rcg egy MightyFC - MarvellousFC, (2:2) mérkőzésen.

Marvellous, Mighty, Golden, Magical, Magnificent; avagy melyiket válasszam?

Megkísérti a hétről-hétre szorgalmasan dolgozó hallgatót, hogy aktuális csapatát (azaz egyben fejlesztését) leváltsa és a laborgyakorlatok diktálta ütem szerint váltogassa azokat. Álljunk ellen ennek, hiszen az előző pont éppen azt mutatta, hogy az új csapatok fejlesztésének motivációja nem az eredményesség, hanem a laborgyakorlat tematikája. (Például a Golden Team FC 0.0.3 azt mutatja meg, hogyan írjunk JUnit teszteket a projektünkhöz. A Golden Team FC 0.0.4 pedig azt, hogyan tudunk AspectJ aspektusokat szőni a projektünkbe.) Ezzel szemben azt javaslom, hogy kisebb részeket építsünk be a saját fejlesztésünkbe. A Mighty Magyars FC-ból például a passzolást, s hogy ennek alternatívái kezelik a bedobást vagy a szabadrúgást is. Ez például csupán a LatottJatekos osztály, s a Jatekos osztályból a passz függvény és a kapcsolódó tagok, illetve érző metódusok átvételét, utóbbiak esetén bővítését jelenti.

Az eddigi fejlesztések módszertana

 

„Több erőfeszítést fektetünk a tervezésbe és kódolásba (ami szórakozás), hogy csökkentsük a jóval költségesebb tesztelést és hibajavítást (ami nem az).”

 
 --Kernighan-Plauger [KERNIGHANPLAUGER] A programozás magasiskolája

Speciális helyzetben voltunk/vagyunk a jegyzet példái kapcsán, hiszen (a fenti mottó szellemét idézve) esetünkben még a tesztelés is szórakozás, mert egészen pontosan ez most a mérkőzések megfigyelése.

Hozzáálásunk az eddigi csapatok szintjén és adott csapaton belül is egyértelműen az evolúciós programfejlesztést idézi. Emlékezzünk csak, hogy a Mighty Magyars FC-ból például még ugyanazon hét két külön laborgyakorlata között is dobtunk ki metódusokat (ezek a vanKozeliTars és vanTisztaTars függvények mint „eldobható prototípusok” voltak) a passzolás környékéről.

Munkánk inkrementális jellegét az adja, hogy a robotfoci világának megismerését, a saját csapatok tervezést és azok megvalósítását nem elkülönülve, hanem jobbára egyidejűleg végezzük.

Miközben leginkább a saját csapatunk programjával törődünk, ez agilis extrém jegy. Eközben annyira háttérbe került a dokumentáció, hogy nyilván feltűnően az eddigi csapatok kódjában még a szabványos javadoc dokumentációs megjegyzéseket sem írtunk.

Ám a Mighty Magyars FC csapattal bezárva, ezt az eddigi megközelítésünket felfüggesztjük. A következő Golden Team FC 0.0.2 példától már több hangsúlyt fektetünk a tervezésre és a dokumentálásra. S majd az ezt követő csapatokat már nem sértve az OCP elvet, ennek a csapatnak a valódi továbbfejlesztéseiként valósítjuk meg.

HELIOS_base - Golden Team FC, 35:0

 

„Tapasztalatunk szerint a program minőségének legjobb kritériuma az olvashatóság: ha egy program könnyen olvasható, valószínűleg jó; ha nehezen olvasható, valószínűleg nem jó.”

 
 --Kernighan-Plauger [KERNIGHANPLAUGER] A programozás magasiskolája

Igen, ennél a csapatnál, az eddigi csapatok fejlesztésének áttekintésénél írtaknak megfelelően, már doksizzuk is a kódot. Sőt immár azt is jó ötletnek tartjuk, hogy a csapat fejlesztését absztraháló Maven projekt tárgyát, azaz egyszerűen a csapatot megvalósító jar állományt más projektek függőségeként használd. Konkrétan például az itt kifejlesztett Hidegkuti osztálybeli játékost használd fel egy másik csapat felépítésében.

A csapat további aktualitása, hogy a projekt gyökerében elhelyeztünk egy kis indító bash szkriptet, aminek az a feladata, hogy minden játékost külön virtuális gépben indítson. Ezzel elkerülhető az a vétség, hogy figyelmetlenségből, vagy akár etikátlanul ágenseid az RCSS (say) parancsát megkerülve kommunikáljanak egymással. Hiszen erre a „kisértés adott volt”, például abban a formában, hogy két ágens objektum hívja egymást, vagy valamilyen közös tagot, esetleg valamilyen mindkettőjük által látható statikus tagot vagy metódust használjanak. Ha tehát volt is ilyen vétség, ez a külön virtuális gépek miatt nem lesz érdemben alkalmazható. S ez fontos például az említett aktualitásban, a most indított (s remélhetőleg hagyományt teremtő) Prog2 labor labdarúgó-bajnokság-ban, avagy röviden a PLB-ben.

Az említett szkript a következő:

#!/bin/bash

host=${1-localhost}
port=${2-6000}
team=${3-Prog2}

for ((i=0;i<11;++i))
do
java -jar target/site/GoldenTeamFC-0.0.1-jar-with-dependencies.jar $host $port $team $i&
sleep 1
done
exit 0
            

nem csinál mást, mint egyenként, külön JVM-ben indítja az ágenseket. Ha a szimuláció kész, majd a killall java paranccsal tudod kényelmesen kilőni ezt az így indított 11 processzt. A projekt modellje nem változott, ennek megfelelően a projekt GoldenTeamFC-0.0.1-jar-with-dependencies.jar (most függőségeivel is ugyancsak csapatainknál szokásosan egybecsomagolt) tárgyát a

                                 
[norbert@matrica GoldenTeamFC-0.0.1]$ mvn3 clean package assembly:single
                    

életciklussal generálod le.

A Golden Team FC osztályai

Az indító szkriptben láthatod, hogy egy plussz paramétert, a játékos számát is átt kell adnod, s majd a connectAll hívása helyett a connect aktuális paramétereként továbbadnod.

A GoldenTeamFC osztály

Ennek megfelelően a GoldenTeamFC osztály az alábbiak szerint módosul a korábbi csapat hasonló funkciójú csapatához képest.

package hu.fersml.magyarfc;

public class GoldenTeamFC extends atan.model.AbstractTeam {

  private static org.apache.log4j.Logger logger =
          org.apache.log4j.Logger.getLogger(GoldenTeamFC.class);

  public GoldenTeamFC(String team, int port, String host) {

    super(team, port, host, true);
    logger.info(host + ":" + port + " " + team);

  }

  @Override
  public atan.model.ControllerCoach getNewControllerCoach() {
    return new hu.fersml.magyarfc.w63.Sebes();
  }

  @Override
  public atan.model.ControllerPlayer getNewControllerPlayer(int number) {

    hu.fersml.magyarfc.Jatekos jatekos = null;

    switch (number) {

      ... 
      // Ugyanúgy példányosítunk az Aranycsapat kezdőiből, 
      // mint az előző csapatnál
      ...

    }

    return (atan.model.ControllerPlayer) jatekos;
  }

  public static void main(String[] args) {

    org.apache.log4j.BasicConfigurator.configure();
    org.apache.log4j.Logger.getLogger("atan.model").setLevel(org.apache.log4j.Level.ERROR);

    if (args.length == 4) {
      // host port team squad_number     
      int port = 6000;
      try {
        port = Integer.parseInt(args[1]);
      } catch (java.lang.NumberFormatException e) {
        port = 6009;
      }
      int number = 0;
      try {
        number = Integer.parseInt(args[3]);
      } catch (java.lang.NumberFormatException e) {
        number = 0;
      }
      new GoldenTeamFC(args[2], port, args[0]).connect(number);

    } else if (args.length == 3) {
      // port team  squad_number     
      int port = 6000;
      try {
        port = Integer.parseInt(args[0]);
      } catch (java.lang.NumberFormatException e) {
        port = 6009;
      }
      int number = 0;
      try {
        number = Integer.parseInt(args[2]);
      } catch (java.lang.NumberFormatException e) {
        number = 0;
      }
      new GoldenTeamFC(args[1], port, "localhost").connect(number);

    } else if (args.length == 2) {
      // team squad
      int number = 0;
      try {
        number = Integer.parseInt(args[1]);
      } catch (java.lang.NumberFormatException e) {
        number = 0;
      }
      new GoldenTeamFC(args[0], 6000, "localhost").connect(number);

    } else if (args.length == 1) {
      // squad
      int number = 0;
      try {
        number = Integer.parseInt(args[0]);
      } catch (java.lang.NumberFormatException e) {
        number = 0;
      }
      new GoldenTeamFC("GoldenFC", 6000, "localhost").connect(number);

    } else {
      new GoldenTeamFC("GoldenFC", 6000, "localhost").connectAll();

    }
  }
}
                    

A kényelmes tesztelést szolgálja, hogy paraméter nélküli híváskor ugyanúgy az egész csapatod bekapcsolja a szerverhez, mint ahogyan eddig használtad.

Némiképp módosul a helyzet, ha az edzőt is szeretnéd csatlakoztatni. Ehhez az Atan 1.0-t is módosítanod kell, mert per pillanat nem tudja csak a connectAll hívásával az edzőt csatolni. Triviális megoldásként most a 11. „játékosként” csatoljuk az edzőt, azaz az alábbi módosítást tesszük a atan/model/AbstractTeam.java forrás kódjában

  public void connect(int index) {
    try {
      
      if (index == 0) {
        
        players[index].connect("", true);
        
      } else if (index == 11) {
        
        coach.connect();
        
      } else {
        
        players[index].connect("", false);
        
      }

                    

minekután legyártjuk azt az Atan-t, ami már tudja csatolni az edzőt is külön. Ez lesz nekünk most az atan-1.0.1plb.jar tárgy. Ha nem tőlem rántod le hanem régebbi atan-1.0.0 projekt könyvtáradban dolgozol, akkor ne feledd a pom.xml-ben emelni a verziószámot:

  <modelVersion>4.0.0</modelVersion>
  <groupId>atan</groupId>
  <artifactId>atan</artifactId>
  <packaging>jar</packaging>
  <version>1.0.1plb</version>
  <name>Atan</name>
                    

a szóban forgó 1.0.1plb-re, amely a következő parancssorral készül

[norbert@matrica atan-1.0.1plb]$  mvn3 clean generate-sources javacc:jjdoc package site assembly:single install:install-file -Dfile=target/atan-1.0.1plb.jar -DgroupId=atan -DartifactId=atan -Dversion=1.0.1plb -Dpackaging=jar
                  

ahol az utolsó életciklus alkalmazásával a lokális repóba is települt. Nincs más hátra, mint a saját csapatunkban explicite megadni ezt az új verziót, hogy bizony mi már attól függünk:

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
        </dependency>
        <dependency>
            <groupId>atan</groupId>
            <artifactId>atan</artifactId>
            <version>1.0.1plb</version>
        </dependency>
    </dependencies>
    <build>
                    

s innentől már megint csak a saját projektünkre koncentrálhatunk. Tesztelés képpen nézzük meg, hogy a start.sh szkripttel indított módon látszik-e a logó, mert ha igen, az jó, hiszen csakis az edző küldhette be. Kipróbáltam, OK, működik.

Egy másik Java alapú csapat: a DAInamite

A 2011-es isztambuli világbajnokság 14. helyezettje a berlini DAInamite csapat szintén Java, bár nem Atan alapú, hanem saját keretrendszerükre épít. Az említett legrangosabb világversenyen a most 6-7 éves múlttal rendelkező csapat kuriózumnak számít azzal, hogy nem az egyeduralkodónak számító C++ nyelven, hanem Javában implementált.

[Tipp]Prog2 - Dainamite, 0:13

A DAInamite is egy Maven alapú projekt, de a királyi úton is járhatunk: ha csak pár mérkőzést akarunk velük játszani, akkor a VB lapjáról letölthetjük a binárisat, ami már ismerős módon egy jar állomány (a mi saját szkriptünk mintájára érdemes egy indítót készíteni, amelyben a Dainamite indító szkriptjét hívod, például a localhost hoszttal és a . aktuális könyvtárral).

A mérkőzés felvételét itt találja a kedves olvasó: 201110271143-Prog2_0-vs-Dainamite_13.rcg. A Prog2 csapatnév a Golden Team FC 0.0.1 változatának (indító szkriptjebeli) alapértel neve, gyakorlatilag a Mighty.

[Tipp]GoldenFC - Photon, 0:18

Az angol Photon egy C++, speciálisan egy Agent2D alapú csapat. Ha már a http://www.socsim.robocup.org/files/2D/binary/RoboCup2011/ lapon jártunk, kipróbálunk néhány csapatot, tanulságos lesz... ennek jegyében játszottuk le azt a mérkőzést.

A mérkőzés felvételét itt találja a kedves olvasó: 201110271619-GoldenFC_0-vs-Photon_18.rcg. Saját csapatként a Golden Team FC 0.0.1 verziót futtattuk itt is.

[Tipp]GoldenFC - HELIOS2011, 0:50

A japán HELIOS2011 nem kell bemutatnunk, hiszen sokat foglalkoztunk vele már eddig is a jegyzetben. A jelen három mérkőzést megtekintve egyrészt látszik, hogy még a világklasszis csapatok közötti rangsornak is van struktúrája. A japán csapat megvalósítja az emberi klasszis csapat archetípusát, miszerint egy eladott labda és máris ott van belőle a gól. Még jó, hogy mi a programozás bevezetésének szántuk a jelenlegi csapataink fejlesztését és nem az MI algoritmusoknak...

A mérkőzés felvételét itt találja a kedves olvasó: 201110281449-GoldenFC_0-vs-HELIOS2011_50.rcg.

A Golden Team FC 0.0.2 mint multiágens rendszer

Eddig érve az olvasásban és a párhuzamos gép melletti munkában már érezhető, hogy a csapat fejlesztésének jelenlegi módszere nehézkessé vált és nem mellesleg spagetti kódot fog eredményezni. A világklasszis csapatok ellen eddig fejlesztéseink teljesen eredménytelenek, az egymás közötti játék viszont már képes némi sikerélményhez juttatni a fejlesztőt és (igaz ritkábban) a nézőt.

Annyit azért elértünk, hogy megvannak az első gyakorlati tapasztalatok, megismertük a a problémát annyira, hogy egy megoldását újra tudjuk, immár multiágens rendszerként fogalmazni-gondolni. Az ágens intuitív fogalmát mostanra bizonyosan megalapoztuk az olvasóban, most a témakör szakkifejezéseit használva tekintünk vissza az eddig elvégzett munkára. A szóban forgó terminológiát a [RUSSELNORVIG] alapkönyvből ismerhetitek meg, s természetesen mi is onnan használjuk.

Reflexszerű viselkedés

A félévben megismert Atan interfész eleve az ágens szemléletet támogatja: az Atan-ra épülő program érzékeli a környezetét, például ha a játékos ágens lát valamit (see parancs érkezik a szervertől), meghívódnak az interfész megvalósított infoSee* módszerei, ezekre alapozva a játékos ágens program feladata meghatározni, hogy milyen tevékenységekkel válaszoljon a postInfo függvényében.

Első csapataink egyszerű érzékelés-tevékenység szabályokat tartalmaztak. Például a CsakALabda programja azt fogalmazta meg, hogy ha (az else if ágban)

  @Override
  public void postInfo() {

    if (kozepkezdes) {
      kozepkezdes();
    } else if (latomAFocit) {
      utana();
                    

láttam a labdát, akkor olyan

  protected void utana() {

    getPlayer().turn(directionFoci);
    getPlayer().turnNeck(directionFoci + dirChangeFoci);
    getPlayer().dash(100);

    if (distanceFoci < 0.6) {

      getPlayer().kick(20, directionFoci);

    }

  }
                    

cselekvéseket hajtok végre, amelyek (várhatóan majd) a labda felé visznek.

De láthattad, hogy ez az egyszerű reflexszerű (simple reflex agent) viselkedés már arra sem elegendő, hogy a rögzített szituációkat (bedobás, szöglet, pontrúgás) kezelni tudja az ágens, hiszen az aktuális érzékelés felülírta a korábbit, hogy egy ilyen rögzített szituáció történik éppen.

Modellalapú reflexszerű viselkedés

A laborgyakorlatokon számos csoport fogalmazta meg az igényt, hogy például „ha támadás van, akkor ezt meg ezt kellene csinálni” de itt a támadás már egy komplex viselkedés, amit mi emberi megfigyelők még abba a viselkedésbe is bele tudunk látni a pályán, ami nincs is (onnan tudjuk, hogy nincs, mert mondjuk mi programoztuk a tesztelt klienst és tudjuk, hogy csak szimpla reflexeket írtunk bele). Azonban a támadást magának az ágensnek kell felismernie. Ehhez nyilván nem elegendő az aktuális érzékelés, hanem a korábbi érzékelések sorozata, sőt egy olyan belső modellben értelmezett sorozata szükséges, ami alapján egyáltalán megpróbálhatjuk eldönteni, hogy támadásban vagy védekezésben van-e a csapat.

Azt megjegyezhetjük, hogy az előző pontban említett rögzített szituációk bekövetkezésének tényét egyetlen észlelésből megtudhatjuk, ez egyszerűen a környezet alaptulajdonsága.

Visszatérve a belső modell alkotás szükségességéhez, most csapatunkat továbbfejlesztjük, hogy az továbbra is reflexszerű, de immár egyben modellalapú ágensként (model based agent) funkcionáljon.

Viselkedés alapú megközelítés

Az előző két pontban szóhasználatában észrevehetjük, hogy hatott ránk a kurzusban a a LEGO© NXT robotok viselkedésalapú programozása során megismert Viselkedés API, pontosabban a Rodney A. Brooks által 1991-ben [BROOKS] bevezetett behavior-based robotics / subsumption architecture alapú megközelítés. (A leJOS API lejos.subsumption csomagja megadja annak lehetőségét, hogy a Java nyelven programozott robotjaink működését subsumption architecture alapon szervezzük meg.)

A csapat továbbfejlesztése egy az előző két pontbeli megközelítésre alapuló hibrid lesz. Csak nagyon távoli célunk lehet egy olyan intuitív öntudatos [COP] ágens fejlesztése, melyben a belső modell alapja akár maga az RCSS szerver lenne.

Quo vadis Golden Team FC?

Komoly terveink vannak, habár nem a robotfocival, hanem a FerSML platformmal, de ennek ellenére távolabbi célként kijelöltük egy FerSML avatárokat RCSS focicsapattá transzformáló szoftver írását. Ehhez persze előszöt a FerSML avatár fogalmának kell kikristályosodnia.

A korábban említett intuitív öntudatos [COP] ágens megintcsak egy nagyon távoli cél lehet.

Ezért a robotfoci iránti érdeklődésünk fenntartásához közelebbi, napi célok kijelölése is szükséges. Egy ilyen legyen például a megkezdett „gps”-es és ahhoz alternatív pozicionáló rendszerünk továbbfejlesztése, azzal, hogy a helymeghatározást Kálmán szűréssel próbálja javítani a játékos. Az ilyen közeli fejlesztési célok teljesítésének, továbbá valamilyen csapatszintű stratégia kidolgozásának indikátoraiként szolgálnak majd a tervbe vett Magical Magyars FC és a Magnificent Magyars FC csapatok.

Milyen problémákról és megoldásokról olvashatunk a szakirodalomban?

A szakirodalmi „hálózatot” könnyen felderíthetjük, hiszen a TDP-k (a RoboCup kvalifikácós eljárásnak megfelelően) tipikusan rövid, lényegi cikkek, könnyű őket átfutni, s hivatkoznak a megfelelő alapcikkekre (a felhasznált módszerekre, szoftver forrásokra, az előző állapotokhoz viszonyított fejlődésre és sorolhatnánk az imént linkelt „Qualification in 2D RoboCup Simulation League for RoboCup 2011” című dokumentum tételeit).

A 2010-es TDP-k között (az itt szereplő 19 között) böngészve első ismerkedésként megnézzük, hogy bizonyos kulcsszavak hány TDP-ben szerepelnek. Mondjuk a pozícionálás szó kapcsán ez „positioning”=7. Nem ehhez kötve, csak vaktában rákeresve lehetséges módszerekre ezt kapjuk: „machine learning”= 4, „neural networks”= 5, „reinforcement learning”= 3, „Q learning 2”= „genetic algorithm 2”= 2, „a-star 1”= 2, „Kalman filter”= 1.

HELIOS_base - Golden Team FC 0.0.2, 34:0

 

„Aki a szakmában nem ismeri a Kálmán-szűrést az félember.”

 
 --Arató Mátyás A szerző személyes élményként hallotta gyakorta Arató Mátyástól ezt a Kossuth Lajos Tudományegyetem Rendszerelmélet kurzusán.

Deliberatív vagy reaktív?

Egyelőre (tehát a jelen csapatban) nem tervezzük, hogy ágenseink tervezzenek, tehát továbbra is reflexszerűen reagálnak majd a környezet változására. Az alcím felvetette kérdésben pedig belevágunk egy egyszerű világmodell felépítésébe (deliberatív jelleg), de e mellett a tervezett érzékelés-akció párokat viselkedések (reaktív jelleg) formájában implementáljuk. Tehát az előző pontok folytatásaként egy hibrid architektúrában gondolkozunk.

Ágens technológia tekintetében a [AGENSKOMLODI1], [AGENSKOMLODI2] közleményeket ajánljuk, mint horizonálisabb perspektívájú áttekintéseket. Vertikális megközelítésben, a [RUSSELNORVIG] tankönyvet javasoljuk bevezetőként fellapozni, ha például az olvasó a subsumption architecture (a most hivatkozott könyv terminológiájában: alárendelt architektúra) kapcsán akar több ismeretet szerezni.

A Vilagmodell osztály

A Jatekos osztályan voltak olyan tagjaink, amelyek a játék modelljének leírását szolgálták, ilyenek voltak például a többi játékost jellemző LatottJatekos tömbök a saját (például LatottJatekos sajat[] = new LatottJatekos[11];) és az ellen csapatra, vagy az összes zászló LatottZaszlo tömbje. Első lépésben ezeket emeljük át ebbe az új, a világmodellt reprezentáló osztályba.

A módszertani részben írtaknak megfelelően innentől nem írunk le sort dokumentáció nélkül, illetve a korábbi, most látókörünkbe kerülő kódokat is doksizzuk. Minden forrásállományt ellátunk egy az állományra vonatkozó megjegyzéssel, majd a csomag deklaráció után az osztályok fordítási egységeiben (egy forrás/egy osztály rendszerben) az osztály dokumentációs megjegyzése következik, s így tovább a Java kódolási konvencióknak megfelelően. A jelen osztályunk eleje ennek megfelelően kialakítva most így fest majd:

/*
 * Vilagmodell.java
 *
 * Bátfai Norbert: Mesterséges intelligencia a gyakorlatban: bevezetés a
 * robotfoci programozásba Debreceni Egyetem, Informatikai Kar,
 * Információtechnológia Tanszék, http://www.inf.unideb.hu/~nbatfai/phd/
 *
 * Golden Team FC, "0.0.2"-től.
 *
 * Copyright (C) 2010, Dr. Bátfai Norbert
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * Ez a program szabad szoftver; terjeszthető illetve módosítható a Free
 * Software Foundation által kiadott GNU General Public License dokumentumában
 * leírtak; akár a licenc 3-as, akár (tetszőleges) későbbi változata szerint.
 *
 * Ez a program abban a reményben kerül közreadásra, hogy hasznos lesz, de
 * minden egyéb GARANCIA NÉLKÜL, az ELADHATÓSÁGRA vagy VALAMELY CÉLRA VALÓ
 * ALKALMAZHATÓSÁGRA való származtatott garanciát is beleértve. További
 * részleteket a GNU General Public License tartalmaz.
 *
 * A felhasználónak a programmal együtt meg kell kapnia a GNU General Public
 * License egy példányát; ha mégsem kapta meg, akkor tekintse meg a
 * <http://www.gnu.org/licenses/> oldalon.
 *
 * Verziótörténet: 0.0.1 start, a Jatekos osztályan voltak olyan tagjaink, amik
 * a játék modelljének leírását szolgálták, ilyenek voltak például a többi
 * játékos és a zászlók pozíciója stb. első lépésben ezeket emeljük át ebben az
 * új osztályba.
 *
 */
package hu.fersml.magyarfc;

/**
 * Világmodell példa az ágensek deliberatív szemléletéhez. Folyamatosan
 * nyilvántartja a saját, a többi játékos és a zászlók helyzetét.
 *
 * @author Bátfai Norbert
 * @version 0.0.1, 11/09/11
 * @since Golden Team FC 0.0.2
 * @see hu.fersml.magyarfc.Jatekos;
 */
public class Vilagmodell {
                    

Visszatérve érdemben a világmodellhez: az említett „látott” játékosokat és zászlókat hordozó LatottJatekos és LatottZaszlo tömbök elnevezése most nem a legszerencsésebb, hiszen a modell lényegénél fogva az éppen nem látott játékosokat és zászlókat is tartalmazza. Ez most a korábbi munkánk öröksége, egyelőre nem nyúlunk hozzá, hogy az átalakításunk ne azon csússzon el, hogy egyszerre mindent megváltoztatunk. Ha tehát a szóban forgó tagokat átemeljük az új osztályba, akkor írnunk kell hozzájuk lekérdező és beállító módszereket, hiszen az információk (az érző infoSee* metódusokból) továbbra is a Jatekos osztálybeli objektumokban keletkeznek majd a mi programunk szintjén.

Ha tehát a Jatekos osztályban van egy hivatkozás az áttett

sajat[tars]
                    

tagra, akkor innentől a Jatekos osztályban tagként megjelenő vilagmodell objektumon keresztül használjuk majd azt:

vilagmodell.sajatJatekos(tars)
                    

De még ennél is egyszerűbben járhatunk el, ha a Jatekos osztályból azokat a módszereket, amelyek használják az immár Vilagmodell osztálybeli tagokat (ezek most a gps és a helymeghatarozas módszerek) tehát azokat a módszereket is átemeljük. Így egyrészt nem is kell a forrásimplementációjukhoz nyúlnunk, másrészt a Jatekos osztály koncepciója is tovább tisztul ezzel az elválasztással.

Egy alternatív helyzetmeghatározás

A világmodellben bevezetett újdonság a játékosok helymeghatározásához egy kiegészítő megoldás támogatása. Ami egyszerűen arra épül, hogy implicite felteszi: az ágens becsült szöge és pozíciója megvan. Ekkor ha lát egy zászlót, akkor a zászló pontosan ismert koordinátáiból és a távolságból ki tudja számítani az aktuális helyzetének becslését.

A Jatekos osztály érző módszereiből állítjuk a „látott zászlókat”, ezeket a hívásokat kicsit megpatkoljuk, hogy átadják (a zászló mágikus sorszáma mellett) direktben a látott zászló távolságát is a világmodellnek:

    if (!getPlayer().isTeamEast()) {

      switch (flag) {

        case OTHER_50:
          vilagmodell.zaszlo(distance, 54).setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
        case OTHER_40:
          vilagmodell.zaszlo(distance, 53).setTulajd(belsoIdo, distance, direction, distChange, dirChange, bodyFacingDirection, headFacingDirection);
          break;
                    

A Vilagmodell alábbi zaszlo függvényét

  public LatottZaszlo zaszlo(int szam) {

    return zaszlo[szam];
  }
                    

pedig ilyenformán túlterheljük:

  public LatottZaszlo zaszlo(double tav, int szam) {

    if (tav < 15.0) {
      double zx = zaszlo[szam].getX();
      double zy = zaszlo[szam].getY();

      double atfogo = Math.sqrt((becsultx - zx) * (becsultx - zx)
              + (becsulty - zy) * (becsulty - zy));

      if (atfogo != 0.0) {

        double szog = Math.asin((becsulty - zy) / atfogo);

        becsultya = becsulty + zy - becsulty + tav * Math.sin(szog);
        becsultxa = becsultx + zx - becsultx - tav * Math.cos(szog);

        logger.info("ALT BECS "
                + " zaszlo = " + szam
                + " tav = " + tav
                + " atfogo = " + atfogo
                + " szog = " + Math.toDegrees(szog)
                + " becsultx = " + becsultx
                + " becsulty = " + becsulty
                + " becsultxa = " + becsultxa
                + " becsultya = " + becsultya);

      }
    }

    return zaszlo[szam];
  }
                    

miszerint ha a zászló 15 méternél közelebb van, akkor elkérjük a pozícióját, kiszámoljuk, hogy az éppen tárolt korábbi (de a másik módszerből származó) pozíciónk milyen messze volt a zászlótól (atfogo), amivel kijön, hogy milyen abszolút szögben (szog) jövünk a zászló felé, amivel a zászló távolságával, mint átfogóval számolva már tudjuk származtatni az alternatív becslésünket.

Az alternatív helyzetmeghatározás működése

A következő ábrán egy középkezdéskori szituációban mutatjuk be az imént tárgyalt kódot. Azért itt, mert ez esetben a becsült koordináta tudjuk, hogy a pontosan ismert koordináta, s a koordináta rendszer mind a négy negyedében ellenőrizhetjük egyszerre a számítást. (Szépséghiba, hogy a példa így elfajuló, mert a korábbi távolság és a zászló kapcsán látott ugye most egyenlő.)

7.7. ábra - Az alternatív helyzetmeghatározás működése

Az alternatív helyzetmeghatározás működése.

A logok tanulsága szerint számolásunk jó:

                                 

...
38780 [GoldenFC Player # 3] INFO hu.fersml.magyarfc.Vilagmodell  - ALT BECS  zaszlo = 1 tav = 12.2 atfogo = 12.206555615733702 szog = -55.00797980144134 becsultx = -7.0 becsulty = -10.0 becsultxa = -6.996240601232605 becsultya = -9.994629430332294
...
38889 [GoldenFC Player # 5] INFO hu.fersml.magyarfc.Vilagmodell  - ALT BECS  zaszlo = 1 tav = 12.2 atfogo = 12.206555615733702 szog = 55.00797980144134 becsultx = -7.0 becsulty = 10.0 becsultxa = -6.996240601232605 becsultya = 9.994629430332294
...
17100 [Bcsapat Player # 3] INFO hu.fersml.magyarfc.Vilagmodell  - ALT BECS  zaszlo = 1 tav = 12.2 atfogo = 12.206555615733702 szog = -55.00797980144134 becsultx = -7.0 becsulty = -10.0 becsultxa = -6.996240601232605 becsultya = -9.994629430332294
...
17178 [Bcsapat Player # 5] INFO hu.fersml.magyarfc.Vilagmodell  - ALT BECS  zaszlo = 1 tav = 12.2 atfogo = 12.206555615733702 szog = 55.00797980144134 becsultx = -7.0 becsulty = 10.0 becsultxa = -6.996240601232605 becsultya = 9.994629430332294
...

                        

A w63 alcsomag osztályai

A w63 alcsomag osztályai (az GoldenTeam FC, 0.0.1 ugyanilyen nevű osztályához képest is) mind megváltoztak. Hiszen az eddig proteced tagként örökölt becsült helyzet koordináták már nem lézetnek, csak a világmodell beállító és lekérdező módszerein keresztül érik el azokat, példaképpen nézzük meg „Hidegkutit” amiként a középkezdéskor aktualizálja a becsült helyzetét a pontossal:

	switch (getPlayer().getNumber()) {

	    case MEZSZAM:
		vilagmodell.setBecsultX(-1.0);
		vilagmodell.setBecsultY(0.0);
                    

A GoldenTeamFC osztály

Apró módosítást eszközöltünk a GoldenTeamFC osztályban, a kényelmes tesztelhetőség miatt már a korábbi verzióban is hagytunk egy connectAll-os ágat (alul az else ág), most az egy parancssorargumentumos ágat bővítettük anyival, hogy ha nem a mezszám a szóban forgó paraméter, akkor csapatnévnek veszi azt. Ez a tesztelésben a csapat önmagával játszott mérkőzéseinek indításában (és méginkább leállításában) kényelmes opció.

    } else if (args.length == 1) {
      // squad
      int number = 0;
      try {

        number = Integer.parseInt(args[0]);
        new GoldenTeamFC("GoldenFC2", 6000, "localhost").connect(number);

      } catch (java.lang.NumberFormatException e) {
        // most arra számítva, hogy ugyanazt a csapatot magával tesztelem, 
        // ha nem (mez)számot adtam:
        new GoldenTeamFC(args[0], 6000, "localhost").connectAll();
      }

    } else {
      new GoldenTeamFC("GoldenFC2", 6000, "localhost").connectAll();

    }
  }
}
                    

Ágensek kommunikációja, a Jatekos osztály

Az RCSS robotfoci játékos ágensek kizárólag (say) parancsok alkalmazásával kommunikálhatnak egymással. Ebben a pontban lehetővé téve ezt a kommunikációt, kibővítjük a Jatekos osztályt.

Mire akarjuk ezt felhasználni? A Golden Team FC sajnálatos viselkedési formája, hogy néha bedobásnál vagy akár mezőnyben számos játékos torlódik a labda közelében és ebből az állapotukból nem könnyen tudnak kilépni. Ezen fogunk most változtatni. A terv az, hogy például a bedobásnál az oda érkező játékos azt kiáltja a társaknak, hogy bedob, s aki ezt hallja, a belső órája (belsoIdo) szerinti néhány ütés erejéig a hanggal ellentétes irányba mozog. (Szinkronizációval egyelőre nem foglalkozunk, hanem mondjuk 1/2 vagy 1/3 valséggel viselkednek így a játékosok.)

Kezdésként az alábbi két csipettel bővítjük az osztály kódját, egyrészt a bedobásnál, a függvény végén

  protected void bedobasnal() {

...

    getPlayer().say("\"dobok\"");
    logger.info("HALLOM -> "
            + getPlayer().getNumber());

  }
                    

illetve hallgatózunk is az infoHearPlayer felüldefiniálásával:

  @Override
  public void infoHearPlayer(double direction, String message) {

    hallomATarsat = true;
    directionHallom = direction;
    messageHallom = message;

    logger.info("HALLOM <- "
            + getPlayer().getNumber()
            + " hang iranya = " + directionHallom
            + " mit mond = " + messageHallom);

  }
                    
[Tipp]Atan házi használatra, 1.0.2

Bedobásnál tudjuk tesztelni a hangot, ám már nem olyan könnyű kirúgatni az ágensekkel a játékszert, hiába dobjuk a vonal mellé, hiszen elsődlegesen immár társnak továbbítanak (de ezért hagytuk meg, hogy a középkezdéskor „Hidegkutit” 10-ből nyolcszor túl erősen továbbítja „Budainak” és máris bedobás következik.)

Azt tapasztaljuk, hogy az Atan a message paramétert nem a várt üzenettel, hanem a protokoll szerint az our vagy opp sztringekkel tölti fel. Ezért némiképpen az Atan forrásaiban is módosítunk. A atan/parser/player/CmdParserPlayer.java az alábbi módosítást hajtjuk végre az else if (jj_2_4(2)) ágban:

  final public void startHearCommand() throws ParseException {
   Token num;Token msg;
    jj_consume_token(NUM);
    jj_consume_token(SEP);
    if (jj_2_2(2)) {
      jj_consume_token(SELF);
    } else if (jj_2_3(2)) {
      jj_consume_token(REFEREE);
      jj_consume_token(SEP);
      hearReferee();
    } else if (jj_2_4(2)) {
      num = jj_consume_token(NUM);
      jj_consume_token(SEP);
      jj_consume_token(NAM);
      jj_consume_token(SEP);
      jj_consume_token(NUM);
      jj_consume_token(SEP);
      msg = jj_consume_token(NAM);
        Double dNum = new Double(num.image);
        controller.infoHearPlayer(dNum.doubleValue(), msg.image);
    } else {
      jj_consume_token(-1);
      throw new ParseException();
    }
                    

azaz a hear-t követő idő után nem két, hanem 4 paramétert olvasunk, pontosabban egy számot, ami ugye (a protokoll szerint) a szög, majd jön a szóköz, egy szrtinget átlépünk (NAM) stb. s a valódi üzenetet adjuk át az „érző” metódusnak. (A valóságban ennél szofisztikáltabb módosítás lenne szükséges, de néhány megfelelő tesztre ez is megteszi.)

Mi ezt az apró kiegészítést a házi használatra szánt Atan 1.0.2plb formájában valósítottuk meg. Ne feledd, hogy először majd ezt kell installálnod az alábbi szokásos paranccsal

[norbert@matrica atan-1.0.2plb]$  mvn3 clean generate-sources javacc:jjdoc package site assembly:single install:install-file -Dfile=target/atan-1.0.2plb.jar -DgroupId=atan -DartifactId=atan -Dversion=1.0.2plb -Dpackaging=jar
                  

majd a Golden Team FC projekted pom.xml modelljében állítsd az Atan függőséget az 1.0.2plb-re

        <dependency>
            <groupId>atan</groupId>
            <artifactId>atan</artifactId>
            <version>1.0.2plb</version>
        </dependency>
                    

Ezután (amit addig nyilván nem kell újra ismételni, amig az Atan forrásain nem változtatsz) jöhet a saját projekt élesztése

[norbert@matrica GoldenTeamFC-0.0.2]$ mvn3 clean package assembly:single
                  

majd tesztelése

[norbert@matrica GoldenTeamFC-0.0.2]$ java -jar target/site/GoldenTeamFC-0.0.2-jar-with-dependencies.jar |grep HALLOM
                  

illetve a másik csapat tekintetében az alábbi formában

[norbert@matrica GoldenTeamFC-0.0.2]$ java -jar target/site/GoldenTeamFC-0.0.2-jar-with-dependencies.jar Bcsapat|grep HALLOM
                  

A Golden Team FC 0.0.2 forrásból történő felépítéséről egy videót (screencast hanggal) is készítettünk, amit ide most beágyaztunk, illetve a YouTubeSM videómegosztón és eredeti minőségben a szerző honlapján is elhelyeztünk.

A Golden Team FC 0.0.2 forrásból történő felépítéséről egy videót (screencast hanggal) is készítettünk, amit ide most beágyaztunk, illetve a YouTubeSM videómegosztón és eredeti minőségben a szerző honlapján is elhelyeztünk.

A Golden Team FC 0.0.2 játékáról is van egy (screencast hang nélkül) felvételünk, ami ugyancsak fellelhető YouTubeSM videómegosztón és eredeti minőségben a szerző honlapján.

A Golden Team FC 0.0.2 játékáról (Golden Team FC 0.0.2 - Bcsapat) is van egy (screencast hang nélkül) felvételünk, ami ugyancsak fellelhető YouTubeSM videómegosztón és eredeti minőségben a szerző honlapján.

[Megjegyzés]Atan Sample1 - Golden Team FC 0.0.2, 0:3, illetve Golden Team FC 0.0.2 - Atan Sample1, 3:0

A Golden Team FC 0.0.2 előző önmagával vívott mérkőzése jóval inkább látszik focinak, mint a két most említett, ahol nagyon kiütközik a Mighty Magyars FC-től megfigyelhető gólérzéketlenség: a meddő passzolás.

Legalább viszont azt már elértük, hogy magabiztosan verjük az Atan példa csapatát. A mérkőzések felvételeit itt találja a kedves olvasó: 201111111907-Simple1_0-vs-GoldenFC2_3.rcg, 201111111931-GoldenFC2_3-vs-Simple1_0.rcg.

[Tipp]Ellenőrző kérdés sebesség témában

Milyen hatással van a Golden Team FC 0.0.2 játékára, ha az egyuttElaJatekkal függvényből a fordulási parancsot kikommentezed és csak a dash parancsot hagyod a metódusban?



[2] http://en.wikipedia.org/wiki/Golden_Team, s a további elnevezéseinkben is lelkesen követjük majd az itt említett Mighty, Magical, Magnificent stb. jelzőket.

[3] Vitatott, hogy a mondat elhangzott-e, illetve pontosan hol és milyen összefüggésben, lásd még: http://www.urbanlegends.hu/2010/12/puskas-kis-penz-kis-foci-nagy-penz-nagy-foci/.

8. fejezet - Agent2D alapú csapatok

Ebben a fejezetben megismerkedsz a jegyzetben már számtalan esetben hivatkozott, s nem utolsó sorban 2010-ben világbajnok (2009-ben és 2011-ben második, 2007-ben és 2008-ban harmadik) HELIOS japán csapat gondozásában álló agent2d kliens ágens forrásaival.

  • Robot nem rúghat gólt emberi kapuba!

  • A robotnak engedelmeskednie kell az emberi játékos „HAGYD!” parancsának, ha az nem ütközik az első törvénybe!

  • A robotnak védenie kell a saját kapuját, ha az nem ütközik az első vagy a második törvénybe!

Az agent2d források tárgyalása, avagy szerelem első látásra

Az agent2d kliens ágenssel való ismerkedést a következő, időrendben szedett blog posztok megjelenési idejével datálhatjuk.

Két alapvető úton indulhatunk el a források megismerésében, az egyik szerint funkcionálisan közelítünk azzal, hogy megkeressük a main függvényt és olvassuk mi történik. A másik inkább holisztikus: legeneráljuk a forrásokból a (Doxygen) dokumentációt és azt forgatva ismerkedünk meg az agent2d OO szervezésével (maga a forráscsomag a find . -name '*.h'|xargs grep class parancs tanulsága szerint több, mint 170 osztályból áll). A fent hivatkozott posztoknak megfelelően mindkét úton elindulunk.

Munkamenet az agent2d forrásokkal

A szoftver telepítését tárgyaló részek most is igazak, hiszen az agent2d-re most mindenképpen forrásban lesz szükséged. Viszont elegendő a make parancs kiadásáig elmenni, aztán a src/start.sh-val indítva tesztelni a változtatásokat, ahogyan tettük például a korábbi HELIOS_base - Marvellous Magyars FC mérkőzés esetén.

Ám abban az esetben, ha az agent2d alatti librcsc forrásaiban módosítasz, ott (a librcsc forráskönyvtárában) nyilván tovább kell lépni és a make install paranccsal a telepítést is elvégezni a korábbi tárgyalásnak megfelelően.

Első Atan alapú csapatainknál vettük fel a szokást, hogy a róluk szóló fejezetcímeket a HELIOS_base csapattal vívott mérkőzések eredményével színesítjük[4]. Most feltünhet, hogy a japán alapcsapat felállása ott most más, s igen azt már módosítottuk, ám ehhez nem kellett a forrásokhoz nyúlnunk, hanem elegendő volt a megfelelő konfigurációs állomány szerkesztése.

Középkezdés

Ami ebben az alábbi esetben a src/formations-dt/before-kick-off.conf állomány

Formation Static
# ---------------------------------------------------------
# move positions when playmode is BeforeKickOff or AfterGoal.
1 Goalie     -49.0   0
2 CenterBack -20.0  -8.0
3 CenterBack -20.0   8.0
4 SideBack   -18.0 -18.0
5 SideBack   -18.0  18.0
6 DefensiveHalf -15.0   0
7 OffensiveHalf 0 -12.0
8 OffensiveHalf 0  12.0
9  SideForward  10.0 -22.0
10 SideForward 10.0  22.0
11 CenterForward 10   0
# ---------------------------------------------------------
                

A main függvény

A main függvényt a src/main_player.cpp http://en.sourceforge.jp/projects/rctools/downloads/51943/agent2d-3.1.0.tar.gz/ állományban találjuk.

int
main( int argc, char **argv )
{
    struct sigaction sig_action;
    sig_action.sa_handler = &sig_exit_handle;
    sig_action.sa_flags = 0;
    if ( sigaction( SIGINT, &sig_action , NULL ) != 0
         || sigaction( SIGTERM, &sig_action , NULL ) != 0
         || sigaction( SIGHUP, &sig_action , NULL ) != 0 )
        /*if ( signal(SIGINT, &sigExitHandle) == SIG_ERR
          || signal(SIGTERM, &sigExitHandle) == SIG_ERR
          || signal(SIGHUP, &sigExitHandle) == SIG_ERR )*/
    {
        std::cerr << __FILE__ << ": " << __LINE__
                  << ": could not set signal handler: "
                  << std::strerror( errno ) << std::endl;
        std::exit( EXIT_FAILURE );
    }
                

ahol POSIX-es jelkezelést használva néhány jel kezeléséhez (például a SIGINT jelhez, azaz ha Ctrl+C kombót nyomsz a konzolon, amin indítottad) beállítja a felette definiált sig_exit_handle függvényt. (Kommentben a korábbi, System V - BSD jellegű jelkezeléssel szerepel ugyanez.)

Következik egy rcsc::BasicClient osztálybeli objektum példányosítása:

    rcsc::BasicClient client;

    if ( ! agent.init( &client, argc, argv ) )
    {
        return EXIT_FAILURE;
    }
                

az rcsc::BasicClient osztály az RCSS szerverrel való UDP kapcsolatot absztrahálja. Az objektumot a parancssorral együtt adjuk át a SamplePlayer osztálybeli agent objektum inicializálásához. A SamplePlayer osztály egy rcsc::PlayerAgent, ami pedig egy rcsc::SoccerAgent osztály. A rcsc::SoccerAgent absztrakt ősnek van egy védett BasicClient tagja, ezt állíja be a most hívott (egyetlen nem virtuális) init függvénye.

[Tipp]Laborkártya

A Magas szintű programozási nyelvek 1 kurzus hallgatónak már közeli ismerős a SoccerAgent osztály, hiszen a 6. előadás laborkártyája éppen ez volt, nevezetesen ami azt mutatja be, hogy az osztály hogyan tiltja le a másoló konstruktort és a másoló értékadást.

Folytatva a main testének átolvasását, nagyon előzékenyen, nyilván számítva rá, hogy számos leendő csapat majd ezekben a forrásokban találja meg a megváltóját, segítenek névjegyünk megadásában (és természetesen az Övék megtartásában)

    /*
      You should add your copyright message here.
     */
    // std::cout << "*****************************************************************\n"
    //           << " This program is modified by <Team Name>\n"
    //           << " Copyright 20xx. <Your name>.\n"
    //           << " <Affiliation>\n"
    //           << " All rights reserved.\n"
    //           << "*****************************************************************\n"
    //           << std::flush;

    /*
      Do NOT remove the following copyright notice!
     */
    std::cout << "*****************************************************************\n"
              << " This program is based on agent2d created by Hidehisa Akiyama.\n"
              << " Copyright 2006 - 2011. Hidehisa Akiyama and Hiroki Shimora.\n"
              << " All rights reserved.\n"
              << "*****************************************************************\n"
              << std::flush;
                

ennek megfelelően, például az eggyel korábbi (3.0.0) verzió esetén mi így jártunk el:

    /*
      You should add your copyright message here.
     */
     std::cout << "*****************************************************************\n"
               << " This program is modified by FerSML team\n"
               << " Copyright 2011. Norbert Bátfai.\n"
               << " University of Debrecen\n"
               << " All rights reserved.\n"
               << "*****************************************************************\n"
               << std::flush;

    /*
      Do NOT remove the following copyright notice!
     */
    std::cout << "*****************************************************************\n"
              << " This program is based on agent2d created by Hidehisa Akiyama.\n"
              << " Copyright 2006 - 2010. Hidehisa Akiyama.\n"
              << " National Institute of Advanced Industrial Science and Technology\n"
              << " All rights reserved.\n"
              << "*****************************************************************\n"
              << std::flush;
                

de visszatérve a 3.1.0 tárgyalásához, indítja az elkészített klienst:

    client.run( &agent );

    return EXIT_SUCCESS;
}
                

amivel a vezérlés a librcsc-4.1.0/rcsc/common/basic_client.cpp-ben olvasható void BasicClient::runOnline( SoccerAgent * agent ) függvényébe kerül, ahol a select-el megvalósított multiplexelt IO-t találjuk az RCSS szerverrel.

    FD_ZERO( &read_fds );
    FD_SET( M_socket->fd(), &read_fds );
    read_fds_back = read_fds;

    int timeout_count = 0;
    long waited_msec = 0;

    while ( isServerAlive() )
    {
        read_fds = read_fds_back;
        interval.tv_sec = M_interval_msec / 1000;
        interval.tv_usec = ( M_interval_msec % 1000 ) * 1000;

        int ret = ::select( M_socket->fd() + 1, &read_fds,
                            static_cast< fd_set * >( 0 ),
                            static_cast< fd_set * >( 0 ),
                            &interval );
        if ( ret < 0 )
        {
            perror( "select" );
            break;
        }
        else if ( ret == 0 )
        {
            // no meesage. timeout.
            waited_msec += M_interval_msec;
            ++timeout_count;
            agent->handleTimeout( timeout_count,
                                  waited_msec );
        }
        else
        {
            // received message, reset wait time
            waited_msec = 0;
            timeout_count = 0;
            agent->handleMessage();
        }
                

Az olvasott adatokkal az agent rcsc::SoccerAgent objektum foglalkozik majd.

[Tipp]select

Ugyancsak a Magas szintű programozási nyelvek 1 kurzusban tárgyaltuk a select rendszerhívással megvalósított multiplexelt IO-t. S ennek megfelelően a http://fersml.blog.hu/2011/01/05/ismerkedes_a_japan_helios_csapat_szoftvereivel_avagy_nehany_trivialis_hello_vilag posztban az iménti kódcsipet else ágát kiegészítettük azzal, hogy az FD_ISSET makró hívásával megkérdeztük, hogy az RCSS szerverrel felépített UDP kommunikációs végpont leírója benne van-e a halmazban (ami most triviálisan benne van, hiszen csak ezt az egy leírót figyeljük)

        else
        {
            if (FD_ISSET(M_socket->fd(), &read_fds)) {
                // received message, reset wait time
                waited_msec = 0;
                timeout_count = 0;
                agent->handleMessage();
            }
        }
                

Az rcsc::BasicClient osztály

Az BasicClient osztály az RCSS szerverrel való kapcsolatot valósítja meg. A tagként tartalmazott M_socket smart pointer tagja az UDP kommunikációs végpontot absztrahálja

    boost::shared_ptr< UDPSocket > M_socket;
                

az osztály definícióját a librcsc-4.1.0/rcsc/common/basic_client.cpp állomány tartalmazza.

Az BasicClient osztály run függvényében fut a szerverrel a multiplexelt IO, minek során az olvasott üzeneteket a run-nak paraméterül adott rcsc::SoccerAgent osztálybeli objektum kapja meg annak polimorf handleMessage üzenetének meghívásával.

Az agent2d OO struktúrája

Jóval könnyebben ismerkedhetünk a forrásokkal, ha azokat egy a forráskódböngészést támogató IDE-ben olvassuk, például a KDevelop-ban, vagy legeneráljuk az agent2d és librcsc forráskódok dokumentációját a Doxygen-el.

8.1. ábra - Az agent2d és a librcsc a Doxygen HTML kimenetének böngészése.

Az agent2d és a librcsc a Doxygen HTML kimenetének böngészése.

Az rcsc::SoccerAgent osztály

A rcsc::SoccerAgent osztály a játékos ágensek ősosztálya. Ennek leszármazottját, az agent2d játékos ágensét megvalósító SamplePlayer osztálybeli agent objektumot kapcsoltunk be a szimulációba játékos ágensként a BasicClient osztály run módszerének aktuális paramétereként a main_player.cpp forrás main indító függvényében.

8.2. ábra - A rcsc::SoccerAgent osztály leszármazási fája a Doxygen PDF kimenetében.

A rcsc::SoccerAgent osztály leszármazási fája a Doxygen PDF kimenetében.

[Tipp]Folytatás a Magas szintű programozási nyelvek 1 kurzusban

A jegyzet elsődleges célja a Magas szintű programozási nyelvek 2 kurzus támogatása volt, ami célplatformját tekintve leginkább Java. Továbbá a jegyzet írásának és az említett kurzus félévének az időbeli metszete nem volt üres. Ezért a C++ alapú csapatok fejlesztésével a Magas szintű programozási nyelvek 1 kurzusban tervezzük hasonló mélységben foglalkozni, mint ahogyan a jelen jegyzetben a Java alapú csapatokkal tettük. Ha lehetőségeink engedik, akkor éppen egy ilyen jellegű jegyzetben, aminek most éppen a végére ért a kedves olvasó.



[4] Utólag ezen módosítottunk és a fejezetcím „névadó mérkőzéseket” az érintetlen HELIOS_base csapattal játszottuk le.

Irodalomjegyzék

Idézetek

[SEBES] Sebes, Gusztáv. A magyar labdarúgás. Sport Lap- és Könyvkiadó . 1955.

[KITANO] Kitano, Hiroaki, Asada, Minoru, Kuniyoshi, Yasuo, Noda, Itsuki, és Osawa, Eiichi. RoboCup: The Robot World Cup Initiative. ACM, Proceedings of the first international conference on Autonomous agents. AGENTS '97. 340-347. 1997. ACM, Proceedings of the first international conference on Autonomous agents http://dl.acm.org/citation.cfm?doid=267658.267738 . 1997.

[KENNEDY] Special Message to the Congress on Urgent National Needs, May 25, 1961. John F. Kennedy Presidential Library & Museum Special Message to the Congress on Urgent National Needs, May 25, 1961 . 2007.

[HACKERHOWTO] Raymond, Eric Steven. How To Become A Hacker/Hogyan lesz az emberből Hacker (fordította Kovács Emese). http://catb.org/~esr/faqs/hacker-howto.html http://esr.fsf.hu/hacker-howto.html . 2001.

[KENNEDY] Special Message to the Congress on Urgent National Needs, May 25, 1961. John F. Kennedy Presidential Library & Museum Special Message to the Congress on Urgent National Needs, May 25, 1961 . 2007.

[METAMATH] Chaitin, Gregory. META MATH! The Quest for Omega. http://arxiv.org/PS_cache/math/pdf/0404/0404335v7.pdf . 2004.

[RIS20] Robotics Invention System 2.0. http://mindstorms.lego.com . 1999.

[HERCZEG] Herczeg András: Több volt ebben a mérkőzésben. http://www.dvsc.hu/Lapok/hirek_hud.aspx?id=4187 . 2010.

[KERNIGHANPLAUGER] Kernighan, Brian W. és Plauger, P. J.. A programozás magasiskolája. Műszaki. 1982.

RoboCup

[KALYANAKRISHNAN] Kalyanakrishnan, Shivaram, Hester, Todd, Quinlan, Michael, Bentor, Yinon, és Stone, Peter. Three Humanoid Soccer Platforms: Comparison and Synthesis. Springer, Lecture Notes in Computer Science, RoboCup. 5949. 140-152. 2009. Springer, Lecture Notes in Computer Science, RoboCup http://www.springerlink.com/content/x54m159261802767/ . 1997.

[HELIOS] Akiyama, Hidehisa és Shimora, Hiroki. HELIOS2010 Team Description. http://julia.ist.tugraz.at/robocup2010/tdps/2D_TDP_HELIOS.pdf . 2010.

[EDINFERNO2D] Hawasly, Majd és Ramamoorthy, Subramanian. EdInferno.2D Team Description Paper for RoboCup 2011 2D Soccer Simulation League. http://wcms.inf.ed.ac.uk/ipab/robocup/research/TDP-Edinferno2D.pdf . 2011.

[PARANOID] Khodabakhshi, Vahid, Mesri, Mojtaba, KeyhaniRad, Navid, és Zolanvar, Hosein. ParaNoid 2D Soccer Simulation Team Description Paper 2011. 2011.

[NADCO2D] Sadeghi Marasht, Mohammad Ali. NADCO-2D Soccer 2D Simulation Team Description Paper 2011. http://robolab.cse.unsw.edu.au/conferences/RoboCup-2011/TDPs/Soccer/Simulation/2d/S2D_NADCO-2D_TDP.pdf . 2011.

[OXSY] Marian, Sebastian, Luca, Dorin, Sarac, Bogdan, és Cotarlea, Ovidiu. OXSY 2009 Team Description. http://romeo.ist.tugraz.at/robocup2009/tdps/oxsy-rc09-tdp.pdf . 2009.

[AUA2D] Tao, Lei és Zhang, Runmei. AUA2D Soccer Simulation Team Description Paper for RoboCup 2011. http://mephisto.ist.tugraz.at/storage/104280b00fc97e0a88e79b36052499ea_AUA2D.pdf . 2011.

[PHOTON] Barati, Morteza, Hakimi, Zahra, és Javadi, Amir Homayoun. Photon 2D Soccer Simulation Team Description Paper. https://sites.google.com/site/ahjavadi/Attachments/Photon%2CTPD.pdf?attredirects=0 . 2011.

[RCSSMANUAL] Chen, Mao, Dorer, Klaus, Foroughi, Ehsan, Heintz, Fredrik, Huang, ZhanXiang, Kapetanakis, Spiros, Kostiadis, Kostas, Kummeneje, Johan, Murray, Jan, Noda, Itsuki, Obst, Oliver, Riley, Pat, Steffens, Timo, Wang, Yi, és Yin, Xiang. Users Manual RoboCup Soccer Server for Soccer Server Version 7.07 and later. https://sourceforge.net/projects/sserver/files/rcssmanual/ . 2003.

[ATAN] James, Nick és Wagner, Wolfgang. Atan. sourceforge.net/projects/atan1 http://atan1.sourceforge.net . 2011.

Futball

[FERSML] Bátfai, Norbert. Footballer and Football Simulation Markup Language and related Simulation Software Development. Journal of Computer Science and Control Systems. III/1. 13-18. 2010. Journal of Computer Science and Control Systems http://electroinf.uoradea.ro/reviste%20CSCS/volumes/JCSCS_Nr_1_integral.pdf . 2010.

[PRFC] Bátfai, Norbert és Bátfai, Erika. Public Resource Computing in European Football. (submitted). . . 2010. (submitted) . 2010.

[BATFAI] Bátfai, Norbert. Bevezető számítások a labdarúgás szimulációs jelölőnyelv kialakításához. Híradástechnika. LXV: 5-6. 16-20. 2010. Híradástechnika http://www.hiradastechnika.hu/data/upload/file/2010/2010_05_06/HT2010_5_6.pdf . 2010.

[DEIKFOCI] Bátfai, Norbert, Ispány, Márton, Jeszenszky, Péter, Széll, Sándor, és Vaskó, Gábor. A Debreceni Egyetem labdarúgást szimuláló szemináriuma. Híradástechnika. 66/1. 32-36. 2011. Híradástechnika http://www.hiradastechnika.hu/data/upload/file/2011/2011_01_01magyar/batfain.pdf . 2011.

[FIFA] Blatter, Joseph S. és Valcke, Jérôme. Laws of the Game. Fédération Internationale de Football Association http://www.fifa.com/worldfootball/lawsofthegame . 2011.

[BOLLING] Ekblom (editor), Bjorn. Football (Soccer) Handbook of Sports Medicine and Science. 0 632 03328 2. Blackwell Scientific Publications . 1994.

Programozás

[NEHOGY] Bátfai, Norbert. Nehogy már a mobilod nyomkodjon Téged!. 978 963 473 094 1. Debrecen, DEENK http://www.eurosmobil.hu/NehogyMar . 2008.

[NEHOGYMEGINT] Bátfai, Norbert. Nehogy már megint a mobilod nyomkodjon Téged!. Kempelen Farkas Digitális Felsőoktatási Tankönyvtár http://www.tankonyvtar.hu . 2011.

[PP] Bátfai, Norbert. Programozó Páternoszter. http://www.inf.unideb.hu/~nbatfai/ProgramozoPaternoszter.pdf . 2007.

[JAVATTANITOK] Bátfai, Norbert és Juhász, István. Javát tanítok. Bevezetés a programozásba a Turing gépektől a CORBA technológiáig. Kempelen Farkas Digitális Felsőoktatási Tankönyvtár http://www.tankonyvtar.hu/site/upload/pdf/b10108.pdf http://www.tankonyvtar.hu/informatika/javat-tanitok-javat-080904 . 2007.

[KATEDRALIS] Raymond, Eric S.. The Cathedral and the Bazaar. O'Reilly Media http://magyar-irodalom.elte.hu/robert/szovegek/bazar/ magyar fordítás: http://magyar-irodalom.elte.hu/robert/szovegek/bazar/ . 1999.

MI, gépi tudatosság

[RUSSELNORVIG] Russell, Stuart J. és Norvig, Peter. MESTERSÉGES INTELLIGENCIA MODERN MEGKÖZELÍTÉSBEN. PANEM, Budapest . 2005.

[BROOKS] Brooks, Rodney A.. Intelligence without representation. Artificial Intelligence. 47. 139-159. 1991. Artificial Intelligence people.csail.mit.edu/brooks/papers/representation.pdf . 1991.

[COP] Bátfai, Norbert. Conscious Machines and Consciousness Oriented Programming. http://arxiv.org/abs/1108.2865 . 2011.

[AGENSKOMLODI1] Kömlődi, Ferenc. Ágensalapú technológiák. Égen-földön informatika. 434-460. 2008. Dömölki, Bálint. TYPOTEX http://www.nhit-it3.hu/images/tagandpublish/Files/it3-2-1-12-u.pdf . 2008.

[AGENSKOMLODI2] Kömlődi, Ferenc. Autonóm mobil robotok. Égen-földön informatika. 588-613. 2008. Dömölki, Bálint. TYPOTEX http://www.nhit-it3.hu/images/tagandpublish/Files/it3-2-2-4-u.pdf . 2008.

Gyerekeknek

[JAVACSKA] Bátfai, Erika és Bátfai, Norbert. Fantasztikus programozás. Debreceni Egyetem Egyetemi és Nemzeti Könyvtár http://javacska.lib.unideb.hu/konyv/bv-naploja-kezirat-I-5_0_0.pdf . 2004.

Filmek

[GOAL] Cannon, Danny. Goal!. http://www.imdb.com/title/tt0380389/ . 2005.

[GOALII] Collet-Serra, Jaume. Goal II: Living the Dream. http://www.imdb.com/title/tt0473360/ . 2007.

[MATRIX] Wachowski, Larry és Wachowski, Andy. The Matrix. http://www.imsdb.com/scripts/Matrix,-The.html . 1996.

[TERMINATOR] Cameron, James. T E R M I N A T O R. http://www.imdb.com/title/tt0088247/ http://www.imsdb.com/scripts/Terminator.html . 1984.