/*	simlife.c -- Programm unter der GPL, (c) RTC @ 2003		*/
/*	Programm simuliert einen zellularen Automaten			*/

/* ---- Einbinden der Header-Dateien ---------------------------------- */
#include <graphics.h>
#include <stdlib.h>
#include <time.h>

/* ---- Standard-Werte definieren ------------------------------------- */
#define		XMAX		800
#define		YMAX		600
#define		XLAENGE		10
#define		YLAENGE		8
#define		XANZ		200
#define		YANZ		200
#define		LEBEND		1
#define		TOT		0
#define		AKTUELL		0
#define		TEMP		1

/* ---- modulglobale Variablen deklarieren ---------------------------- */
int xmax, ymax, xfelder, yfelder;
int mom_feld[XANZ][YANZ], temp_feld[XANZ][YANZ];


static void setze_feld(int feld, int status, int x, int y){
	setviewport(x*XLAENGE+1, y*YLAENGE+1, x*XLAENGE+XLAENGE-1, y*YLAENGE+YLAENGE-1);
	if(status==LEBEND){
		clearviewport(BLUE);
		if(feld==AKTUELL)
			mom_feld[x][y]=LEBEND;
		else
			temp_feld[x][y]=LEBEND;
	}
	else{
		clearviewport(LIGHTGRAY);
		if(feld==AKTUELL)
			mom_feld[x][y]=TOT;
		else
			temp_feld[x][y]=TOT;
	}
}

static int pruefe_geburt(int x, int y){
	int xstart, ystart, xende, yende, i, j;
	int lebende=0;

	xstart=x-1;
	ystart=y-1;
	xende=x+1;
	yende=y+1;

	if(x==0) xstart=0;
	if(x==xfelder-1) xende=xfelder-1;
	if(y==0) ystart=0;
	if(y==yfelder-1) yende=yfelder-1;

	for(i=ystart; i<=yende; ++i)
		for(j=xstart; j<=xende; ++j)
			if(mom_feld[j][i]==LEBEND)
				if(j!=x || i!=y)
					++lebende;

	if(lebende==3)
		return 1;

	return 0;
}

static int pruefe_tot(int x, int y){
	int xstart, ystart, xende, yende, i, j;
	int lebende=0;

	xstart=x-1;
	ystart=y-1;
	xende=x+1;
	yende=y+1;

	if(x==0) xstart=0;
	if(x==xfelder-1) xende=xfelder-1;
	if(y==0) ystart=0;
	if(y==yfelder-1) yende=yfelder-1;

	for(i=ystart; i<=yende; ++i)
		for(j=xstart; j<=xende; ++j)
			if(mom_feld[j][i]==LEBEND)
				if(j!=x || i!=y)
					++lebende;
	if(lebende>3 || lebende<2)
		return 1;

	return 0;
}

