Belépés »

Menü

Korábbi

reklámfejléc
Tavaly

Részletes példa

Az alábbiakban az általunk feltöltött példaprogram dokumnetációját találjátok, azzal a plusz információval kiegészítve, hogy hogyan tudjátok ezt más nyelvekre is lefordítani (ha valaki nem C++-ban szeretné írni a kódot).

 

Update 1: Az egyik versenyző hívta fel a figyelmünket egy hibára, miszerint, ha a Protocol Buffer által generált string tartalmazza a 0x0A bájtot (sorvége jel), akkor a getline függvény csak addig olvas, így nem az egész stringet adja vissza. Ennek javítására módosítottuk a kommunikációt, hogy először egy számot küldjön a szerver program, amely megadja hogy hány karakter lesz a hasznos üzenet, majd ezután kerül elküldésre a Protocol Buffer által generált string. Ez a szám sima szövegként kerül elküldésre, és az üzenetet egy sorvége jel zárja, ahogy a Protocol Buffer által generált stringet is. Javításra kerül továbbá egy másik hiba is, így most már a játékos HP-jának a valós értéke kerül elküldésre ellentétben az eddigi, 100-al megnövelttel. A módosításokat beletettük a kiadott minta kliens program forráskódjába is.


Amint azt a feladatkiírásban olvashattátok, a játék lényege minél tovább életben maradni a pályán, kajacsomagokat gyűjtögetve.


Kommunikáció a program és a szerver közt:


A program a futása során folyamatos kapcsolatban van a szerverrel, ami a ti gépeteken fut. A tesztelés során a szerver programot kell elindítani, ami meghívja a (konfig fájlban meghatározott fájl névvel) játékos programot. Ezután körönként elküldi neki a játék aktuális állását a standard bemenetre - a program erre egy iránnyal kell hogy válaszoljon, amerre lépi szeretne.


A kommunikáció a Google Protocol Buffere (továbbiakban PB) segítségével történik, ami annyit tesz, hogy minden kör elején a program standard bemenetről (std::cin) beolvas egy objektumot string formában, visszaalakítja objektummá, és ennek getter függvényeit használva megkapja a szükséges adatokat (úgy mint pályaméret, falak és kajacsomagok pozíciói, játékos pozíciója). Ezeket a lépéseket C++-ban már megírtuk nektek, más nyelv esetében segítséget jelenthet a példaprogram átnézése és a Protocoll Buffer dokumentációjának átolvasása.


Ez a beolvasás a következőképpen zajlik:

  1. A program beolvas std::cin-ről egy string-et, amiben az adatok vannak (a PB által meghatározott formában). Itt egyetlen dolgot érdemes kiemelni: a beolvasás getline használatával kell történjen, különben az első whitespace karakternél megáll, és dekódolási hibát kapunk.

  2. Ezt az üzenetet a PB által generált Message osztály parseFromString függvénye képes “értelmes” formára hozni: ennek meghívása után a Message osztály getter függvényeit használva kinyerhetőek az adatok.
    Hogy hogyan is zajlik ez az átalakítás, megtaláljátok a PB dokumentációjában, vagy megnézhetitek a példaprogram State osztályában (amit mi írtunk, és elvégzi nektek ezeket a lépéseket).
    Így a második lépés röviden: meg kell hívni a State osztály readFromString függvényét az előző lépésben beolvasott string változóra. A függvény egy boolean értékkel tér vissza, ennek értéke true, ha az átalakítás sikeres volt. Ezt érdemes ellenőrizni, mert ha ez a lépés hibás, akkor nem fogjátok elérni a szükséges adatokat.

  3. Adatok tényleges kinyerése: a State osztálynak getter függvényivel történik, ezek:

    1. getMapWidth() és getMapHeight(): egy unsigned int-et térít vissza, amiben a pálya szélessége és hosszúsága van.

    2. getMap() - egy unsigned int-ből álló std::vectort ad vissza, ebben található sorfolytonosan a pályát leíró mátrix.

    3. getPlayerID - megadja, hogy hányas kód jelenti a pályán a játékosunkat.

    4. getPlayerHP - megadja, hogy hány HP-nk van jelenleg.

