Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

589 řádky
13 KiB

/* GameOfLife - Conway's Game of Life in 3D
*
* Copyright (c) 2008 by Sebastian Lohff, seba@seba-geek.de
* http://www.seba-geek.de
*
* This file is part of GameOfLife.
*
* GameOfLife 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 2 of the License, or
* (at your option) any later version.
*
* GameOfLife 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 GameOfLife. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gameoflife.h"
GameOfLife::GameOfLife(int _x, int _y) {
init();
x = _x;
y = _y;
allocate();
}
GameOfLife::GameOfLife(std::string file) {
init();
load(file);
}
GameOfLife::GameOfLife(int _x, int _y, int r_life, int r_dead) {
init();
fillRandom(_x, _y, r_life, r_dead);
}
void GameOfLife::init() {
view3d = true;
feld = 0;
x = y = 0;
generation = 0;
thickness = 0.5f;
cellwidth = 1.0f;
torus = true;
fullcelluse = true;
radius = 0.3f;
height = 1.0f;
parts = 12;
sectobuild = 0.91f;
b_secdone = 0.0f;
secpertick = 1.0f;
t_secdone = 0.0f;
editmode = false;
// Calc cylinder sinvals
sinval = new float[parts];
cosval = new float[parts];
for(int i=0; i<parts; i++) {
sinval[i] = radius * sin(segl::deg2rad(360.0f/parts*i));
cosval[i] = radius * cos(segl::deg2rad(360.0f/parts*i));
}
quad = gluNewQuadric();
}
void GameOfLife::allocate() {
feld = new State*[x];
for(int i=0; i<x; i++) {
feld[i] = new State[y];
// clean it
for(int j=0; j<y; j++)
feld[i][j] = dead;
}
}
void GameOfLife::cleanup() {
if(feld) {
for(int i=0; i<x; i++) {
delete[](feld[i]);
}
delete(feld);
feld = 0;
}
}
void GameOfLife::renderBrett() {
float x1 = cellwidth*x / 2.0f;
float y1 = thickness / 2.0f;
float z1 = cellwidth*y / 2.0f;
// Brettquader
GLfloat mat_diffuse[] = { 1.0f, 0.0f, 0.0f, 1.0f };
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glColor3f(1.0f, 0.0f, 0.0f);
glBegin(GL_QUADS);
// Platte
glNormal3f(0.0f, 1.0f, 0.0f);
glVertex3f(-x1, y1, -z1);
glVertex3f( x1, y1, -z1);
glVertex3f( x1, y1, z1);
glVertex3f(-x1, y1, z1);
// Boden
glNormal3f(0.0f,-1.0f, 0.0f);
glVertex3f(-x1, -y1, -z1);
glVertex3f( x1, -y1, -z1);
glVertex3f( x1, -y1, z1);
glVertex3f(-x1, -y1, z1);
// Seiten
glNormal3f(0.0f, 0.0f, 1.0f);
glVertex3f(-x1, -y1, z1);
glVertex3f( x1, -y1, z1);
glVertex3f( x1, y1, z1);
glVertex3f(-x1, y1, z1);
glNormal3f(0.0f, 0.0f,-1.0f);
glVertex3f(-x1, -y1,-z1);
glVertex3f( x1, -y1,-z1);
glVertex3f( x1, y1,-z1);
glVertex3f(-x1, y1,-z1);
glNormal3f(-1.0f, 0.0f, 0.0f);
glVertex3f(-x1, -y1, z1);
glVertex3f(-x1, y1, z1);
glVertex3f(-x1, y1,-z1);
glVertex3f(-x1, -y1,-z1);
glNormal3f(1.0f, 0.0f, 0.0f);
glVertex3f( x1, -y1, z1);
glVertex3f( x1, y1, z1);
glVertex3f( x1, y1,-z1);
glVertex3f( x1, -y1,-z1);
glEnd();
//Gitter
glPushMatrix();
mat_diffuse[0] = 0.0f;
mat_diffuse[1] = 1.0f;
mat_diffuse[2] = 0.0f;
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glColor3f(0.0f, 1.0f, 0.0f);
for(int a=0; a<1; a++) {
glTranslatef(0.0f, 0.01f, 0.0f);
glBegin(GL_LINES);
glNormal3f(0.0f, 1.0f, 0.0f);
for(float i=-x1; i<=x1; i+=cellwidth) {
glVertex3f(i, y1, -z1);
glVertex3f(i, y1, z1);
}
for(float i=-z1; i<=z1; i+=cellwidth) {
glVertex3f(-x1, y1, i);
glVertex3f( x1, y1, i);
}
glEnd();
}
glPopMatrix();
}
void GameOfLife::renderStein(State s) {
float sin1, sin2 = sinval[0], cos1, cos2 = cosval[0];
float height2 = 0.0f;
switch(s) {
case alife:
height2 = height;
break;
case born:
height2 = height * (b_secdone/sectobuild);
break;
case dies:
height2 = height * ((sectobuild-b_secdone)/sectobuild);
break;
case dead:
// unlikely to happen...
height2 = 0.0f;
break;
}
if(height2<0.001f)
return;
glColor3f(0.2f, 0.2, 0.9f);
// for(float i=0, step=360.0f/parts; i<360.0f; i+= step) {
for(int i=0; i<parts; i++) {
sin1 = sin2;
cos1 = cos2;
sin2 = sinval[(i+1)%parts];
cos2 = cosval[(i+1)%parts];
// sin2 = radius * sin(deg2rad(i+step));
// cos2 = radius * cos(deg2rad(i+step));
GLfloat mat_diffuse[] = { 0.2f, 0.2, 0.9f, 1.0f };
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
/*
glPushMatrix();
glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
gluCylinder(quad, radius, radius, height2, 12, 1);
glTranslatef(0.0f, 0.0f, height2);
gluDisk(quad, 0.0f, radius, 12, 1);
glPopMatrix();
*/
glBegin(GL_QUADS);
glNormal3f(0.0f, 0.0f,-1.0f);
glVertex3f(-radius, 0.0f, -radius);
glVertex3f(-radius, height2,-radius);
glVertex3f( radius, height2,-radius);
glVertex3f( radius, 0.0f, -radius);
glNormal3f(0.0f, 0.0f, 1.0f);
glVertex3f( radius, 0.0f, radius);
glVertex3f( radius, height2, radius);
glVertex3f(-radius, height2, radius);
glVertex3f(-radius, 0.0f, radius);
glNormal3f(-1.0f, 0.0f, 0.0f);
glVertex3f(-radius, 0.0f, -radius);
glVertex3f(-radius, 0.0f, radius);
glVertex3f(-radius, height2, radius);
glVertex3f(-radius, height2,-radius);
glNormal3f( 1.0f, 0.0f, 0.0f);
glVertex3f( radius, 0.0f, radius);
glVertex3f( radius, 0.0f, -radius);
glVertex3f( radius, height2,-radius);
glVertex3f( radius, height2, radius);
glNormal3f(0.0f, 1.0f, 0.0f);
glVertex3f(-radius, height2, radius);
glVertex3f( radius, height2, radius);
glVertex3f( radius, height2,-radius);
glVertex3f(-radius, height2,-radius);
glEnd();
/*
glBegin(GL_TRIANGLES);
// Boden Unten
glNormal3f(0.0f,-1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(sin1, 0.0f, cos1);
glVertex3f(sin2, 0.0f, cos2);
// Boden Oben
glNormal3f(0.0f, 1.0f, 0.0f);
glVertex3f(0.0f, height2, 0.0f);
glVertex3f(sin1, height2, cos1);
glVertex3f(sin2, height2, cos2);
//Seite
glNormal3f((sin1+sin2)/2.0f, 0.0f, (cos1+cos2)/2.0f);
glVertex3f(sin1, 0.0f, cos1);
glVertex3f(sin2, 0.0f, cos2);
glVertex3f(sin2, height2, cos2);
glNormal3f((sin1+sin2)/2.0f, 0.0f, (cos1+cos2)/2.0f);
glVertex3f(sin2, height2, cos2);
glVertex3f(sin1, height2, cos1);
glVertex3f(sin1, 0.0f, cos1);
glEnd();
*/
}
}
void GameOfLife::translateTo(int _x, int _y) {
glTranslatef(-cellwidth/2.0f*x+cellwidth*_x+cellwidth/2.0f, thickness/2.0f, -cellwidth/2.0f*y+cellwidth*_y+cellwidth/2.0f);
}
void GameOfLife::lockStates() {
for(int i=0; i<x; i++) {
for(int j=0; j<y; j++) {
if(feld[i][j]==dies)
feld[i][j] = dead;
else if(feld[i][j]==born)
feld[i][j] = alife;
}
}
}
void GameOfLife::set3D(bool on) {
view3d = on;
}
bool GameOfLife::load(std::string file) {
std::ifstream data(file.c_str());
if(!data)
return false;
cleanup();
std::string tmp;
data >> x;
data >> y;
data >> torus;
if(x<=0 || y <=0)
return false;
allocate();
int j=0;
getline(data, tmp); // Remove \n
while(getline(data, tmp) && j<y) {
for(int i=0; i<x && i<(int)tmp.length(); i++) {
feld[i][j] = (tmp[i]=='1') ? born : dead;
}
j++;
}
return true;
}
bool GameOfLife::save(std::string file) {
std::ofstream data(file.c_str());
if(!data)
return false;
data << x << " " << y << " " << (torus?1:0) << std::endl;
for(int j=0; j<y; j++) {
for(int i=0; i<x; i++) {
data << ((feld[i][j]==alife||feld[i][j]==born) ? '1' : '0');
}
data << std::endl;
}
return true;
}
void GameOfLife::fillRandom(int _x, int _y, int r_life, int r_dead) {
cleanup();
x = _x;
y = _y;
allocate();
srand(time(0));
if(r_life+r_dead==0) {
r_life = rand()%100 + 1;
r_dead = rand()%100 + 1;
}
float ratio = r_life/(float)(r_life+r_dead);
for(int i=0; i<x; i++) {
for(int j=0; j<y; j++) {
if((rand()%100)/100.0f < ratio) {
feld[i][j] = born;
} else {
// should be set to dead, but to be sure...
feld[i][j] = dead;
}
}
}
}
// void GameOfLife::toggle(int _x, int _y, State stat) {
// if(feld && _x>=0 && _x<x && _y>=0 && _y<y) {
// feld[_x][_y] = stat;
// }
// }
// const bool** GameOfLife::getFeld() {
// return (const bool**)feld;
// }
void GameOfLife::setEditMode(bool e) {
editmode = e;
if(e) {
lockStates();
} else {
}
}
void GameOfLife::move(int up, int right) {
if(up>0)
setpos.y = ((int)setpos.y+y+1)%y;
else if(up<0)
setpos.y = ((int)setpos.y+y-1)%y;
if(right>0)
setpos.x = ((int)setpos.x+x+1)%x;
else if(right<0)
setpos.x = ((int)setpos.x+x-1)%x;
}
void GameOfLife::toggle() {
if(feld[(int)setpos.x][(int)setpos.y]==born||feld[(int)setpos.x][(int)setpos.y]==alife) {
feld[(int)setpos.x][(int)setpos.y] = dead;
} else {
feld[(int)setpos.x][(int)setpos.y] = alife;
}
}
void GameOfLife::render(float sec) {
if(!feld)
return;
// Cylinder-building-timer
if(b_secdone<=sectobuild) {
b_secdone += sec;
if(b_secdone>=sectobuild) {
lockStates();
}
}
// Tick-timer
t_secdone += sec;
if(t_secdone>=secpertick) {
tick();
t_secdone = 0.0f;
b_secdone = 0.0f;
}
if(view3d) {
renderBrett();
for(int j=0; j<y; j++) {
for(int i=0; i<x; i++) {
if(feld[i][j]!=dead) {
glPushMatrix();
translateTo(i, j);
renderStein(feld[i][j]);
glPopMatrix();
}
if(editmode) {
if(i==setpos.x && j==setpos.y) {
glPushMatrix();
float mat_diffuse[3] = { 0.0f, 1.0f, 0.0f };
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
translateTo(i, j);
glTranslatef(0.0f, 0.01f, 0.0f);
glBegin(GL_QUADS);
glColor3f(0.0f, 1.0f, 0.0f);
glNormal3f(0.0f, 1.0f, 0.0f);
glVertex3f(-cellwidth/2.0f, 0.0f, cellwidth/2.0f);
glVertex3f( cellwidth/2.0f, 0.0f, cellwidth/2.0f);
glVertex3f( cellwidth/2.0f, 0.0f,-cellwidth/2.0f);
glVertex3f(-cellwidth/2.0f, 0.0f,-cellwidth/2.0f);
glEnd();
glPopMatrix();
}
}
}
}
} else {
SDL_Surface *screen = SDL_GetVideoSurface();
float wperp = screen->w /(float)x;
float hperp = screen->h /(float)y;
float border;
glPushMatrix();
if(wperp<hperp) {
glTranslatef(0.0f, (hperp-wperp)*y/2.0f, 0.0f);
hperp = wperp;
} else {
glTranslatef((wperp-hperp)*x/2.0f, 0.0f, 0.0f);
}
border = 0.0f;
glBegin(GL_QUADS);
for(int j=0; j<y; j++) {
for(int i=0; i<x; i++) {
if(feld[i][j]==alife||feld[i][j]==born) {
glColor3f(0.0f, 0.0f, 1.0f);
} else {
glColor3f(1.0f, 0.0f, 0.0f);
}
glVertex2f(hperp*i+border, hperp*j+border);
glVertex2f(hperp*i+border, hperp*(j+1)-border);
glVertex2f(hperp*(i+1)-border, hperp*(j+1)-border);
glVertex2f(hperp*(i+1)-border, hperp*j+border);
}
}
glEnd();
if(editmode) {
glBegin(GL_QUADS);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex2f(hperp*setpos.x+border, hperp*setpos.y+border);
glVertex2f(hperp*setpos.x+border, hperp*(setpos.y+1)-border);
glVertex2f(hperp*(setpos.x+1)-border, hperp*(setpos.y+1)-border);
glVertex2f(hperp*(setpos.x+1)-border, hperp*setpos.y+border);
glEnd();
}
// glTranslatef(0.0f, 0.0f, 0.1f);
glColor3f(0.0f, 0.0f, 0.0f);
glBegin(GL_LINES);
for(float i=0; i<x; i+=1.0f) {
glVertex2f(i*hperp, 0);
glVertex2f(i*hperp, y*hperp);
}
for(float i=0; i<y; i+=1.0f) {
glVertex2f(0, i*hperp);
glVertex2f(x*hperp, i*hperp); }
glEnd();
glPopMatrix();
}
}
float GameOfLife::getTickSec() {
return secpertick;
}
float GameOfLife::getBuildSec() {
return sectobuild;
}
void GameOfLife::setTickSec(float s) {
secpertick = s;
}
void GameOfLife::setBuildSec(float s) {
sectobuild = s;
}
void GameOfLife::setTorus(bool t) {
torus = t;
}
unsigned long GameOfLife::getGeneration() {
return generation;
}
void GameOfLife::tick() {
if(sectobuild>secpertick)
lockStates();
int near;
int x1, y1;
for(int i=0; i<x; i++) {
for(int j=0; j<y; j++) {
near = 0;
for(int a=i-1; a<=(i+1); a++) {
for(int b=j-1; b<=(j+1); b++) {
if((a==i && b==j) || (!torus && (a<0 || b<0 || a>=x || b>=y)))
continue;
if(torus) {
x1 = (a+x)%x;
y1 = (b+y)%y;
} else {
x1 = a;
y1 = b;
}
if(feld[x1][y1]==alife || (fullcelluse && (feld[x1][y1]==dies)))
near++;
}
}
if(feld[i][j]==alife) {
if(near!=2 && near!=3)
feld[i][j] = dies;
} else if(feld[i][j]==dead) {
if(near==3)
feld[i][j] = born;
}
}
}
if(sectobuild<0.1f)
lockStates();
generation++;
}
void GameOfLife::clear() {
for(int i=0; i<x; i++) {
for(int j=0; j<y; j++) {
feld[i][j] = dead;
}
}
}
GameOfLife::~GameOfLife() {
cleanup();
delete[](sinval);
delete[](cosval);
}