3D Game of Life implementation in c++
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gameoflife.cpp 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. #include "gameoflife.h"
  2. GameOfLife::GameOfLife(int _x, int _y) {
  3. init();
  4. x = _x;
  5. y = _y;
  6. allocate();
  7. }
  8. GameOfLife::GameOfLife(std::string file) {
  9. init();
  10. load(file);
  11. }
  12. GameOfLife::GameOfLife(int _x, int _y, int r_life, int r_dead) {
  13. init();
  14. fillRandom(_x, _y, r_life, r_dead);
  15. }
  16. void GameOfLife::init() {
  17. view3d = true;
  18. feld = 0;
  19. x = y = 0;
  20. generation = 0;
  21. thickness = 0.5f;
  22. cellwidth = 1.0f;
  23. torus = true;
  24. fullcelluse = true;
  25. radius = 0.3f;
  26. height = 1.0f;
  27. parts = 12;
  28. sectobuild = 0.91f;
  29. b_secdone = 0.0f;
  30. secpertick = 1.0f;
  31. t_secdone = 0.0f;
  32. editmode = false;
  33. // Calc cylinder sinvals
  34. sinval = new float[parts];
  35. cosval = new float[parts];
  36. for(int i=0; i<parts; i++) {
  37. sinval[i] = radius * sin(segl::deg2rad(360.0f/parts*i));
  38. cosval[i] = radius * cos(segl::deg2rad(360.0f/parts*i));
  39. }
  40. quad = gluNewQuadric();
  41. }
  42. void GameOfLife::allocate() {
  43. feld = new State*[x];
  44. for(int i=0; i<x; i++) {
  45. feld[i] = new State[y];
  46. // clean it
  47. for(int j=0; j<y; j++)
  48. feld[i][j] = dead;
  49. }
  50. }
  51. void GameOfLife::cleanup() {
  52. if(feld) {
  53. for(int i=0; i<x; i++) {
  54. delete[](feld[i]);
  55. }
  56. delete(feld);
  57. feld = 0;
  58. }
  59. }
  60. void GameOfLife::renderBrett() {
  61. float x1 = cellwidth*x / 2.0f;
  62. float y1 = thickness / 2.0f;
  63. float z1 = cellwidth*y / 2.0f;
  64. // Brettquader
  65. GLfloat mat_diffuse[] = { 1.0f, 0.0f, 0.0f, 1.0f };
  66. glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
  67. glColor3f(1.0f, 0.0f, 0.0f);
  68. glBegin(GL_QUADS);
  69. // Platte
  70. glNormal3f(0.0f, 1.0f, 0.0f);
  71. glVertex3f(-x1, y1, -z1);
  72. glVertex3f( x1, y1, -z1);
  73. glVertex3f( x1, y1, z1);
  74. glVertex3f(-x1, y1, z1);
  75. // Boden
  76. glNormal3f(0.0f,-1.0f, 0.0f);
  77. glVertex3f(-x1, -y1, -z1);
  78. glVertex3f( x1, -y1, -z1);
  79. glVertex3f( x1, -y1, z1);
  80. glVertex3f(-x1, -y1, z1);
  81. // Seiten
  82. glNormal3f(0.0f, 0.0f, 1.0f);
  83. glVertex3f(-x1, -y1, z1);
  84. glVertex3f( x1, -y1, z1);
  85. glVertex3f( x1, y1, z1);
  86. glVertex3f(-x1, y1, z1);
  87. glNormal3f(0.0f, 0.0f,-1.0f);
  88. glVertex3f(-x1, -y1,-z1);
  89. glVertex3f( x1, -y1,-z1);
  90. glVertex3f( x1, y1,-z1);
  91. glVertex3f(-x1, y1,-z1);
  92. glNormal3f(-1.0f, 0.0f, 0.0f);
  93. glVertex3f(-x1, -y1, z1);
  94. glVertex3f(-x1, y1, z1);
  95. glVertex3f(-x1, y1,-z1);
  96. glVertex3f(-x1, -y1,-z1);
  97. glNormal3f(1.0f, 0.0f, 0.0f);
  98. glVertex3f( x1, -y1, z1);
  99. glVertex3f( x1, y1, z1);
  100. glVertex3f( x1, y1,-z1);
  101. glVertex3f( x1, -y1,-z1);
  102. glEnd();
  103. //Gitter
  104. glPushMatrix();
  105. mat_diffuse[0] = 0.0f;
  106. mat_diffuse[1] = 1.0f;
  107. mat_diffuse[2] = 0.0f;
  108. glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
  109. glColor3f(0.0f, 1.0f, 0.0f);
  110. for(int a=0; a<1; a++) {
  111. glTranslatef(0.0f, 0.01f, 0.0f);
  112. glBegin(GL_LINES);
  113. glNormal3f(0.0f, 1.0f, 0.0f);
  114. for(float i=-x1; i<=x1; i+=cellwidth) {
  115. glVertex3f(i, y1, -z1);
  116. glVertex3f(i, y1, z1);
  117. }
  118. for(float i=-z1; i<=z1; i+=cellwidth) {
  119. glVertex3f(-x1, y1, i);
  120. glVertex3f( x1, y1, i);
  121. }
  122. glEnd();
  123. }
  124. glPopMatrix();
  125. }
  126. void GameOfLife::renderStein(State s) {
  127. float sin1, sin2 = sinval[0], cos1, cos2 = cosval[0];
  128. float height2 = 0.0f;
  129. switch(s) {
  130. case alife:
  131. height2 = height;
  132. break;
  133. case born:
  134. height2 = height * (b_secdone/sectobuild);
  135. break;
  136. case dies:
  137. height2 = height * ((sectobuild-b_secdone)/sectobuild);
  138. break;
  139. case dead:
  140. // unlikely to happen...
  141. height2 = 0.0f;
  142. break;
  143. }
  144. if(height2<0.001f)
  145. return;
  146. glColor3f(0.2f, 0.2, 0.9f);
  147. // for(float i=0, step=360.0f/parts; i<360.0f; i+= step) {
  148. for(int i=0; i<parts; i++) {
  149. sin1 = sin2;
  150. cos1 = cos2;
  151. sin2 = sinval[(i+1)%parts];
  152. cos2 = cosval[(i+1)%parts];
  153. // sin2 = radius * sin(deg2rad(i+step));
  154. // cos2 = radius * cos(deg2rad(i+step));
  155. GLfloat mat_diffuse[] = { 0.2f, 0.2, 0.9f, 1.0f };
  156. glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
  157. /*
  158. glPushMatrix();
  159. glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
  160. gluCylinder(quad, radius, radius, height2, 12, 1);
  161. glTranslatef(0.0f, 0.0f, height2);
  162. gluDisk(quad, 0.0f, radius, 12, 1);
  163. glPopMatrix();
  164. */
  165. glBegin(GL_QUADS);
  166. glNormal3f(0.0f, 0.0f,-1.0f);
  167. glVertex3f(-radius, 0.0f, -radius);
  168. glVertex3f(-radius, height2,-radius);
  169. glVertex3f( radius, height2,-radius);
  170. glVertex3f( radius, 0.0f, -radius);
  171. glNormal3f(0.0f, 0.0f, 1.0f);
  172. glVertex3f( radius, 0.0f, radius);
  173. glVertex3f( radius, height2, radius);
  174. glVertex3f(-radius, height2, radius);
  175. glVertex3f(-radius, 0.0f, radius);
  176. glNormal3f(-1.0f, 0.0f, 0.0f);
  177. glVertex3f(-radius, 0.0f, -radius);
  178. glVertex3f(-radius, 0.0f, radius);
  179. glVertex3f(-radius, height2, radius);
  180. glVertex3f(-radius, height2,-radius);
  181. glNormal3f( 1.0f, 0.0f, 0.0f);
  182. glVertex3f( radius, 0.0f, radius);
  183. glVertex3f( radius, 0.0f, -radius);
  184. glVertex3f( radius, height2,-radius);
  185. glVertex3f( radius, height2, radius);
  186. glNormal3f(0.0f, 1.0f, 0.0f);
  187. glVertex3f(-radius, height2, radius);
  188. glVertex3f( radius, height2, radius);
  189. glVertex3f( radius, height2,-radius);
  190. glVertex3f(-radius, height2,-radius);
  191. glEnd();
  192. /*
  193. glBegin(GL_TRIANGLES);
  194. // Boden Unten
  195. glNormal3f(0.0f,-1.0f, 0.0f);
  196. glVertex3f(0.0f, 0.0f, 0.0f);
  197. glVertex3f(sin1, 0.0f, cos1);
  198. glVertex3f(sin2, 0.0f, cos2);
  199. // Boden Oben
  200. glNormal3f(0.0f, 1.0f, 0.0f);
  201. glVertex3f(0.0f, height2, 0.0f);
  202. glVertex3f(sin1, height2, cos1);
  203. glVertex3f(sin2, height2, cos2);
  204. //Seite
  205. glNormal3f((sin1+sin2)/2.0f, 0.0f, (cos1+cos2)/2.0f);
  206. glVertex3f(sin1, 0.0f, cos1);
  207. glVertex3f(sin2, 0.0f, cos2);
  208. glVertex3f(sin2, height2, cos2);
  209. glNormal3f((sin1+sin2)/2.0f, 0.0f, (cos1+cos2)/2.0f);
  210. glVertex3f(sin2, height2, cos2);
  211. glVertex3f(sin1, height2, cos1);
  212. glVertex3f(sin1, 0.0f, cos1);
  213. glEnd();
  214. */
  215. }
  216. }
  217. void GameOfLife::translateTo(int _x, int _y) {
  218. glTranslatef(-cellwidth/2.0f*x+cellwidth*_x+cellwidth/2.0f, thickness/2.0f, -cellwidth/2.0f*y+cellwidth*_y+cellwidth/2.0f);
  219. }
  220. void GameOfLife::lockStates() {
  221. for(int i=0; i<x; i++) {
  222. for(int j=0; j<y; j++) {
  223. if(feld[i][j]==dies)
  224. feld[i][j] = dead;
  225. else if(feld[i][j]==born)
  226. feld[i][j] = alife;
  227. }
  228. }
  229. }
  230. void GameOfLife::set3D(bool on) {
  231. view3d = on;
  232. }
  233. bool GameOfLife::load(std::string file) {
  234. std::ifstream data(file.c_str());
  235. if(!data)
  236. return false;
  237. std::string tmp;
  238. data >> x;
  239. data >> y;
  240. data >> torus;
  241. if(x<=0 || y <=0)
  242. return false;
  243. cleanup();
  244. allocate();
  245. int j=0;
  246. getline(data, tmp); // Remove \n
  247. while(getline(data, tmp) && j<y) {
  248. for(int i=0; i<x && i<(int)tmp.length(); i++) {
  249. feld[i][j] = (tmp[i]=='1') ? born : dead;
  250. }
  251. j++;
  252. }
  253. return true;
  254. }
  255. bool GameOfLife::save(std::string file) {
  256. std::ofstream data(file.c_str());
  257. if(!data)
  258. return false;
  259. data << x << " " << y << " " << (torus?1:0) << std::endl;
  260. for(int j=0; j<y; j++) {
  261. for(int i=0; i<x; i++) {
  262. data << ((feld[i][j]==alife||feld[i][j]==born) ? '1' : '0');
  263. }
  264. data << std::endl;
  265. }
  266. return true;
  267. }
  268. void GameOfLife::fillRandom(int _x, int _y, int r_life, int r_dead) {
  269. cleanup();
  270. x = _x;
  271. y = _y;
  272. allocate();
  273. srand(time(0));
  274. if(r_life+r_dead==0) {
  275. r_life = rand()%100 + 1;
  276. r_dead = rand()%100 + 1;
  277. }
  278. float ratio = r_life/(float)(r_life+r_dead);
  279. for(int i=0; i<x; i++) {
  280. for(int j=0; j<y; j++) {
  281. if((rand()%100)/100.0f < ratio) {
  282. feld[i][j] = born;
  283. } else {
  284. // should be set to dead, but to be sure...
  285. feld[i][j] = dead;
  286. }
  287. }
  288. }
  289. }
  290. // void GameOfLife::toggle(int _x, int _y, State stat) {
  291. // if(feld && _x>=0 && _x<x && _y>=0 && _y<y) {
  292. // feld[_x][_y] = stat;
  293. // }
  294. // }
  295. // const bool** GameOfLife::getFeld() {
  296. // return (const bool**)feld;
  297. // }
  298. void GameOfLife::setEditMode(bool e) {
  299. editmode = e;
  300. if(e) {
  301. lockStates();
  302. } else {
  303. }
  304. }
  305. void GameOfLife::move(int up, int right) {
  306. if(up>0)
  307. setpos.y = ((int)setpos.y+y+1)%y;
  308. else if(up<0)
  309. setpos.y = ((int)setpos.y+y-1)%y;
  310. if(right>0)
  311. setpos.x = ((int)setpos.x+x+1)%x;
  312. else if(right<0)
  313. setpos.x = ((int)setpos.x+x-1)%x;
  314. }
  315. void GameOfLife::toggle() {
  316. if(feld[(int)setpos.x][(int)setpos.y]==born||feld[(int)setpos.x][(int)setpos.y]==alife) {
  317. feld[(int)setpos.x][(int)setpos.y] = dead;
  318. } else {
  319. feld[(int)setpos.x][(int)setpos.y] = alife;
  320. }
  321. }
  322. void GameOfLife::render(float sec) {
  323. if(!feld)
  324. return;
  325. // Cylinder-building-timer
  326. if(b_secdone<=sectobuild) {
  327. b_secdone += sec;
  328. if(b_secdone>=sectobuild) {
  329. lockStates();
  330. }
  331. }
  332. // Tick-timer
  333. t_secdone += sec;
  334. if(t_secdone>=secpertick) {
  335. tick();
  336. t_secdone = 0.0f;
  337. b_secdone = 0.0f;
  338. }
  339. if(view3d) {
  340. renderBrett();
  341. for(int j=0; j<y; j++) {
  342. for(int i=0; i<x; i++) {
  343. if(feld[i][j]!=dead) {
  344. glPushMatrix();
  345. translateTo(i, j);
  346. renderStein(feld[i][j]);
  347. glPopMatrix();
  348. }
  349. if(editmode) {
  350. if(i==setpos.x && j==setpos.y) {
  351. glPushMatrix();
  352. float mat_diffuse[3] = { 0.0f, 1.0f, 0.0f };
  353. glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
  354. translateTo(i, j);
  355. glTranslatef(0.0f, 0.01f, 0.0f);
  356. glBegin(GL_QUADS);
  357. glColor3f(0.0f, 1.0f, 0.0f);
  358. glNormal3f(0.0f, 1.0f, 0.0f);
  359. glVertex3f(-cellwidth/2.0f, 0.0f, cellwidth/2.0f);
  360. glVertex3f( cellwidth/2.0f, 0.0f, cellwidth/2.0f);
  361. glVertex3f( cellwidth/2.0f, 0.0f,-cellwidth/2.0f);
  362. glVertex3f(-cellwidth/2.0f, 0.0f,-cellwidth/2.0f);
  363. glEnd();
  364. glPopMatrix();
  365. }
  366. }
  367. }
  368. }
  369. } else {
  370. SDL_Surface *screen = SDL_GetVideoSurface();
  371. float wperp = screen->w /(float)x;
  372. float hperp = screen->h /(float)y;
  373. float border;
  374. glPushMatrix();
  375. if(wperp<hperp) {
  376. glTranslatef(0.0f, (hperp-wperp)*y/2.0f, 0.0f);
  377. hperp = wperp;
  378. } else {
  379. glTranslatef((wperp-hperp)*x/2.0f, 0.0f, 0.0f);
  380. }
  381. border = 0.0f;
  382. glBegin(GL_QUADS);
  383. for(int j=0; j<y; j++) {
  384. for(int i=0; i<x; i++) {
  385. if(feld[i][j]==alife||feld[i][j]==born) {
  386. glColor3f(0.0f, 0.0f, 1.0f);
  387. } else {
  388. glColor3f(1.0f, 0.0f, 0.0f);
  389. }
  390. glVertex2f(hperp*i+border, hperp*j+border);
  391. glVertex2f(hperp*i+border, hperp*(j+1)-border);
  392. glVertex2f(hperp*(i+1)-border, hperp*(j+1)-border);
  393. glVertex2f(hperp*(i+1)-border, hperp*j+border);
  394. }
  395. }
  396. glEnd();
  397. if(editmode) {
  398. glBegin(GL_QUADS);
  399. glColor3f(0.0f, 1.0f, 0.0f);
  400. glVertex2f(hperp*setpos.x+border, hperp*setpos.y+border);
  401. glVertex2f(hperp*setpos.x+border, hperp*(setpos.y+1)-border);
  402. glVertex2f(hperp*(setpos.x+1)-border, hperp*(setpos.y+1)-border);
  403. glVertex2f(hperp*(setpos.x+1)-border, hperp*setpos.y+border);
  404. glEnd();
  405. }
  406. // glTranslatef(0.0f, 0.0f, 0.1f);
  407. glColor3f(0.0f, 0.0f, 0.0f);
  408. glBegin(GL_LINES);
  409. for(float i=0; i<x; i+=1.0f) {
  410. glVertex2f(i*hperp, 0);
  411. glVertex2f(i*hperp, y*hperp);
  412. }
  413. for(float i=0; i<y; i+=1.0f) {
  414. glVertex2f(0, i*hperp);
  415. glVertex2f(x*hperp, i*hperp); }
  416. glEnd();
  417. glPopMatrix();
  418. }
  419. }
  420. float GameOfLife::getTickSec() {
  421. return secpertick;
  422. }
  423. float GameOfLife::getBuildSec() {
  424. return sectobuild;
  425. }
  426. void GameOfLife::setTickSec(float s) {
  427. std::cout << "Set to " << s << std::endl;
  428. secpertick = s;
  429. }
  430. void GameOfLife::setBuildSec(float s) {
  431. sectobuild = s;
  432. }
  433. void GameOfLife::setTorus(bool t) {
  434. torus = t;
  435. }
  436. unsigned long GameOfLife::getGeneration() {
  437. return generation;
  438. }
  439. void GameOfLife::tick() {
  440. if(sectobuild>secpertick)
  441. lockStates();
  442. int near;
  443. int x1, y1;
  444. for(int i=0; i<x; i++) {
  445. for(int j=0; j<y; j++) {
  446. near = 0;
  447. for(int a=i-1; a<=(i+1); a++) {
  448. for(int b=j-1; b<=(j+1); b++) {
  449. if((a==i && b==j) || (!torus && (a<0 || b<0 || a>=x || b>=y)))
  450. continue;
  451. if(torus) {
  452. x1 = (a+x)%x;
  453. y1 = (b+y)%y;
  454. } else {
  455. x1 = a;
  456. y1 = b;
  457. }
  458. if(feld[x1][y1]==alife || (fullcelluse && (feld[x1][y1]==dies)))
  459. near++;
  460. }
  461. }
  462. if(feld[i][j]==alife) {
  463. if(near!=2 && near!=3)
  464. feld[i][j] = dies;
  465. } else if(feld[i][j]==dead) {
  466. if(near==3)
  467. feld[i][j] = born;
  468. }
  469. }
  470. }
  471. if(sectobuild<0.1f)
  472. lockStates();
  473. generation++;
  474. }
  475. void GameOfLife::clear() {
  476. for(int i=0; i<x; i++) {
  477. for(int j=0; j<y; j++) {
  478. feld[i][j] = dead;
  479. }
  480. }
  481. }
  482. GameOfLife::~GameOfLife() {
  483. cleanup();
  484. delete[](sinval);
  485. delete[](cosval);
  486. }