A pályát leíró mátrixban a következő számok fordulhatnak elő:

  1. 0 - ez üres mezőt jelent, ide tudtok lépni

  2. 1 - ez fal, ide nem lehet lépni. Ha a program mégis megpróbál, a szerver figyelmen kívül hagyja az utasítást, azaz a játékos ott marad ahol volt. Erről külön hibaüzenetet nem kaptok, de a program ellenőrizheti, hogy a kért lépés szerint változott-e a pozíciója vagy sem.

  3. 11-99 - ezek a mezők kajacsomagot tartalmaznak, amit a játékos a mezőre lépve tud összeszedni. Ekkor a játékos megkapja a csomag értékének megfelelő mennyiségű HP-t, ez a mező kódszáma mínusz tíz (vagyis pl. a 15-ös számot tartalmazó mezőn egy 15-10=5 HP értékű kajacsomag található).

  4. 1000-nél nagyobb számok - ezek a játékosokat jelentik. Az első fordulóban csak egy játékos (a tietek) lesz a pályán, vagyis egyetlen 1000-nél nagyobb számra lehet számítani. Azt, hogy melyik ilyen szám jelenti a ti játékosotokat, a getPlayerID függvénnyel deríthetitek ki.


A fenti lépések szépen láthatóak a letölthető példaprogramban. Ha ennek ellenére gondotok akad az adatok beolvasásával, dekódolásával, használatával, nyugodtan keressetek meg minket a megadott email címen vagy facebook oldalon.


Aki nem C++-t szeretne használni, a Protocoll Buffer más nyelvre történő lefordításához szükséges fájlt megtalálja a letöltések menüpontban.


Miután az adatokat sikerült beolvasni, a ti programotok feladata lesz kiszámítani, hogy merre érdemes lépni, és ezt PB-n keresztül közölni a szerverrel.

A válasz küldéséhez a PB generált egy AnswerMessage nevű osztályt, amit mi felturbóztunk kicsit: így a ti feladatotok csupán két függvény meghívása. Első lépésben a setWay() függvényt kell meghívnotok a lehetséges irányok (NOWHERE, LEFT, RIGHT, UP, DOWN) valamelyikére, ezután ki kell íratnotok a standard kimenetre (std::cout) az osztály serialize() függvénye által visszatérített string-et.


Mivel elképzelhető, hogy a program fejlesztése során szükségetek lesz különböző (hiba)üzenetek kiíratására, hogy lássátok, hogy mi is történik éppen, létrehoztunk nektek egy LogWriter nevű osztályt, aminek segítségével logfájlkat tudtok majd készíteni a program futása során. Ennek működése roppant egyszerű: a LogWriter::log(“üzenet”) függvényhívással tudtok írni a player.log nevű szöveges fájlba. Ez a függvény a programotok tetszőleges pontjáról hívható, így kiírathattok hibaüzenetet, pályaállást, lépési kísérletet - bármit, amire szükségetek van. A player.log fájl minden futtatás során felülírásra kerül, így ha fontosnak tartotok egy log-ot, érdemes azt átnevezni, vagy áthelyezni egy másik mappába.


FONTOS, hogy a programok közti kommunikáció miatt a standard kimenet, (beleértve a hibák számára fenntartott std::cerr-t is) nem használható. Ha a kimenetek bármelyikére az AnswerMessage::serialize() kivételével mást is kiírattok, elrontjátok a kommunikációs csatornát, és a szerver nem lesz képes értelmezni a programotok válaszát - arról nem beszélve, hogy az átirányított standard kimenet miatt a konzolon semmi nem fog megjelenni. A szerverprogram védett ugyan az ilyen hibákkal szemben, de a ti programotok jó eséllyel lefagy egy ilyen kiíratási próbálkozás után. Tanulság: ha írni kell, használd a LogWriter-t.


A letölthető példaprogram kódja részletesen kommentezve van, de azért itt még egyszer összefoglaljuk, hogy mit is csinál:

  1. indít egy while ciklust, ami addig fut, amíg az std::cin-en kap adatot a program.

  2. beolvassa az msg (messagge, üzenet) nevű string-et az std::cin-ről

  3. a state osztály readFromString függvényével dekódolja a kapott információt

  4. a state osztály getter függvényit használva kiolvassa az információt, majd a LogWriter használatával kiírja a player.log fájlba (ha még nem volt ilyen, futtatás után ez megjelenik az .exe fájlt tartalmazó mappában). A számok kiíratásához a logwriter to_string nevű segédfüggvényét is felhasználtuk, ez képes integer értékeket szöveggé alakítani.

  5. Létrehoz egy AnswerMessage példányt, konstruktorban megadja a kör elején kapott state-et (ezt a szerver a kommunikáció ellenőrzésére használja, ha nem azt a játékos ID-t kapja vissza amit küldött, akkor hibát jelez). Mivel ez a state osztályban privát mező, véletlenül nem nagyon lehet elrontani - szándékosan meg nem érdemes :)

  6. a program ezután beállítja a lépésirányt NOWHERE-re, azaz jelzi a szerver felé, hogy ebben a körben ott kíván maradni, ahol volt - ettől még a HP-ja fogy, de nyilván nem az volt a célunk, hogy sokáig élő kódot adjunk ki: ez már a ti feladatotok ;)

  7. kiírja az std::cout-ra az answermessage::serialize() által visszaadott stringet, ezzel elküldi a választ a szervernek.


Megjegyzés: az std::cin és std::cout működési módja miatt érdemes beolvasás után törölni a bemenetet, válaszadás után pedig flush-elni a kimenetet, így biztosítva, hogy tényleg a teljes üzenetet (és csak az üzenetet) olvassuk, illetve a választ ténylegesen és egészben küldjük el a szerver felé. Ezek a műveletek (a példakódban látható módon) az std::cin.clear() és az std::cout.flush() függvényhívásokkal, más nyelvekben az ennek megfelelő parancsokkal hajthatóak végre.


Beadáskor amit várunk tőletek, az a programotok teljes forráskódja (projekt fájl, felhasznált lib-ek, minden, ami a fordításhoz és linkeléshez szükséges), továbbá egy ebből összerakott működőképes .exe, és némi dokumentáció, amiben nagyvonalakban le van írva a program működése, és a stratégia, ami alapján kajacsomagokra vadászik (pl. mindig a legközelebbi, mindig a legnagyobb hp értékű, stb.)


Mi a példa program megírásakor az Eclipse Mars IDE-t használtuk, a projekt ebbe könnyedén importálható, fordítható és futtatható. Más IDE, mint pl. CodeBlocks, MS Visual Studio, QtCreator, stb. használata esetén a PB-hez tartozó fájlok fordítása a ti feladatotok lesz - ha nem tudjátok, hogyan kell ezt helyesen beállítgatni, ajánlott az Eclipse vagy olyan IDE használata, ami képes Eclipse projekteket importálni.


A kész programokat Microsoft Windows 7 alatt fogjuk futtatni, más operációs rendszer használatára technikai okokból nincs lehetőség (azaz nem tudunk elfogadni olyan megoldást, ami pl. csak Linux vagy OS X alatt fut).

 

Bármilyen kísérletet a teszteredmények befolyásolására (mint pl. a szerverprogram futásának piszkálása, a gép vagy kritikus rendszerfolyamatok kikapcsolása, memória szándékos feltöltése, stb.) még akkor is ha nem sikerül,  a versenyből való kizárást eredményezheti.

PPKE Információs Technológiai Kar
H-1083 Budapest, Práter utca 50/a.
Az ITK a Google Maps-en
Tel: (36-1) 886-4700
E-mail: progverseny@itk.ppke.hu

Szponzoraink:

PCWorld     web4     GameStar     Morgan Stanley     IVSZ     NJSZT     IBControll     ITCafé      Nemzeti Fejlesztési Minisztérium