Tetris ágens
Írta és fejlesztette: Farády László
Tetris: a történet
A tetris egyike azon kevés játékoknak amelyek igen nagy népszerűséget vívtak ki maguknak. Ez a játék rendkívül egyszerű és egyben rendkívül nehéz. Előfordult ő már minden számítógépen és játék konzolon ami létezik, és millió szám adták el az egész világon azt a kis műanyag csipogót, ami megszépítette életemet nekem is, mikor még kisgyerek voltam.
Lehet, hogy furcsán hangzik, de a legelső tetris játék már jóval a számítógépek előtt létezett. Az eredeti tetrist Pentomino-nak hívták. A játék célja az volt, hogy egy adott területet kitölteni adott alakzatokkal, amelyek öt (latinul: penta) kis négyzetből állnak, úgy hogy ne maradjon üres hely.
Ám néhány évtizeddel később (az első számítógépek megjelenése idejében), 1985-ben egy Alexey Pazhitnov nevű orosz úriember gondolt egyet és átalakította egy kicsit ezt a játékot, hogy még megnyerőbb legyen. Időzítővel látta el ill. egy másik hasznos gondolat folytán átformálta a kis darabokat is: lefaragott néhány kockát minden egyes alakzatból, így lett a „penta”-ból „tetri”.
Ezek a tulajdonságok tették a tetrist szerfelett népszerűvé, és így a tetris vált a legtöbbet eladott játékká.
Feladatom egy tetris ágens java applet program megírása volt. Egy weblapon két oldalt megjelenik egy-egy játékfelület, ahol 6 kontrol mód közül lehet választani (akár a játék közben is).
De még mielőtt rátérnék ezekre felhívom a figyelmüket a program egy kis hiányosságára. Mivel a két megjelenő applet egy és ugyanaz, és a véletlen eseteket a java setSeed függvényével valósítottam meg, amely a rendszeridőből generál egy véletlen számot. Néha előfordul, hogy a weblap megnyitásakor a két program nem egy időben tud elindulni. Ekkor előfordul az, hogy sajnos nem ugyanazok az elemek jelennek meg a két oldalon. Kérem ezért a tisztelt felhasználót, hogy ilyenkor refresh gombbal újból letölteni szíveskedjék az oldalt. Tesztelés során legfeljebb 3-4 letöltés után sikerült elérnem ezt a célt. Úgy gondolom ez megbocsátható, mivel így mindig másmilyen sorozatot kapunk.
r = new Random();
r.setSeed(System.currentTimeMillis());
Kézi vezérlés. Ez az alapértelmezett mód. A játékos maga irányítja a különböző alakzatokat a helyükre. Mozgatás a nyílbillentyűkkel valamint az alakzatok forgatása a Space billentyűvel. Bármikor átválthatunk erre a vezérlési módra, ha rákattintunk a játéktérre és megnyomjuk a 0 gombot.
Ctr1: random: itt szintén a fent említett véletlen szám generátort használja a program az aktuális elem helyének meghatározására.
cx = Math.abs(r.nextInt())%15;
cr = Math.abs(r.nextInt())%4;
A cx paraméter adja az alakzat közepének (melyet a játék során kis négyzet jelöl) x koordinátáját jelöli. A cr az alakzat elforgatottságát jelzi.
Az ezután következő vezérlési módok kvázi azonosak. Csupán abban különböznek egymástól, hogy az egyes esetek tulajdonságait milyen preferencia sorrendben tekinti meg az adott jellem.
Ctr2: sor/mely/lyuk(1)/bal: amely úgy helyezi el az elemet, hogy a legtöbb sor tűnjön el. Ha az az eset állna elő, hogy két pozíció is ugyanannyi sort tüntetne el, akkor a kettő közül azt választja, amelyben az aktuális elemet mélyebbre tudja tenni. Ha több ilyen helyzet is létezik, akkor a vezérlő azt választja, amely kevesebb üres lyukat eredményez a táblán. Ha még mindig több ilyen pozíciót talál, akkor a bal oldalon lévőt választja.
Bemutatok egy esetet:
Ezt a tesztelési esetet én hoztam létre kézi vezérlést használva. (Hála istennek egyik vezérlés sem képes ilyet művelni). A képen jól látható, hogy az L alakzat abban az oszlopban maradva fog le esni. Itt most a mélység dominál, mivel egyik pozícióban sem tud sort eltüntetni.
Ctr3: sor/mely/lyuk(2)/bal: Ahogy említettem, ezek a vezérlési módok nagyon hasonlítanak egymásra, ezért inkább csak a különbségekre hívnám fel a figyelmet. E két utóbbi között a következő eltérés van:
Míg a második vezérlésben a lyukszám meghatározásánál a program fentről lefelé tekinti, itt lentről felfelé.
2.vezérlés:
private int getBunt(int ccx, int ccy, int ccr)
{
int b = 0;
int cb;
for (int x=0; x<15; x++)
{
cb = -1;
for (int y=0;
y<25; y++)
{
int cval = tet.getMap(x,y);
if ((ccx==x) && (ccy==y)) cval=1;
for (int i=0; i<3; i++) if ((ccx+tet.getDx(i,ccr)==x) && (ccy+tet.getDy(i,ccr)==y)) cval=1;
if ((cb<0) && (cval!=0)) cb=0;
if ((cb>=0) && (cval==0)) cb++;
}
if (cb<0) cb=0;
b += cb;
}
return b;
}
3. vezérlés:
private int getBunt(int ccx, int ccy, int ccr)
{
int b = 0;
int cb;
for (int x=0; x<15; x++)
{
cb = -1;
for (int y=24;
y>=0; y--)
{
int cval = tet.getMap(x,y);
if ((ccx==x) && (ccy==y)) cval=1;
for (int i=0; i<3; i++) if ((ccx+tet.getDx(i,ccr)==x) && (ccy+tet.getDy(i,ccr)==y)) cval=1;
if ((cb<0) && (cval==0)) cb=0;
if ((cb>=0) && (cval!=0)) cb++;
}
if (cb<0) cb=0;
b += cb;
}
return b;
}
A különbséget bekövérítettem.
A következő kép egy versenyt mutat be a két mód között.
A jobb oldalon a Ctr3 már befejezte a játékot. Baloldalon
még javában játszott a program. Sajnos nem volt időm végig várni. Ahhoz
viszont, hogy bármifélét is következtetni lehessen - szerintem - rengeteg
tesztversenyre lenne szükséges.
Ctr4:
sor/lyuk(1)/mely/bal: A már fent
említett pozíció tulajdonságokat a következő sorrendben ellenőrzi: minél több
sort tüntessen el, minél kevesebb lyukat (hibát, vagy ha hű akarok lenni a
függvény névhez: büntetőpontot)
eredményezzen, minél mélyebbre tegye az adott elemet, és ha ezek után is
választania kell, akkor a baloldalhoz legközelebb elhelyezkedőt választja.
Ctr5:
sor/lyuk(2)/mely/bal: Ugyanaz tehát
mint a negyedik vezérlési mód, azzal a különbséggel, hogy lyukaknál lentről
felfelé pásztázza végig a játéktáblát.
A további teszteket a felhasználóra bízom. Érdekes
dolgok jöhetnek majd ki. Jó időtöltést kívánok.