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.