/* ---- Funktion zur Initialisierung des Feldes ----------------------- */
void init_feld(int argc, char *argv[]){
	int x, y;
	char abfrage;

	outtextxy(10, 10, XMAX, YMAX,
		"Simulation eines zellularen Automaten \"Game of life\"\n"
		"====================================\n\n"
		"Programm simuliert das Leben von x Zellen, wobei folgende Regeln gelten:\n"
		" 1. Geburt: eine Zelle wird \"geboren\", wenn drei ihrer Nachbarzellen leben;\n"
		" 2. Tod durch Überbevölkerung: eine Zelle \"stirbt\", wenn 4 oder mehr\n"
		"    ihrer Nachbarzellen leben;\n"
		" 3. Tod durch Vereinsamung: eine Zelle \"stirbt\", wenn 1 oder keine\n"
		"    ihrer Nachbarzellen lebt.\n\n"
		"Sie können wählen, ob sie die Anfangspopulation selbst per Maus oder zufällig\n"
		"durch den Computer bestimmen lassen möchten.\n\n"
		"Abbruch der Simulation durch beliebigen Tastendruck der Tastatur oder Maus\n\n\n\n"
		"[ENTER] zum Starten");
	getch();

	xfelder = getint("Geben sie die horizontale Anzahl der Felder an:");
	yfelder = getint("Geben sie die vertikale Anzahl der Felder an: ");

	closegraph();
	xmax=xfelder*XLAENGE;
	ymax=yfelder*YLAENGE;
	initgraph(xmax, ymax+20);
	clearviewport(WHITE);
	mouse_setwindow(0, 0, xmax, ymax);

	for(y=0; y<yfelder; ++y)
		for(x=0; x<xfelder; ++x){
			setze_feld(AKTUELL, TOT, x, y);
			setze_feld(TEMP, TOT, x, y);
		}
	
	setviewport(5, ymax, xmax, ymax+20);
	clearviewport(WHITE);
	outtextxy(5, ymax, xmax, ymax+20, "Treffen sie eine Auswahl...");

	abfrage = getcharacter(	"Wollen Sie die Anfangspopulation:\n"
				" (1) selber per Maus definieren oder\n"
				" (2) per Zufall ermitteln lassen (Standard)?\n");
	if(abfrage=='1'){
		setviewport(5, ymax, xmax, ymax+20);
		clearviewport(WHITE);
		outtextxy(5, ymax, xmax, ymax+20, "Lese Zellen ein (Zelle löschen mit rechter, Beenden mit mittlerer Maustaste)");
		int xkoord, ykoord;
		while(!mouse_mid()){
			if(mouse_left()){
				mouse_getpos(&xkoord, &ykoord);
				x = xkoord/XLAENGE;
				y = ykoord/YLAENGE;
				setze_feld(AKTUELL, LEBEND, x, y);
			}
			if(mouse_right()){
				mouse_getpos(&xkoord, &ykoord);
				x = xkoord/XLAENGE;
				y = ykoord/YLAENGE;
				setze_feld(AKTUELL, TOT, x, y);
			}
			if(kbhit()){
				getch();
				break;
			}
		}
	}
	else {
		int i, anzahl, xzuf, yzuf;
		srand(time(NULL));

		do{
			anzahl = getint("Wieviele Zellen sollen leben (0 < Anzahl < %d)?", xfelder*yfelder);
		}while(anzahl<=0 || anzahl>=xfelder*yfelder);

		for(i=1; i<=anzahl; ++i){
			do{
				xzuf = rand()%xfelder;
				yzuf = rand()%yfelder;
			}while(mom_feld[xzuf][yzuf]==LEBEND);
			setze_feld(AKTUELL, LEBEND, xzuf, yzuf);
		}

		setviewport(5, ymax, xmax, ymax+20);
		clearviewport(WHITE);
		outtextxy(5, ymax, xmax, ymax+20, "Zellen erzeugt, beliebige Taste oder Maus zum Fortfahren...");
		do{
			if(kbhit()){
				getch();
				break;
			}
		}while(!mouse_button());
	}
}

int sim_life(){
	int x, y, lebende=0, veraendert=0;

	setviewport(5, ymax, xmax, ymax+20);
	clearviewport(WHITE);
	outtextxy(5, ymax, xmax, ymax+20, "Führe \"Game of life\" durch...");

	for(y=0; y<yfelder; ++y){
		for(x=0; x<xfelder; ++x){
			if(mom_feld[x][y]==TOT){
				if(pruefe_geburt(x, y)==1){
					setze_feld(TEMP, LEBEND, x, y);
					++lebende;
				}
				else
					setze_feld(TEMP, TOT, x, y);
			}
			else{
				if(pruefe_tot(x, y)==1)
					setze_feld(TEMP, TOT, x, y);
				else{
					setze_feld(TEMP, LEBEND, x, y);
					++lebende;
				}
			}
		}
	}

	for(y=0; y<=yfelder; ++y)
		for(x=0; x<=xfelder; ++x){
			if(mom_feld[x][y] != temp_feld[x][y])
				veraendert=1;
			mom_feld[x][y]=temp_feld[x][y];
			temp_feld[x][y]=TOT;
		}

	if(lebende==0){
		setviewport(5, ymax, xmax, ymax+20);
		clearviewport(WHITE);
		outtextxy(5, ymax, xmax, ymax+20, "Keine lebenden Zellen mehr vorhanden! Beliebige Taste zum Fortfahren...");
		return 1;
	}
	if(veraendert==0){
		setviewport(5, ymax, xmax, ymax+20);
		clearviewport(WHITE);
		outtextxy(5, ymax, xmax, ymax+20, "Keine weiteren Populationsveränderungen! Beliebige Taste zum Fortfahren...");
		return 2;
	}
	return 0;
}


int main(int argc, char *argv[]){
	initgraph(XMAX, YMAX);
	clearviewport(LIGHTGRAY);

	init_feld(argc, argv);
	do{
		if(sim_life()!=0){
			while(!kbhit() && !mouse_button());
			break;
		}
		delay(100);
	}while(!mouse_button() && !kbhit());
	
	closegraph();
	
	return 0;
}


