XY mérnök-fizikus
Amőba Felhasználói dokumentáció A játék célja: 5 azonos jel elhelyezése a táblán egymás után. Menürendszer: A játék lehetőséget kínál egy vagy két játékos módú felhasználásra. Egy játékos módban a felhasználónak a géppel kell összemérnie tudását. Ezt az opciót választva eldöntheti, hogy kívánja-e kezdeni a játékot, vagy ezt átadja a gépnek. A kezdő játékoshoz mindig az ’x’ tartozik, ezt nem lehet megváltoztatni. A két játékos mód csupán a papír és ceruza hiányát hivatott helyettesíteni. A kezdés jogának eldöntése a felhasználókra van bízva. A játéktábla A rendelkezésre álló játéktér 25*20-as. A programnak a feltüntetett koordináták szerint kell megadni a következő lépés helyét. A betű és a szám sorrendje lényegtelen. E5 ugyan úgy helyes, mint az 5E. A gép elleni játékmódban a szögletes zárójel szimbólumok között feltüntetett koordináta a gép korábbi lépését mutatja ( pl.: [F8] ). (FIGYELEM: A program elfogad bármilyen hosszú bemenő koordinátát, amiből az első 3 hosszúságút veszi figyelembe! Ha pedig 2 betűt adtunk bemenetnek, akkor az utolsót veszi érvényesnek!) Játék vége, félbeszakítása: A játék véget ér, ha valamelyik fél győzött. Ekkor már új jel nem helyezhető el a táblán. A program kiírja a győztes jelét, vár néhány másodpercet, és visszatér a menübe. Ha a felhasználó félbe kívánja hagyni a játékot, akkor a ’00’ koordinátát kell beütnie. Ekkor a program visszatér a főmenübe, és azonnal felkínálja új játék kezdését, vagy az előző folytatását. Ha a tábla betelik, és a gép már nem tud lépni, akkor a ’Tábla betelt’ üzenettel figyelmeztet, és várja, hogy a felhasználó kilépjen. Mentés és visszatöltés: A játék elmentése mindkét módban automatikusan zajlik. Ezért több játékállás elmentésére nincs lehetőség. Ha a program észleli, hogy van félbehagyott játék, akkor felkínálja a folytatás lehetőségét a főmenüben.
Fejlesztői dokumentáció A program leírása: A programmal amőba játékot lehet játszani a hagyományos amőba szabályai szerint (5 azonos jel egymás után=győzelem). A tábla mérete a szöveges képernyő korlátai miatt 24x20-as. A program lehetőséget kínál 1 illetve 2 játékos módra. 1 játékos módban a felhasználó megválaszthatja, hogy kezdő szeretne-e lenni vagy sem. A gépi ellenfél lépéseit az aktuális táblát elemezve, a veszélyes illetve kedvező mezőket pontozva határozza meg. A program minden forduló(2 játékos módban minden lépés) után automatikusan elmenti az aktuális állást. A mentésnél, hogy mentett játékot ne lehessen egyszerűen manipulálni egyszerű kódolást használ. 1
Ezzel egyben lerövidíti a fájl hosszát is. A félbehagyott játékok a program automatikusan érzékeli, és ekkor a menüben a program újbóli indításakor lehetőséget kínál a folytatásra. Egyszerre azonban csak egy játékot tarthatunk függőben, új játék kezdésénél a félbehagyott törlődik. Fontosabb megoldások Kockázat vizsgálat: Az egyes mezőkre vonatkozó kockázatokat, a mezőben karakteresen tárolt számok 0-9-ig. A páros számok a támadás szempontjából kedvező mezőket, a páratlanok a veszélyeseket jelzik. A program csak sorban vizsgálódik, az olyan helyzeteket mint pl a rombusz alakban elhelyezett 4 jel, hogy felismeri a sorban lévő alakzatot. Ha ilyenből többet talál(egymással szöget bezáró sorban), akkor magasabb prioritást ad a mezőnek. Az alacsonyabb szám magasabb prioritást rejt. A veszélyesnek illetve kedvezőnek ítélt kombinációk a következők Mentés kódolása: A kódolás elsődleges célja, hogy ne lehessen egyszerűen módosítani az elmentett fájlt. Mivel a táblát tartalmazó tömbben csak 3 féle mentés szempontjából jelentőséggel bíró karakter van, ezért tekinthetjük 3-as számrendszerbeli számoknak. A kódolás 4 helyiérték hosszú számonként történik. A tömb kisebb indexénél lévő helyiérték az alacsonyabb. Ezeket a számokat 10-es számrendszerbe váltja át, és a ’/’ karaktertől (mint 0tól) felelteti meg őket egy-egy karakternek. Majd ezeket balról jobbra és föntről lefelé (0,0 koordináta értendő fönt,bal-nak) egymás után a fájlba írja. Véletlenszerű lecserélés(lépés kiválasztásánál): A módszer használatát az teszi szükségessé, hogy azonos helyzetekben a program ne mindig ugyan azt a megoldást, vagyis az utolsó legjobbat válassza. Ahhoz, hogy egyszerű véletlen szám generálással válasszunk ki a jók közül egyet, azokat külön tömbben kellene tárolnunk, és ha például találtunk magasabb prioritásút, akkor az egész tömböt törölni, és újra kezdeni e gyűjtögetést. Ez sok adatmozgatást igényel. Éppen ezért a program a következő algoritmust használja: Tegyük föl, hogy megtaláltuk az első olyan mezőt, aminek legnagyobb prioritása van. Tovább keresünk, és találunk még egy ilyet. Ekkor a program véletlen számot generál és ½ -ed valószínűséggel lecseréli a régi mezőt. Ha azonban cserére nem kerül sor, akkor úgy szeretnénk megállapítani a lecserélés valószínűségét, hogy az aktuális elem megtalálása óta nem bekövetkezett csere(cserék) ellenére most is ugyan annyi esélye legyen mindkét mezőnek, hogy végül ő maradjon a kiválasztott. Ezt a következő képen érjük el: Az első csere lehetőségnél ½ -ed esélye volt a most aktuális mezőnek, hogy maradjon, ezért a mostani cserénél ½ *x lesz az esélye a maradásra. Mivel egyenlő esélyeket szeretnénk, ezért ½ *x=1-x egyenletnek kell teljesülnie. Hiszen a csere+nincs csere lehetőségek összegének 1-et kell adnia. A második csere lehetőségre ezzel x=2/3-adot kapunk. Vagyis a csere megvalósulásának valószínűsége ½ *x=1/3-ad lesz. Ezt a módszert addig ismételjük, míg nem történik csere. Ekkor elölről kezdjük. Ha találtunk magasabb prioritású elemet, akkor szintén. Függvények: int save(char ki, char mod) A save.amb nevű text fáljba elmenti a table[][] tömb tartalmát. A fájl elejére a paraméterként kapott két karakter kerül az átadás sorrendjében. Ahol a ki érteke ’x’ vagy ’o’ lehet attól függően, hogy ki következne. A mod értéke ’1’ ha egyjátékos illetve ’2’ ha kétjátékos módból történik a mentés. Ezt követően a táblázatot a ’Mentés 2
kódolása’ című résznél leírtak szerint tárolja el. Visszatérési értéke logikai igaz ha sikerült és logikai hamis, ha nem sikerült a mentés. int load() A save() által elmentett save.amb fáljból visszatölti a táblázatot, valamint a játékmódot és, hogy ki folytatja a játékot. A dekódolás a ’Mentés kódolása’ című résznél leírtaknak megfelelően történik. Visszatérési értékel logikai igaz, ha sikerült és logikai hamis, ha nem sikerült a töltés. void line(int index,int direction,char *actlin) A table[][] tömbből kiemel egy sort amit az actlin-ben átadott címre ír ki. A karaktersorozat elején, és végére is ’\0’ karaktert tesz, a könnyebb kezelés érdekében. A direction és index változókban megadott értéktől függ, hogy milyen irányból, és hányadik sort olvassa ki. Ha a direction 1: balról jobbra, 2: fentről le, 3: bal föntről, jobb le, 4: bal alulról, jobbra föl olvassa ki. Az index 1-es direction értéknél a betűk által meghatározott koordinátáknak megfelelő, 2-es értéknél a számok által jelölt; 3-as, 4esnél 0-23 a betűk, a fölött pedig a számok által jelölt koordinátának felel meg. Ezt a függvényt használja a program a hazard() által módosított karaktersor visszaírására is. Ha a paraméterként kapott tömb 26. eleme ’i’, akkor az előbbiekben leírtaknak szerint a direction és indexnek megfelelő helyre illeszti vissza a table[][] tömbbe. int hazard(char ki) A line()-ből megkapott karaktersorozatot elemzi, kockázat illetve kedvező mezőket keresve. A ki karakterben megkapott jelet tekinti az ellenfél jelének, és ehhez viszonyít, a ’Kockázat vizsgálat’ részben leírtak szerint. A függvény futását mindig az aktuálisan a table[][] tömbben lévő kockázatot jelző karakterek törlésével kezdi. A függvény fölismeri, ha valamelyik játékos megnyerte a játékot. Visszatérési értéke 1, ha az ellenfél, és 2 ha ő maga győzött. Minden más esetben 0; int board() Feladata a tábla kirajzolása a képernyőre. Mivel a table[][] tömbben nem szerepel, ezért ki kell írnia a koordinátatengelyekre a megfelelő számozást, illetve betűzést is. A táblázat a hazard() futtatása után tartalmazni fog karakteres formában számokat is, ezért ha egy pozícióban nem ’x’ vagy ’o’ jel van, akkor az üres helyet jelképező ’.’ karaktert írja a képernyőre. struct koord step() Meghívásával a felhasználó lépését olvassuk be. A felhasználónak a board() által kiírt koordinátákat kell beírnia. A beírás sorrendje nem számít lehet elöl a szám és utána a betű, vagy fordítva. Ha nem értelmezhető koordinátát ütött be a felhasználó, akkor a második koordináta (j) 30-at vesz föl, ami a main számára jelzi, hogy olvastassa újra a koordinátákat. Ha a felhasználó 00 koordinátát üt be, akkor az első koordináta (i) értéke lesz 30, ami a játék félbeszakítását jelenti. A függvény koord struktúrában adja vissza a beolvasott eredményt. A koord az i és j koordinátákból áll, ahol i az 1, j a pedig a második változója a tömbnek.
3
struct koord play (char plyr) A gép által kedvezőnek talált lépés koordinátáival tér vissza az előbbiekben leírt koord struktúrában vagy ha az ellenfél győzött, 31 ha a gép, ha saját maga, akkor 32-vel az i koordinátában. 30-cal pedig, ha betelt a tábla. Paraméterként annak a játékosnak a jelét kapja, aki ellen játszik. A 3 módszerrel próbál helyet találnia a táblán. Először a hazard() szerint végig értékélt táblán megkeresi a legkisebb prioritású értéket. Ha a legkisebb értékből többet is talál, akkor a ’Véletlenszerű lecserélés’ részben leírtak szerint dönt az új mező kiválasztásáról. Ha nem talál a hazard() által veszélyesnek ítélt értéket, akkor keres egy már lerakott jelet, és megpróbálja annak a közvetlen környezetébe rakni(először a sajátjához utána az ellenféléhez). Ha ilyet sem talált, akkor véletlenszerűen tesz le valahová a pálya széléhez 2 sornál nem közelebb egy jelet. int felt() Ha új játékot kezd a felhasználó, akkor ennek segítségével töltjük föl a tömböt az üres helyet jelző ’.’ jelekkel. int main(void) A főprogram 2 részre bontható. 1, fele a menü. A menü első szintje attól függően jelenik meg, hogy létezik-e a save.amb fájl, vagyis van-e félbehagyott program. Ekkor ezt tekintjük főmenünek, és itt kínáljuk föl a kilépés lehetőségét valamint az egy illetve kétjátékos módot. Ha van elmentett játék, akkor az előbb említett menü az ’Uj jatek’ almenünek felel meg, ekkor viszont a kilépés helyett a visszaugrást kínálja föl. Az ’Uj jatek’ almenüjáben a jétékos kiválaszthatja, hogy kezdő szeretne lenni vagy sem. Ha volt betöltött játék, akkor a játék folytatását választva azonnal kezdődik. Az egyes menüválasztásokat a menu (int) és a ki és nki (char) tipusú változókban tároljuk, amit később az aktuálisan lépő, illetve ellenfél saját jel azonosítására használunk. Második része maga a játék lebonyolítása. Itt is két programrész kapott helyet: az egy illetve a kétjátékos mód. A program a beolvasott vagy a fájlból betöltött értékek alapján eldönti, hogy melyik módról van szó. Egyjátékosnál ha a gép kezd, akkor még a menü résznél megteszi az első lépést. Ha a játékos kezd, akkor beállítjuk a ki és az nki változó értékét annak megfelelően, hogy ki melyik jellel van. A ki mindig a felhasználó, az nki a gép jelét tárolja. Ennél a módnál ismétlődő ciklus a következő. Ha már történt lépés, akkor elmentjük a táblát. Bekérjük a felhasználó lépését. Ha ez olyan érték, ahová nem lehet rakni, akkor újra bekéri. Ha pedig nem értelmezhető(00), akkor kilép a játékból, és visszatér a menübe. Ha sikeres lépés történt, akkor a számítógép következik. Ha sikerült helyet találnia(nincs teli a tábla és nem győzött még senki), akkor elhelyezzük a kiválasztott helyre a jelét, újra megnézzük, nem nyert-e ezzel a lépéssel. Ha minden rendben, akkor újra kezdjük a programot. Különben kiírja a megfelelő üzenetet és kilép a menübe. Kétjátékos módban is mindig az ’x’ kezd. Bekérjük a koordinátákat. Az előbb leírtak szerint kezeljük a nem megfelelőeket. Majd beírjuk a table[][] tömbbe, és ellenőrizzük nem nyert-e valaki, ha igen, akkor kilépés következik.. Végül megcseréljük a jeleket.(A ki változóban mindig a soron következő játékos jelét tároljuk.) Ha nem nyert senki, akkor már átcserélt jelekkel kezdődik elölről a ciklus.
4
Fejlesztési lehetőségek Betöltés biztonságának növelése: Jelenleg a save.amb fájl meglétét, és megnyithatóságát csak a menü kiírásánál ellenőrzi a program, a tényleges megnyitásnál és betöltésnél azonban már nem. Bár a load() függvényben nem fog hiba generálódni, de ekkor praktikusabb lenne, ha figyelembe vennénk a main()-ban a load() ’hamisságát’ és ekkor visszaugrana a menübe. Így félő, hogy ilyen hiba bekövetkezésénél a table[][] lefoglalásánál a memóriában maradt rossz értékekkel fog dolgozni a program. Véletlen csere nem csak a veszélykeresésnél: A véletlen csere algoritmusát praktikus lenne függvényként megvalósítani, és használni akkor is, amikor a program a már korábban lerakott jelek mellé szeretne rakni. Ilyen módon elkerülhetnénk azt a hibát, hogy pl. második lépésnek mindig az előzőtől balra fölfelé eső mezőt választja. A játék személyesebbé tétele: A felhasználó számára könnyebben eldönthető lenne, akár egy félbe hagyott játék után, hogy ki következik, ha a bekérné és eltárolná mindkét játékos rövid becenevét. Ezt kiterjesztve az egy játékos módra másik egy fáljban toplistát lehetne tárolni, amit aszerint állítunk föl, hogy ki hány lépésben verte meg a gépet. A támadó stratégia fejlesztése: A hazard() és line() függvények módosításával elérhető lenne, hogy csak tetszőleges méretű területet vizsgáljunk a játéktérből, és ennek eredményét egy másik tömbbe mentsük. Ilyen módon, ha pl. nincs egy meghatározott prioritásnál magasabb a táblán, akkor egy új függvény segítségével kiválasztunk egy területet(lehetőleg olyat, ahol található valamilyen prioritásérték), és megvizsgáljuk, hogy melyik mező elfoglalásával érhetjük el a legnagyobb támadó prioritásnövekedést, vagy veszély csökkenést. Ez alatt azt értve, hogy összességében mennyivel lett több pl a magasabb prioritású támadó mezők száma. Így a program mintegy előre gondolkozna, nem csak az adott helyzetre reagálna. Melléklet: Blokkvázlat (2 oldal)
5