Kirjautuminen

Haku

Tehtävät

Keskustelu: Koodit: C++: Elämän peli

Sivun loppuun

Mazzimo [04.02.2007 14:53:59]

#

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

};

Antti Laaksonen [04.02.2007 19:34:31]

#

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>&nbsp;</td>";
            } else {
                echo "<td width=20 bgcolor=white>&nbsp;</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();
}

?>

jones7 [04.02.2007 22:43:44]

#

Eiks sulla oo tullu pieni looginen virhe tuossa rivillä 251??

kaviaari [04.02.2007 23:01:41]

#

Antti veti kyllä pidemmän korren :D

Aika .. ilmavaa koodia :/

T.M. [05.02.2007 13:37:53]

#

...sen siitä saa kun käyttää liian "kehittyneitä" välineitä koodaamiseen, ja ajattelee liian vaikeasti :)
Yksinkertainen ajattelu on kaiken A ja O.

os [05.02.2007 14:01:43]

#

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.

Mazzimo [05.02.2007 16:05:59]

#

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. ;)

Antti Laaksonen [05.02.2007 20:44:53]

#

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.

Mazzimo [06.02.2007 17:14:24]

#

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ää.

Lahha [06.02.2007 21:33:42]

#

Minulla oli vähän vastaavan tyyppinen projekti joskus. Tosin se ei ollut peli vaan sen oli vain tarkoitus simuloida evuluutiota.

E.K.Virtanen [07.02.2007 23:03:32]

#

Koodia en tutkinut lainkaan mutta 10 pistettä ja papukaija merkki tuosta hyvästä työstä algoritmin toiminnan selittämisestä. Tuosta pitää ottaa ihan oppia.

jones7 [18.02.2007 01:45:11]

#

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!

T.M. [19.02.2007 17:46:32]

#

jones7 kirjoitti:

eksistoivia kompleksisia manufaktuurisia rakenteita

Voiko joku suomentaa tuon mulle? :)

os [23.02.2007 22:26:37]

#

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.html


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta