Mit fegyvert? Siklóágyút!
Korábbi posztunkban már bemutattuk, hogy 1975-ben Benoit Mandelbrot milyen különleges iterációs lényre bukkant a komplex számsíkon az origó környékén. Néhány évvel korábban John Horton Conway konstruált a neumanni értelemben vett önreprodukáló gépeket: ez az életjáték. Feldolgozta a témát a Javát tanítok is, aminek Sejtautomata osztályát a Sejtautomata.java forrásba másolva, a
javac Sejtautomata.java
parancsok után azonnal élvezheted. Esetünkben - lévén, hogy a prog1 bevezető tárgyat támogatjuk részben ezzel a bloggal - ezt a Java megvalósítást írjuk át Qt-ben C++-ra. Kicsit naiv a megközelítésünk, de a két C++ osztálynál a tervezés fő szempontja az volt, hogy szinte semmiben ne különbözzenek a már posztolt kezdő C++ osztályainktól.
java Sejtautomata
Természetesen ugyanígy megy a Qt SDK-val GNU/Linux alatt is, s parancssorból is:
[norbi@sgu Sejtauto]$ ls -l
összesen 28
-rwxrwxrwx. 1 norbi norbi 196 márc 4 12.34 main.cpp
-rwxrwxrwx. 1 norbi norbi 7240 márc 4 12.47 sejtablak.cpp
-rwxrwxrwx. 1 norbi norbi 1267 márc 4 12.35 sejtablak.h
-rwxrwxrwx. 1 norbi norbi 5133 márc 4 12.43 sejtszal.cpp
-rwxrwxrwx. 1 norbi norbi 760 márc 4 12.35 sejtszal.h
[norbi@sgu Sejtauto]$ qmake-qt4 -project
[norbi@sgu Sejtauto]$ qmake-qt4 Sejtauto.pro
[norbi@sgu Sejtauto]$ make
[norbi@sgu Sejtauto]$ ./Sejtauto
Tovább link, ha érdekel:
Mivel nem a végtelen 2D-s sejttérrel dolgozunk, hanem "periodikus határfeltétellel" ("ami fent kimegy, lent bejön" stb.) így a siklók kilövését nem csodálhatjuk a végtelenségig, saját siklói rombolják le az állomást:
Jöjjenek a források!
main.c
#include <QtGui/QApplication> #include "sejtablak.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); SejtAblak w(100, 75); w.show(); return a.exec(); }
sejtablak.h
#ifndef SEJTABLAK_H #define SEJTABLAK_H #include <QtGui/QMainWindow> #include <QPainter> #include "sejtszal.h" class SejtSzal; class SejtAblak : public QMainWindow { Q_OBJECT public: SejtAblak(int szelesseg = 100, int magassag = 75, QWidget *parent = 0); ~SejtAblak(); // Egy sejt lehet élõ static const bool ELO = true; // vagy halott static const bool HALOTT = false; void vissza(int racsIndex); protected: // Két rácsot használunk majd, az egyik a sejttér állapotát // a t_n, a másik a t_n+1 idõpillanatban jellemzi. bool ***racsok; // Valamelyik rácsra mutat, technikai jellegû, hogy ne kelljen a // [2][][]-ból az elsõ dimenziót használni, mert vagy az egyikre // állítjuk, vagy a másikra. bool **racs; // Megmutatja melyik rács az aktuális: [rácsIndex][][] int racsIndex; // Pixelben egy cella adatai. int cellaSzelesseg; int cellaMagassag; // A sejttér nagysága, azaz hányszor hány cella van? int szelesseg; int magassag; void paintEvent(QPaintEvent*); void siklo(bool **racs, int x, int y); void sikloKilovo(bool **racs, int x, int y); private: SejtSzal* eletjatek; }; #endif // SEJTABLAK_H
sejtablak.cpp
// sejtablak.cpp // // Életjáték rajzoló // Programozó Páternoszter // // Copyright (C) 2011, Bátfai Norbert, nbatfai@inf.unideb.hu, nbatfai@gmail.com // // 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. // // // Version history: // // 0.0.1 A két osztály tervezésének fõ szempontja az volt, hogy // ne vagy alig különbözzön az elsõ C++-os példától, a Mandelostól: // http://progpater.blog.hu/2011/02/26/tan_csodallak_amde_nem_ertelek_de_kepzetem_hegyvolgyedet_bejarja // ezért az olyan kényesebb dolgokkal, hogy kezeljük a racsIndex-et a // két osztályra bontott C++ megoldásban, amikor írjuk át a Javásból, nem foglalkoztunk // a kiinduló Javás: http://www.tankonyvtar.hu/informatika/javat-tanitok-1-2-080904-1 // (a bazár eszme: Release Early, Release Often" írjuk ki a posztra) // #include "sejtablak.h" SejtAblak::SejtAblak(int szelesseg, int magassag, QWidget *parent) : QMainWindow(parent) { setWindowTitle("A John Horton Conway-féle életjáték"); this->magassag = magassag; this->szelesseg = szelesseg; cellaSzelesseg = 6; cellaMagassag = 6; setFixedSize(QSize(szelesseg*cellaSzelesseg, magassag*cellaMagassag)); racsok = new bool**[2]; racsok[0] = new bool*[magassag]; for(int i=0; i<magassag; ++i) racsok[0][i] = new bool [szelesseg]; racsok[1] = new bool*[magassag]; for(int i=0; i<magassag; ++i) racsok[1][i] = new bool [szelesseg]; racsIndex = 0; racs = racsok[racsIndex]; // A kiinduló racs minden cellája HALOTT for(int i=0; i<magassag; ++i) for(int j=0; j<szelesseg; ++j) racs[i][j] = HALOTT; // A kiinduló racsra "ELOlényeket" helyezünk //siklo(racs, 2, 2); sikloKilovo(racs, 5, 60); eletjatek = new SejtSzal(racsok, szelesseg, magassag, 120, this); eletjatek->start(); } void SejtAblak::paintEvent(QPaintEvent*) { QPainter qpainter(this); // Az aktuális bool **racs = racsok[racsIndex]; // racsot rajzoljuk ki: for(int i=0; i<magassag; ++i) { // végig lépked a sorokon for(int j=0; j<szelesseg; ++j) { // s az oszlopok // Sejt cella kirajzolása if(racs[i][j] == ELO) qpainter.fillRect(j*cellaSzelesseg, i*cellaMagassag, cellaSzelesseg, cellaMagassag, Qt::black); else qpainter.fillRect(j*cellaSzelesseg, i*cellaMagassag, cellaSzelesseg, cellaMagassag, Qt::white); qpainter.setPen(QPen(Qt::gray, 1)); qpainter.drawRect(j*cellaSzelesseg, i*cellaMagassag, cellaSzelesseg, cellaMagassag); } } qpainter.end(); } SejtAblak::~SejtAblak() { delete eletjatek; for(int i=0; i<magassag; ++i) { delete[] racsok[0][i]; delete[] racsok[1][i]; } delete[] racsok[0]; delete[] racsok[1]; delete[] racsok; } void SejtAblak::vissza(int racsIndex) { this->racsIndex = racsIndex; update(); } /** * A sejttérbe "ELOlényeket" helyezünk, ez a "sikló". * Adott irányban halad, másolja magát a sejttérben. * Az ELOlény ismertetését lásd például a * [MATEK JÁTÉK] hivatkozásban (Csákány Béla: Diszkrét * matematikai játékok. Polygon, Szeged 1998. 172. oldal.) * * @param racs a sejttér ahová ezt az állatkát helyezzük * @param x a befoglaló tégla bal felsõ sarkának oszlopa * @param y a befoglaló tégla bal felsõ sarkának sora */ void SejtAblak::siklo(bool **racs, int x, int y) { racs[y+ 0][x+ 2] = ELO; racs[y+ 1][x+ 1] = ELO; racs[y+ 2][x+ 1] = ELO; racs[y+ 2][x+ 2] = ELO; racs[y+ 2][x+ 3] = ELO; } /** * A sejttérbe "ELOlényeket" helyezünk, ez a "sikló ágyú". * Adott irányban siklókat lõ ki. * Az ELOlény ismertetését lásd például a * [MATEK JÁTÉK] hivatkozásban /Csákány Béla: Diszkrét * matematikai játékok. Polygon, Szeged 1998. 173. oldal./, * de itt az ábra hibás, egy oszloppal told még balra a * bal oldali 4 sejtes négyzetet. A helyes ágyú rajzát * lásd pl. az [ÉLET CIKK] hivatkozásban /Robert T. * Wainwright: Life is Universal./ (Megemlíthetjük, hogy * mindkettõ tartalmaz két felesleges sejtet is.) * * @param racs a sejttér ahová ezt az állatkát helyezzük * @param x a befoglaló tégla bal felsõ sarkának oszlopa * @param y a befoglaló tégla bal felsõ sarkának sora */ void SejtAblak::sikloKilovo(bool **racs, int x, int y) { racs[y+ 6][x+ 0] = ELO; racs[y+ 6][x+ 1] = ELO; racs[y+ 7][x+ 0] = ELO; racs[y+ 7][x+ 1] = ELO; racs[y+ 3][x+ 13] = ELO; racs[y+ 4][x+ 12] = ELO; racs[y+ 4][x+ 14] = ELO; racs[y+ 5][x+ 11] = ELO; racs[y+ 5][x+ 15] = ELO; racs[y+ 5][x+ 16] = ELO; racs[y+ 5][x+ 25] = ELO; racs[y+ 6][x+ 11] = ELO; racs[y+ 6][x+ 15] = ELO; racs[y+ 6][x+ 16] = ELO; racs[y+ 6][x+ 22] = ELO; racs[y+ 6][x+ 23] = ELO; racs[y+ 6][x+ 24] = ELO; racs[y+ 6][x+ 25] = ELO; racs[y+ 7][x+ 11] = ELO; racs[y+ 7][x+ 15] = ELO; racs[y+ 7][x+ 16] = ELO; racs[y+ 7][x+ 21] = ELO; racs[y+ 7][x+ 22] = ELO; racs[y+ 7][x+ 23] = ELO; racs[y+ 7][x+ 24] = ELO; racs[y+ 8][x+ 12] = ELO; racs[y+ 8][x+ 14] = ELO; racs[y+ 8][x+ 21] = ELO; racs[y+ 8][x+ 24] = ELO; racs[y+ 8][x+ 34] = ELO; racs[y+ 8][x+ 35] = ELO; racs[y+ 9][x+ 13] = ELO; racs[y+ 9][x+ 21] = ELO; racs[y+ 9][x+ 22] = ELO; racs[y+ 9][x+ 23] = ELO; racs[y+ 9][x+ 24] = ELO; racs[y+ 9][x+ 34] = ELO; racs[y+ 9][x+ 35] = ELO; racs[y+ 10][x+ 22] = ELO; racs[y+ 10][x+ 23] = ELO; racs[y+ 10][x+ 24] = ELO; racs[y+ 10][x+ 25] = ELO; racs[y+ 11][x+ 25] = ELO; }
sejtszal.h
#ifndef SEJTSZAL_H #define SEJTSZAL_H #include <QThread> #include "sejtablak.h" class SejtAblak; class SejtSzal : public QThread { Q_OBJECT public: SejtSzal(bool ***racsok, int szelesseg, int magassag, int varakozas, SejtAblak *sejtAblak); ~SejtSzal(); void run(); protected: bool ***racsok; int szelesseg, magassag; // Megmutatja melyik rács az aktuális: [rácsIndex][][] int racsIndex; // A sejttér két egymást követõ t_n és t_n+1 diszkrét idõpillanata // közötti valós idõ. int varakozas; void idoFejlodes(); int szomszedokSzama(bool **racs, int sor, int oszlop, bool allapot); SejtAblak* sejtAblak; }; #endif // SEJTSZAL_H
sejtszal.cpp
// sejtszal.cpp // // Életjáték rajzoló // Programozó Páternoszter // // Copyright (C) 2011, Bátfai Norbert, nbatfai@inf.unideb.hu, nbatfai@gmail.com // // 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. // // // Version history: // // 0.0.1 A két osztály tervezésének fõ szempontja az volt, hogy // ne vagy alig különbözzön az elsõ C++-os példától, a Mandelostól: // http://progpater.blog.hu/2011/02/26/tan_csodallak_amde_nem_ertelek_de_kepzetem_hegyvolgyedet_bejarja // ezért az olyan kényesebb dolgokkal, hogy kezeljük a racsIndex-et a // két osztályra bontott C++ megoldásban, amikor írjuk át a Javásból, nem foglalkoztunk // a kiinduló Javás: http://www.tankonyvtar.hu/informatika/javat-tanitok-1-2-080904-1 // (a bazár eszme: Release Early, Release Often" írjuk ki a posztra) // #include "sejtszal.h" SejtSzal::SejtSzal(bool ***racsok, int szelesseg, int magassag, int varakozas, SejtAblak *sejtAblak) { this->racsok = racsok; this->szelesseg = szelesseg; this->magassag = magassag; this->varakozas = varakozas; this->sejtAblak = sejtAblak; racsIndex = 0; } /** * Az kérdezett állapotban lévõ nyolcszomszédok száma. * * @param rács a sejttér rács * @param sor a rács vizsgált sora * @param oszlop a rács vizsgált oszlopa * @param állapor a nyolcszomszédok vizsgált állapota * @return int a kérdezett állapotbeli nyolcszomszédok száma. */ int SejtSzal::szomszedokSzama(bool **racs, int sor, int oszlop, bool allapot) { int allapotuSzomszed = 0; // A nyolcszomszédok végigzongorázása: for(int i=-1; i<2; ++i) for(int j=-1; j<2; ++j) // A vizsgált sejtet magát kihagyva: if(!((i==0) && (j==0))) { // A sejttérbõl szélének szomszédai // a szembe oldalakon ("periódikus határfeltétel") int o = oszlop + j; if(o < 0) o = szelesseg-1; else if(o >= szelesseg) o = 0; int s = sor + i; if(s < 0) s = magassag-1; else if(s >= magassag) s = 0; if(racs[s][o] == allapot) ++allapotuSzomszed; } return allapotuSzomszed; } /** * A sejttér idõbeli fejlõdése a John H. Conway féle * életjáték sejtautomata szabályai alapján történik. * A szabályok részletes ismertetését lásd például a * [MATEK JÁTÉK] hivatkozásban (Csákány Béla: Diszkrét * matematikai játékok. Polygon, Szeged 1998. 171. oldal.) */ void SejtSzal::idoFejlodes() { bool **racsElotte = racsok[racsIndex]; bool **racsUtana = racsok[(racsIndex+1)%2]; for(int i=0; i<magassag; ++i) { // sorok for(int j=0; j<szelesseg; ++j) { // oszlopok int elok = szomszedokSzama(racsElotte, i, j, SejtAblak::ELO); if(racsElotte[i][j] == SejtAblak::ELO) { /* Élõ élõ marad, ha kettõ vagy három élõ szomszedja van, különben halott lesz. */ if(elok==2 || elok==3) racsUtana[i][j] = SejtAblak::ELO; else racsUtana[i][j] = SejtAblak::HALOTT; } else { /* Halott halott marad, ha három élõ szomszedja van, különben élõ lesz. */ if(elok==3) racsUtana[i][j] = SejtAblak::ELO; else racsUtana[i][j] = SejtAblak::HALOTT; } } } racsIndex = (racsIndex+1)%2; } /** A sejttér idõbeli fejlõdése. */ void SejtSzal::run() { while(true) { QThread::msleep(varakozas); idoFejlodes(); sejtAblak->vissza(racsIndex); } } SejtSzal::~SejtSzal() { }
Kisbajnokságok
A 4 és az 1 trófeát érő bajnokságok között keresd a kapcsolódóakat!