Az életjáték leírása itt található a Wikipédián, de azért a játék lényegét most leírom:
Képzeljünk el egy végtelen kiterjedésű "kockás" papírt. Rajzoljunk be a négyzetekbe pöttyöket. Ezek lesznek a sejtek. A sejtek az új generációja úgy jön létre, ha van a környezetében másik sejt, de nincsen túl sok! Ez azt jelenti, hogyha 2 vagy 3 szomszédos helyen van sejt, akkor az aktuális helyen lévő sejt tovább él. Ha kevesebb van, akkor "éhenhal", ha több szomszédban van sejt, akkor megfojtják.
Készíts olyan programot, amellyel feltöltheted a játékteret és amely a generációkat lépteti. A program lehet konzolos, akkor az egyes helyek a konzol képernyőhelyeit jelentik. Ha grafikus felületet akarsz használni, akkor célszerűen checkboxok legyenek az egyes helyek.
Megoldási gondolatok 1.
Az adatok tárolásához egy NxN-es tömböt kell deklarálnunk, ahol N egy pozitív egész szám. Mivel végtelen területet nem tudunk létrehozni, ezért N legyen elég nagy szám (teszteléshez elég 10-20 között)!
Végig kell néznünk a játékteret (két darab egymásba ágyazott ciklussal) és meg kell néznünk minden egyes hely szomszédainak a számát. Ha 2 vagy 3 szomszédon van akkor a következő generációban az adott helyen sejt lesz. Nyolc szomszéd lehet.
// 8 szomszéd hely
// o o o
// o X o
// o o o
for (int i=0;i<N;i++){
for(int j=0; j<N ; j++){
db = 0;
try{
if( t[i-1,j-1] >0 ) db++;
}catch{}
.... //Itt nyolc helyet kell megvizsgálni
....
if( db >1 && db<4)
kov[i,j] =1;
else
kov[i,j] = 0;
}
}
Megjegyzés
A try{}catch{} szerkezet azért fontos, mert a tömbindexek a vizsgálatnál kívül eshetnek a tömbhatárokon. Ezt persze lehetne orvosolni úgy, hogy a program nyelv 0-tól indexeli a tömböket N-1-ig, de mi 0-tó N+2-ig deklaráljuk a tömböt és akkor néhány elemet nem használunk semmire, de nem kell használnunk a hibakezelést!
A programrészlet lefutása után a kov[] tömbben lesznek a következő generáció adata, amelyet ki kel vissza kell másolnunk az eredeti tömbbe. Ehhez is két egymásba ágyazott ciklusra van szükségünk.
for (int i=0; i < N;i++){
for(int j=0; j < N ; j++){
t[i,j] = kov[ i, j ];
}
}
Megjegyzés
Amikor egy program egymás utáni állapotokat jelenít meg olyan módon, hogy az előző állapot adataiból (minden adatából) kell a következő állapot adatait kiszámolnunk, akkor mindig két adattáblát kell használnunk, mivel ha a változásokat az aktuális táblába írnánk, akkor rögtön elrontanánk a tartalmát. A megoldás először az új kiszámolt adatokat egy új táblázatba tesszük, majd a végén az egészet visszamásoljuk az eredeti adattáblába.
Az adatok beviteléről és megjelenítéséről
Nyilvánvalóan a megjelenítést is két egymásba ágyazott ciklussal végezhetjük és egy i,j koordinátájú helyre kiteszünk egy pöttyöt, ha van élet t[i,j] >0 a feltétel és nem teszünk semmit, ha nincsen t[i,j] == 0;
Ha konzolos alkalmazást írunk, akkor célszerűen a kezdőadatokat kézzel kell bevinnünk, vagy fájlból beolvasnunk. Ha fájlból olvasunk be, akkor a fájlban soronként tárolnunk érdemes annyi 0 vagy 1 értéket ;-ve elválasztva, ahány oszlopunk van. A fájlban pedig annyi sornak kell lennie, ahány soros a táblázat. Az adatok kijelzéséhez pedig használhatjuk:
for (int i=0; i < N;i++){
for(int j=0; j < N ; j++){
Console.SetCursorPosition(i,j);
if(t[i,j]>1){
Console.Write("x");
}else{
Console.Write(" ");
}
}
}
Ha Windows Form Applicationt használunk, akkor célszerű CheckBoxokat létrehozni és az adatok értéke alapján a CheckBox.CheckState = CheckState.Checked; CheckBox.CheckState = CheckState.Unchecked állapotot beírni az aktuális checkboxba;
for (int i=0; i < N;i++){
for(int j=0; j < N ; j++){
if(t[i,j]>1){
this.checks[i,j].CheckState=CheckState.Checked;
}else{
this.checks[i,j].CheckState=CheckState.Unchecked
}
}
}
Másik megoldási gondolatok
A programban kritikus az a rész, amikor egy pont környezetében megnézzük, hogy hány másik pont létezik. Azért kritikus, mert a 0. és az utolsó (N-1-ik) sorban és oszlopban is vizsgálni kellene az eggyel nagyobb és kisebb helyeket, ami ugye már a tömbön kívül helyet jelentenek. Az előző példában erre való volt a try...catch szerkezet. A másik megoldás azonban elegánsabb és gyorsabb is általában egy kis plusz memóriafoglalás árán.
A játékteret ne 0....N-1 között határozzuk meg, hanem 1...N között, továbbá a legkisebb sorú és oszlopú elem előtt és a legnagyobb sorú és oszlopú sor után is legyen egy-egy plusz sor és oszlop. Vagyis a tömbünk legyen t[N,N] legyen hanem t[N+2, N+2] és a játéktér 1..N-ig tartson!
Ettől kezdve minden tömbművelet úgy néz ki, hogy:
for (int i=1; i < N+1;i++){
for(int j=1; j < N+1 ; j++){
..... tetszőleges műveletek....
}
}
Ez után az sem lesz probléma, ha a játéktér szélső vagy utolsó sorának a környezetét nézzük meg, mert lesz a tömbnek ilyen oszlopa és sora, nem akad ki a program. Igaz, hogy az ezekben lévő értékek 0-ák lesznek, de az nekünk pont jó lesz. Ezeket a helyeket nem kell megjelenítenünk, mert úgyis nulla az értéke, vagyis nincsn élő sejt bennük!
Ha a Windows Form Applicationt használunk, akkor a korábban említett két darab két dimenziós tömb közül az egyik lehet a checkboxok tömbje és csak a következő generáció legyen egy "mezei" integerekből álló tömb. ebben az esetben az alábbi módon lehet megnézni, hogy mi lesz a következő generációban
int db = 0;
for(int i = 1; i < N+1 ; i++)
{
for( int j = 1; j < N+1 ; j++)
{
//Megnézzük az aktuális pont körüli pontok számát
db = 0;
for(int k=i-1;k <= i+1; k++)
{
for(int l=j-1; l <=j+1; l++)
{
//Ha az adott checkbox be van jelölve, akkor növelem a db-ot eggyel.
db += this.checks[k, l].CheckState == CheckState.Checked ? 1:0;
}
}
//Ha az aktuális pont be van jelölve, akkor csökkenteni kell a db értékét 1-gyel.
if (this.checks[i, j].CheckState == CheckState.Checked ) db--;
//Ha a pontnak 2 vagy 3 szomszédja van, akkor a következő generációban él.
if ((2 == db ) || (db == 3))
kovgeneracio[i, j] = 1;
else
kovgeneracio[i, j] = 0;
}
}
//A következő generáció szerint beállítom acheckboxokat
for (int i = 1; i < N+1; i++)
{
for (int j = 1; j < N+1 ; j++)
{
if (kovgeneracio[i, j] == 1)
this.checks[i, j].CheckState = CheckState.Checked;
else
this.checks[i, j].CheckState = CheckState.Unchecked;
}
}
}
Sajnos konzolos alkalmazás esetén mindenképpen kell a két tömb!