Eli tutustuin yhden kaverin kautta John Horton Conwayn 1970 kehittämään "peliin". Kts. http://en.wikipedia.org/wiki/Conway's_Game_of_Life
Eli tarkoituksena on pitää elämää yllä yksittäisissä soluissa. Pääperiaate on, että jos elävällä solulla on vähemmän kuin 2 vierustoveria, se kuolee yksinäisyyteen. Jos solulla on enemmän kuin 3 vierustoveria se kuolee liian ahtaaseen elintilaan. Jos kuolleella solulla on tasan kolme kaveria niin se herää henkiin hyväntuulisena kavereistansa. :)
En osaa kuvailla kunnolla mitä seuraava koodiesimerkki yrittää teille opettaa. Ehkäpä std::map-luokan käyttöä osoittimia avaiminaan käyttäen. Lisäksi tässä tulee hieman asiaa functoreihin (osoittimiin toiminnallisuuteen), sillä niitä tarvitaan piirtoa varten. Ne eivät kuitenkaan ole tärkeässä osassa tätä koodia.
Kehittämäni algoritmi menee seuraavasti:
1) Luo tyhjä taulukko.
2) Sytytä haluttuihin soluihin elämää. Toista tämä niin monta kertaa kuin haluat.
3) Päivitä solut.
4) Tyhjennä solut. Lopetus.
------- ITSE PELIN ALGORITMI -------
Tämä on tärkein. Se käsittää solujen päivityksen. Algoritmi on pääpiirteittäin 5-vaiheinen.
-- 1. VAIHE - ELÄVIEN TARKISTUS --
1) Tarkista elävän solun vieressä oleva solu.
1a) Jos solu on taulukossa niin:
1aa) Tarkista onko solu elävä tai kuollut.
1aaa) Solu on elävä.
Merkkaa solu päivitettäväksi (lisää päivitettäväkarttaan jos ei ole vielä) myöhempää tarkistusta varten ja lisää solun kaverilaskuria yhdellä. Lisää karttaan myös solun x- ja y-indeksit. Palauta true.
1aab) Solu ei ole elävä.
Palauta false.
1b) Jos solu ei ole taulukossa niin:
Palauta false.
2) Tarkista palautetun solun arvo.
2a) True:
Lisää solun kaverilaskuria.
2b) False:
Älä tee mitään.
3) Toista kohdat 1 ja 2 jokaiselle vierussolulle.
4) Tarkista solun kaverit.
4a) Kavereita 2 tai 3:
Älä tee mitään.
4b) Kavereita joku muu määrä:
Lisää solu tuhottavien listalle. Anna listaan solun x- ja y-indeksi.
5) Tarkista kaikki elävät solut elävä-kartasta (toista kohdat 1-4).
-- 2. VAIHE - KUOLLEIDEN HERÄTYS --
1) Iteroi päivitettävien kartta.
2) Tarkista solun kaverilaskuri.
2a) 3:
Herätä solu henkiin. Lisää solun osoite elävien karttaan. Anna tiedoksi myös solun x-ja y-indeksi. Aseta pohjataulukon solun arvoksi TRUE.
2b) Muu luku:
Älä tee mitään.
3) Tarkista kartan kaikki solut (kohdat 1-2).
-- 3. VAIHE - KUOLEVIEN POISTO --
1) Iteroi kuolevien listaa.
2) Katso solun x- ja y-indeksi elävien kartasta käyttäen listassa olevaa osoitinta (sama osoitin on avaimena elävien kartassa).
3) Aseta pohjataulukon solun arvoksi FALSE (käyttäen kartasta saatuja indeksi-lukuja).
4) Poista solu elävien kartasta käyttäen listan osoitinta poistettavan solun avaimena.
5) Käy koko lista läpi (kohdat 1-4).
-- 4. VAIHE - VÄLIAIKAISTIETOJEN POISTO -
1) Tyhjennä päivitettävien kartta.
2) Tyhjennä poistettavien lista.
-- 5. VAIHE - PIIRTO -
1) Tarkista, onko piirtofunktio asetettu.
1a) True:
Jatka piirto-operaatiota (siirry kohtaan 2).
1b) False:
Poistu 5. vaiheesta (skippaa loput kohdat).
2) Iteroi elävien karttaa.
3) Anna funktiolle parametrina elävän solun x- ja y-indeksi.
4) Piirtofunktio tekee haluamansa.
5) Käy kaikki loput elävät solut läpi (toista kohdat 2-4).
Jeps... Kuten huomaatte, en ole mikään kovin hyvä selittämään sanallisesti näitä juttuja... :/ Noh. Koodin pitäisi olla kohtalaisen selkeää ja luettavaa. En väitä kuitenkaan, että koodaustyylini olisi hyvää. Lisäksi en 100% varmasti takaa, että algoritmi on täysin toimiva. Tarkemmin voitte tutkia vaiheita katsomalla Life::update()-metodin sisällön ja tutkimalla sen kutsumia metodeita hieman tarkemmin.
Tässäpä kaikki! Kommentteja/kritiikkiä kiitos! :)
Life.h
#pragma once // "Life.h" #include <map> #include <vector> #include "FunctionPtr.h" /** Taulukon koko x-akselilla. */ #define ARRAY_X_DIMENSION 40 /** Taulukon koko y-akselilla. */ #define ARRAY_Y_DIMENSION 40 class Life { public: /** Sisältää solun x- ja y-indeksit. @notes Välitetään piirtofunktiolle, jotta osataan piirtää oikein. */ struct CellIndex { int x; int y; }; /** Alustaa instanssin. @notes Asettaa jokaisen solun arvoksi FALSE. */ Life(); /** Tuhoaa instanssin. */ ~Life(); /** Päivittää taulukon solut tappaen turhat ja nostaen henkiin tarvittavat. @notes Tätä metodia tulee kutsua joka loopinkierto. */ void update(); /** Pakottaa halutun solun elämään. @param x Halutun solun x-indeksi. @param y Halutun solun y-indeksi. @return FALSE, jos sijoitettava solu on taulukon ulkopuolella, muutoin TRUE. @notes Tällä metodilla asetetaan alkuasetelmat elämälle. */ bool setCellActive(int x, int y); /** Asettaa osoittimen piirtofunktiolle, jota kutsutaan. @notes Funktion pitää palauttaa boolean-tyyppinen arvo ja ottaa parametrikseen const Life::CellIndex& tyyppisen arvon, jossa on piirrettävän solun indeksit. */ void setDrawFunction( FunctionPtr<bool, const CellIndex&> ptr ); private: // METODIT //// /** Ei käytetä. */ Life(const Life& l); /** Ei käytetä. */ Life& operator =(const Life& l); /** Lisää halutulle solulle kaverilistaan yhden merkinnän. @param x Solun x-indeksi. @param y Solun y-indeksi. @notes Palauttaa samalla solun tämänhetkisen tilan. Jos tila on jo TRUE, ei merkintää tehdä enää. @returns Merkittävän solun nykyinen tila. */ inline bool markCell(int x, int y); /** Päivittää "elävät" solut. @notes Tarkistaa eläville soluille, että onko niillä tarpeeksi kavereita elääkseen. Merkitsee samalla tyhjät ympäryssolut, jotta niille tehdään tarkistus, voisivatko ne syttyä eloon. @remarks Tämä on ensimmäinen kohta algoritmissä. */ inline void updateActiveCells(); /** Tarkistaa ei-elävistä merkityistä soluista, voisivatko nämä syttyä elämään. @notes Jos tarkistettavalla solulla on 3 kaveria ympärillä, se syttyy elämään. @remarks Tämä on toinen kohta algoritmissä. */ inline void updateCheckCells(); /** Poistaa kuolevat solut taulukosta. @remarks Tämä on kolmas kohta algoritmissä. */ inline void deleteUnActiveCells(); /** Puhdistaa tapettavien listan ja testattavien listan. @remarks Tämä on neljäs kohta algoritmissä. */ inline void clearAll(); /** Piirtää elävät solut. @notes Käyttää annettua funktio-osoitinta (aka Functor) piirtoon. Jos funktio-osoitinta ei ole asetettu, ei piirretä mitään. @remarks Algoritmin viimeinen eli viides kohta. */ void drawAll(); // LUOKAT //// /** Sisällyttää tietyn solun kaverien määrän. @notes Käytetään päivityskartoissa, kuten listaamaan "elävät" solut ja niiden vaikutuksen alaiset solut eri karttoihin. */ struct Cell { /** Asettaa solun arvot nollille. */ Cell() { x = y = count = 0; } int x; // solun x-indeksi int y; // solun y-indeksi int count; // solun kavereiden määrä }; // ATTRIBUUTIT //// /** Kartta, joka ottaa avaimekseen boolean-tyyppisen arvon osoittimen. @notes Käytetään listaamaan elävät sekä päivitettävät solut. Avain: bool* Säiliö: Cell */ typedef std::map<bool*, Cell> CellMap; /** Vektori, jonka tarkoituksena on säilöä joka päivityskerralla kuolevien solujen osoitteet. @notes Säiliö: bool* */ typedef std::vector<bool*> CellVector; bool mCells[ARRAY_X_DIMENSION][ARRAY_Y_DIMENSION]; // taulukko joka sisältää datan CellMap mActiveCells; // solut, joissa on elämää CellMap mCheckCells; // solut, joissa ei ole elämää, mutta elävät solut ovat vaikuttaneet näihin CellVector mCellDeletionList; // tähän listataan kuolevat solut, lista käydään läpi päiviksen lopuksi tuhoten kuolevat FunctionPtr<bool, const CellIndex&> mDrawFunction; // piirto bool mDrawSetted; // onko piirto asetettu };
Life.cpp
// Life.cpp #include "Life.h" Life::Life() : mDrawSetted(false) { for(int x = 0 ; x < ARRAY_X_DIMENSION ; x++) { for(int y = 0 ; y < ARRAY_Y_DIMENSION ; y++) { mCells[x][y] = false; } } } Life::~Life() { } void Life::updateActiveCells() { for( CellMap::iterator it = mActiveCells.begin() ; it != mActiveCells.end() ; it++ ) { // tarkistetaan, että ei ole NULL if( it->first ) { // otetaan koordinaatit taulukossa ylös (optimointia) int xx = it->second.x; int yy = it->second.y; // nollataan laskuri int count = 0; for(int x = xx-1 ; x <= xx+1 ; x++) { for(int y = yy-1 ; y <= yy+1 ; y++) { // tarkistetaan, ettei merkata tarkistettavana olevaa solua if( y != yy || x != xx ) { count += markCell(x, y); // lisätään lakuriin ja merkataan tyhjät (jos solu on tyhjä, niin palautus on 0) } } } // jos kavereita ei ole oikeaa määrää niin ryhdytään poistotoimenpiteisiin if( count != 2 ) { if( count != 3 ) mCellDeletionList.push_back( it->first ); } } } } bool Life::markCell(int x, int y) { // tarkistetaan, onko taulukon sisässä if( x < 0 || x >= ARRAY_X_DIMENSION || y < 0 || y >= ARRAY_Y_DIMENSION ) return false; // jos solussa on elämää if( mCells[x][y] ) { return true; } // jos ollaan päästy tänne niin solu on olemassa, mutta siinä ei ole elämää // lisätään se päivitettävien karttaan ja lisätään laskuria // huomaa, että jos solu on jo aktiivisten listassa, siihen lisätään tietoa, jos ei ole niin solu luodaan. bool* ptr = &(mCells[x][y]); mCheckCells[ ptr ].count++; mCheckCells[ ptr ].x = x; mCheckCells[ ptr ].y = y; return false; } void Life::updateCheckCells() { // iteroidaan kaikki tarkistettavat for( CellMap::iterator it = mCheckCells.begin() ; it != mCheckCells.end() ; it++ ) { // varmistetaan, ettei ole NULL if( it->first ) { // jos solulla on tarvittava määrä kavereita if( it->second.count == 3 ) { // merkataan taulukkoon trueksi mCells[it->second.x][it->second.y] = true; // lisätään elävien karttaan mActiveCells[ it->first ] = it->second; } } } } void Life::deleteUnActiveCells() { for( CellVector::iterator it = mCellDeletionList.begin() ; it != mCellDeletionList.end() ; it++ ) { mCells[ mActiveCells[*it].x ][ mActiveCells[*it].y ] = false; mActiveCells.erase( *it ); } } void Life::clearAll() { mCheckCells.clear(); mCellDeletionList.clear(); } void Life::update() { updateActiveCells(); updateCheckCells(); deleteUnActiveCells(); clearAll(); if( mDrawSetted ) drawAll(); } bool Life::setCellActive(int x, int y) { // tarkistetaan, onko taulukon sisässä if( x < 0 || x >= ARRAY_X_DIMENSION || y < 0 || y >= ARRAY_Y_DIMENSION ) return false; mCells[x][y] = true; mActiveCells[ &(mCells[x][y]) ].x = x; mActiveCells[ &(mCells[x][y]) ].y = y; return true; } void Life::drawAll() { CellIndex data; for( CellMap::iterator it = mActiveCells.begin() ; it != mActiveCells.end() ; it++ ) { if( it->first ) { data.x = it->second.x; data.y = it->second.y; mDrawFunction( data ); } } } void Life::setDrawFunction(FunctionPtr<bool,const CellIndex&> ptr) { mDrawSetted = true; mDrawFunction = ptr; }
FunctionPtr.h
#pragma once /* 17.1.2007 - Matti Lankinen * 18.1.2007 - Matti Lankinen : lisätty tuki void-parametreille */ /** FunktioPtr-luokan tarvitsema jäsen joka sisältää virtuaalimetodit funktio-osoittimien käsittelyyn. @remarks Tämä on abstrakti kantaluokka josta peritään luokat _SingleBuilder ja _MemberBuilder. */ template <typename ret, typename args> class _BuilderBase { public: _BuilderBase() {} virtual ~_BuilderBase() {} virtual ret operator() (args a) const = 0; virtual _BuilderBase* copy() const = 0; }; /** FunktioPtr-luokan tarvitsema jäsen joka sisältää virtuaalimetodit funktio-osoittimien käsittelyyn. @remarks Tämä on abstrakti kantaluokka josta peritään luokat _SingleBuilder ja _MemberBuilder. @remarks Erikoistapaus void-tyyppiselle parametrille. */ template <typename ret> class _BuilderBase <ret, void> { public: _BuilderBase() {} virtual ~_BuilderBase() {} virtual ret operator() () const = 0; virtual _BuilderBase* copy() const = 0; }; /** FunktioPtr-luokan tarvitsema kapselointi-instanssi funktion osoittimelle. @remarks Periytetty _BuilderBase-luokasta. */ template <typename ret, typename args> class _SingleBuilder : public _BuilderBase<ret, args> { public: typedef ret (*Pointer)(args); _SingleBuilder(Pointer ptr) : myPtr(ptr) { } virtual ret operator() (args a) const { return myPtr(a); } virtual _BuilderBase<ret,args>* copy() const { return new _SingleBuilder<ret,args>( myPtr ); } protected: Pointer myPtr; }; /** FunktioPtr-luokan tarvitsema kapselointi-instanssi funktion osoittimelle. @remarks Periytetty _BuilderBase-luokasta. @remarks Erikoistapaus void-tyyppiselle paluuarvolle. */ template <typename args> class _SingleBuilder<void, args> : public _BuilderBase<void, args> { public: typedef void (*Pointer)(args); _SingleBuilder(Pointer ptr) : myPtr(ptr) { } virtual void operator() (args a) const { myPtr(a); } virtual _BuilderBase<void,args>* copy() const { return new _SingleBuilder<void,args>( myPtr ); } protected: Pointer myPtr; }; /** FunktioPtr-luokan tarvitsema kapselointi-instanssi funktion osoittimelle. @remarks Periytetty _BuilderBase-luokasta. @remarks Erikoistapaus void-tyyppiselle parametrille. */ template <typename ret> class _SingleBuilder<ret, void> : public _BuilderBase<ret, void> { public: typedef ret (*Pointer)(void); _SingleBuilder(Pointer ptr) : myPtr(ptr) { } virtual ret operator() () const { return myPtr(); } virtual _BuilderBase<ret,void>* copy() const { return new _SingleBuilder<ret,void>( myPtr ); } protected: Pointer myPtr; }; /** FunktioPtr-luokan tarvitsema kapselointi-instanssi luokan jäsenfunktion osoittimelle. @remarks Periytetty _BuilderBase-luokasta. */ template <typename ret, typename args, typename T> class _MemberBuilder : public _BuilderBase<ret, args> { public: typedef ret (T::* Pointer)(args); _MemberBuilder(Pointer ptr, T* instance) { myInstance = instance; myPtr = ptr; } virtual ret operator() (args a) const { return (myInstance->*myPtr)(a); } virtual _BuilderBase<ret,args>* copy() const { return new _MemberBuilder<ret,args,T>(myPtr, myInstance); } protected: T* myInstance; Pointer myPtr; }; /** FunktioPtr-luokan tarvitsema kapselointi-instanssi luokan jäsenfunktion osoittimelle. @remarks Periytetty _BuilderBase-luokasta. @remarks Erikoistapaus void-tyyppiselle parametrille. */ template <typename ret, typename T> class _MemberBuilder<ret,void,T> : public _BuilderBase<ret, void> { public: typedef ret (T::* Pointer)(void); _MemberBuilder(Pointer ptr, T* instance) { myInstance = instance; myPtr = ptr; } virtual ret operator() () const { return (myInstance->*myPtr)(); } virtual _BuilderBase<ret,void>* copy() const { return new _MemberBuilder<ret,void,T>(myPtr, myInstance); } protected: T* myInstance; Pointer myPtr; }; /** tämä luokka sisältää halutun tyyppisen funktion pointterin. @remarks pointterin voi luoda funktiosta, tai tietyn luokan instanssin jäsenfunktiosta. Luokka on malliluokka, joten siitä vuo luoda minkä tyyppisiä funktio-osoittimia tahansa. Funktiolla on paluu- arvo sekä YKSI (1) parametri, jotka käyttäjä saa itse valita instanssia luodessaan. Jäsenfunktion voi ottaa minkä tahansa luokan instanssista! @remarks Huom! void-tyyppiset paluuarvot eivät ole sallittuja! */ template <typename ret, typename args> class FunctionPtr { public: typedef ret (*Pointer)(args); /** Luo tyhjän instanssin */ FunctionPtr() : ptrCapsule(0) { } /** Luo alustetun instanssin olemassa olevasta osoittimesta. @param ptr Osoitin funktioon. */ FunctionPtr( Pointer ptr ) { ptrCapsule = new _SingleBuilder<ret,args>(ptr); } /** Luo alustetun instanssin halutun luokan olion jäsenfunktioista. @param (ret (T::*f)(args)) osoitin luokan jäsenfunktioon @param instance Osoitin olioista, jonka funktio toteutetaan. */ template <class T> FunctionPtr(ret (T::*f)(args), T* instance) { ptrCapsule = new _MemberBuilder<ret,args,T>(f, instance); } /** Luo uuden instanssin käyttäen käyttäen kopioitavan luokan _BuilderBase-instanssia. @remarks Tämä ei ole tavalliselle käyttäjälle sillä kyseiset instanssit ovat vain FunctionPtr-luokan saavutettavissa. Tätä käytetäänkin vain kopiomuodostimessa FunctionPtr-luokan instansseille, joita käyttäjä voi käyttää. @param capsule Kapseli, joka sisältää toiminnallisuuden. */ FunctionPtr( _BuilderBase<ret,args>* capsule ) { ptrCapsule = capsule; } /** Luo uuden instanssin kopioiden sen annetusta instanssista. @remarks Luodun instanssin ()-operaattori tekee siis täysin samat asiat, mitä kopioitavan instanssin operaattori. @param other Instanssi, josta tiedot kopioidaan. */ FunctionPtr(const FunctionPtr& other) : ptrCapsule(0) { if( other.getPtrCapsule() ) ptrCapsule = other.getPtrCapsule()->copy(); } /** Tuhoaa instanssin ja vapauttaa mahdollisesti osoitinkapselin varaamaan vapaan muistin. */ ~FunctionPtr() { release(); } /** Suorittaa instanssissa olevan funktion. @remarks Sinun täytyy olla varma, että instanssissa on varmasti funktio-osoitin sillä tämä ei tarkasta sitä ja seuraukset huolimattomasta käytöstä voivat olla vakavat. @param a Halutun tyyppinen instanssi, jonka funktio ottaa parametrikseen. @returns Halutun tyyppisen instanssin, jonka funktio palauttaa. */ ret operator() (args a) const { return (*ptrCapsule)(a); } /** Palauttaa käyttäjälle instanssissa olevan kapselin osoitteen. @remarks Palautettava arvo on kantaluokka, mutta instanssissa saattaa olla jompi kumpi tämän luokan periytyvistä luokista riippuen instanssin luontitavasta. @returns Kapseli, joka sisältää osoittimen. */ _BuilderBase<ret,args>* getPtrCapsule() const { return ptrCapsule; } /** Tuhoaa instanssissa olevan funktiokapselin muistista. */ void release() { if( ptrCapsule ) delete ptrCapsule; ptrCapsule = 0; } /** Kopioi intanssille tiedot toisesta instanssista. @param other Instanssi, josta tiedot kopioidaan. @returns Kutsuvan instnssin viittaus, jotta operaattoria voidaan käyttää peräkkäin. */ FunctionPtr& operator= (const FunctionPtr& other) { release(); ptrCapsule = other.getPtrCapsule()->copy(); return *this; } /** Kopioi intanssille tiedot kapselista. @param other Kapseli, josta tiedot kopioidaan. @returns Kutsuvan instnssin viittaus, jotta operaattoria voidaan käyttää peräkkäin. */ FunctionPtr& operator= (_BuilderBase<ret,args>* capsule) { release(); ptrCapsule = capsule; return *this; } private: _BuilderBase<ret,args>* ptrCapsule; // funktio-osoittimen sisältävä luokka }; /** tämä luokka sisältää halutun tyyppisen funktion pointterin. @remarks pointterin voi luoda funktiosta, tai tietyn luokan instanssin jäsenfunktiosta. Luokka on malliluokka, joten siitä vuo luoda minkä tyyppisiä funktio-osoittimia tahansa. Funktiolla on paluu- arvo sekä YKSI (1) parametri, jotka käyttäjä saa itse valita instanssia luodessaan. Jäsenfunktion voi ottaa minkä tahansa luokan instanssista! @remarks Erikoistapaus void-tyyppiselle parametrille. */ template <typename ret> class FunctionPtr<ret, void> { public: typedef ret (*Pointer)(void); /** Luo tyhjän instanssin */ FunctionPtr() : ptrCapsule(0) { } /** Luo alustetun instanssin olemassa olevasta osoittimesta. @param ptr Osoitin funktioon. */ FunctionPtr( Pointer ptr ) { ptrCapsule = new _SingleBuilder<ret,void>(ptr); } /** Luo alustetun instanssin halutun luokan olion jäsenfunktioista. @param (ret (T::*f)(args)) osoitin luokan jäsenfunktioon @param instance Osoitin olioista, jonka funktio toteutetaan. */ template <class T> FunctionPtr(ret (T::*f)(void), T* instance) { ptrCapsule = new _MemberBuilder<ret,void,T>(f, instance); } /** Luo uuden instanssin käyttäen käyttäen kopioitavan luokan _BuilderBase-instanssia. @remarks Tämä ei ole tavalliselle käyttäjälle sillä kyseiset instanssit ovat vain FunctionPtr-luokan saavutettavissa. Tätä käytetäänkin vain kopiomuodostimessa FunctionPtr-luokan instansseille, joita käyttäjä voi käyttää. @param capsule Kapseli, joka sisältää toiminnallisuuden. */ FunctionPtr( _BuilderBase<ret,void>* capsule ) { ptrCapsule = capsule; } /** Luo uuden instanssin kopioiden sen annetusta instanssista. @remarks Luodun instanssin ()-operaattori tekee siis täysin samat asiat, mitä kopioitavan instanssin operaattori. @param other Instanssi, josta tiedot kopioidaan. */ FunctionPtr(const FunctionPtr& other) : ptrCapsule(0) { if( other.getPtrCapsule() ) ptrCapsule = other.getPtrCapsule()->copy(); } /** Tuhoaa instanssin ja vapauttaa mahdollisesti osoitinkapselin varaamaan vapaan muistin. */ ~FunctionPtr() { release(); } /** Suorittaa instanssissa olevan funktion. @remarks Sinun täytyy olla varma, että instanssissa on varmasti funktio-osoitin sillä tämä ei tarkasta sitä ja seuraukset huolimattomasta käytöstä voivat olla vakavat. @param a Halutun tyyppinen instanssi, jonka funktio ottaa parametrikseen. @returns Halutun tyyppisen instanssin, jonka funktio palauttaa. */ ret operator() () const { return (*ptrCapsule)(); } /** Palauttaa käyttäjälle instanssissa olevan kapselin osoitteen. @remarks Palautettava arvo on kantaluokka, mutta instanssissa saattaa olla jompi kumpi tämän luokan periytyvistä luokista riippuen instanssin luontitavasta. @returns Kapseli, joka sisältää osoittimen. */ _BuilderBase<ret,void>* getPtrCapsule() const { return ptrCapsule; } /** Tuhoaa instanssissa olevan funktiokapselin muistista. */ void release() { if( ptrCapsule ) delete ptrCapsule; ptrCapsule = 0; } /** Kopioi intanssille tiedot toisesta instanssista. @param other Instanssi, josta tiedot kopioidaan. @returns Kutsuvan instnssin viittaus, jotta operaattoria voidaan käyttää peräkkäin. */ FunctionPtr& operator= (const FunctionPtr& other) { release(); ptrCapsule = other.getPtrCapsule()->copy(); return *this; } /** Kopioi intanssille tiedot kapselista. @param other Kapseli, josta tiedot kopioidaan. @returns Kutsuvan instnssin viittaus, jotta operaattoria voidaan käyttää peräkkäin. */ FunctionPtr& operator= (_BuilderBase<ret,void>* capsule) { release(); ptrCapsule = capsule; return *this; } private: _BuilderBase<ret,void>* ptrCapsule; // funktio-osoittimen sisältävä luokka };
Tässä on PHP-versio samasta pelistä:
http://koti.mbnet.fi/pllk/muut/elama.php
<?php // laskee elävien naapurien määrän function maara($x, $y, $m) { global $maailma; $tulos = 0; $tulos += $maailma[$x+1][$y][$m]; $tulos += $maailma[$x+1][$y+1][$m]; $tulos += $maailma[$x][$y+1][$m]; $tulos += $maailma[$x-1][$y+1][$m]; $tulos += $maailma[$x-1][$y][$m]; $tulos += $maailma[$x-1][$y-1][$m]; $tulos += $maailma[$x][$y-1][$m]; $tulos += $maailma[$x+1][$y-1][$m]; return $tulos; } // siirtyy seuraavaan tilanteeseen function muutos() { global $maailma, $vanha, $uusi; for ($i = 0; $i < 10; $i++) { for ($j = 0; $j < 10; $j++) { $tila = $maailma[$j][$i][$vanha]; $maara = maara($j, $i, $vanha); // selvitetään uusi tila $maailma[$j][$i][$uusi] = 0; if ($maara == 3) { $maailma[$j][$i][$uusi] = 1; } if ($tila == 1 && $maara == 2) { $maailma[$j][$i][$uusi] = 1; } } } // vaihdetaan taulukoiden tehtävät $vali = $uusi; $uusi = $vanha; $vanha = $vali; } // näyttää nykyisen tilanteen function nayta() { global $maailma, $vanha, $uusi; echo "<table border>"; for ($i = 0; $i < 10; $i++) { echo "<tr height=20>"; for ($j = 0; $j < 10; $j++) { if ($maailma[$j][$i][$vanha] == 1) { echo "<td width=20 bgcolor=black> </td>"; } else { echo "<td width=20 bgcolor=white> </td>"; } } echo "</tr>"; } echo "</table><br>"; } $vanha = 0; $uusi = 1; // arvotaan ruudukkoon elämää for ($i = 0; $i < 30; $i++) { $maailma[rand(0,9)][rand(0,9)][$vanha] = 1; } // näytetään peräkkäisiä tilanteita for ($i = 0; $i < 20; $i++) { nayta(); muutos(); } ?>
Eiks sulla oo tullu pieni looginen virhe tuossa rivillä 251??
Antti veti kyllä pidemmän korren :D
Aika .. ilmavaa koodia :/
...sen siitä saa kun käyttää liian "kehittyneitä" välineitä koodaamiseen, ja ajattelee liian vaikeasti :)
Yksinkertainen ajattelu on kaiken A ja O.
Kannattaa tutustua myös HashLife-algoritmiin (http://www.ddj.com/dept/ai/184406478) ja sitä soveltavaan peliin
(http://golly.sourceforge.net/). Vie pelin nopeuden uusiin ulottuvuuksiin.
T.M.: Koodi on monimutkaisempaa sen vuoksi, että se laskee VAIN päivitettävät solut. Antin koodi sitä vastoin käy koko taulukon läpi. Jos taulukon koko olisi esimerkiksi 10000x10000, tulisi ero paljon selkeämmin esille. ;)
Hyvä, että olet selittänyt algoritmin toiminnan tarkasti. Ohjelmasi nopeus taitaa riippua aika paljon siitä, kuinka paljon ruudukossa on elämää. Jos ruudukossa on vain muutama elävä solu, ohjelmasi on todella tehokas. Mutta jos ruudukko kuhisee elämää, minun tekeleeni saattaa olla jopa nopeampi.
Kyllä. Mutta jos ruudukko on 10000x10000 ja siinä on about 200 (eli siis suhteessa kokoon hyvin vähän) elävää solua, voittaa minun tekeleeni nopeudessa. Sinähän käyt koko taulukon läpi, joten se ei katso elävien solujen määrää vaan solujen tarkistusten määrä on aina vakio. Minulla tarkistetaan ne solut, joissa on elämää ja johon voisi syttyä elämää.
Minulla oli vähän vastaavan tyyppinen projekti joskus. Tosin se ei ollut peli vaan sen oli vain tarkoitus simuloida evuluutiota.
Koodia en tutkinut lainkaan mutta 10 pistettä ja papukaija merkki tuosta hyvästä työstä algoritmin toiminnan selittämisestä. Tuosta pitää ottaa ihan oppia.
Tämä peli kuvastaakin hyvin sitä elämän moniulotteisuutta ja eksistoivia kompleksisia manufaktuurisia rakenteita, ja ne kaikki on saatu hyvin sovitettua loogisen algoritmiikan keinoin näinkin tiiviiseen pakettiin. Jatka samaan malliin!
jones7 kirjoitti:
eksistoivia kompleksisia manufaktuurisia rakenteita
Voiko joku suomentaa tuon mulle? :)
Käännä vaan kaikki sanat takaisin englanniksi ja suomenna uudestaan:
"Olemassaoleva monimutkainen (uutta) tuottava rakenne"
Tuosta jones7:n viestistä ei oikein välity se, että itse peli on keksitty 70-luvulla, eivätkä ne elämän manufaktuurit minkään olio-ohjelmoinnin mukana ilmestyneet. Ihmeellisintä itse pelissä on ainakin omasta mielestäni se, että sen on todistettu olevan universaali tietokone, eli erilaisia kuvioita pelikentälle piirtämällä voi luoda peliin simulaation mistä tahansa tietokoneohjelmasta - jos tehoja riittää.
Esimerkkinä alkulukuja "tulostava" Game of Life -kone:
http://radicaleye.com/lifepage/patterns/primes.
Aihe on jo aika vanha, joten et voi enää vastata siihen.