315 lines
6.6 KiB
C
315 lines
6.6 KiB
C
/**
|
|
* Conways Game of life
|
|
* Author: Daniel Otte
|
|
* License: GPLv3
|
|
*
|
|
*
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#ifdef AVR
|
|
#include <util/delay.h>
|
|
#include <avr/sfr_defs.h> /* for debugging */
|
|
#endif
|
|
#include "../config.h"
|
|
#include "../random/prng.h"
|
|
#include "../pixel.h"
|
|
#include "../util.h"
|
|
|
|
/******************************************************************************/
|
|
|
|
#undef DEBUG
|
|
|
|
#define XSIZE NUM_COLS
|
|
#define YSIZE NUM_ROWS
|
|
|
|
/*
|
|
* last line is for debug information
|
|
*/
|
|
#ifdef DEBUG
|
|
#undef YSIZE
|
|
#define YSIZE (NUM_ROWS-1)
|
|
#define DEBUG_ROW (NUM_ROWS-1)
|
|
#define DEBUG_BIT(pos, val) \
|
|
setpixel((pixel){(pos)%XSIZE,DEBUG_ROW+(pos)/XSIZE},(val)?3:0)
|
|
#define DEBUG_BYTE(s,v) \
|
|
DEBUG_BIT((s)*8+0, (v)&(1<<0)); \
|
|
DEBUG_BIT((s)*8+1, (v)&(1<<1)); \
|
|
DEBUG_BIT((s)*8+2, (v)&(1<<2)); \
|
|
DEBUG_BIT((s)*8+3, (v)&(1<<3)); \
|
|
DEBUG_BIT((s)*8+4, (v)&(1<<4)); \
|
|
DEBUG_BIT((s)*8+5, (v)&(1<<5)); \
|
|
DEBUG_BIT((s)*8+6, (v)&(1<<6)); \
|
|
DEBUG_BIT((s)*8+7, (v)&(1<<7))
|
|
#else
|
|
#define DEBUG_BIT(s,v)
|
|
#define DEBUG_BYTE(s,v)
|
|
#endif
|
|
|
|
//#define GLIDER_TEST
|
|
|
|
#define BITSTUFFED
|
|
#define LOOP_DETECT_BUFFER_SIZE 8
|
|
|
|
#ifndef GOL_DELAY
|
|
#define GOL_DELAY 1 /* milliseconds */
|
|
#endif
|
|
|
|
#ifndef GOL_CYCLES
|
|
#define GOL_CYCLES (2*60*3)
|
|
#endif
|
|
|
|
/******************************************************************************/
|
|
/******************************************************************************/
|
|
|
|
typedef enum{dead=0, alive=1} cell_t;
|
|
|
|
#ifndef BITSTUFFED
|
|
|
|
#define FIELD_XSIZE XSIZE
|
|
#define FIELD_YSIZE YSIZE
|
|
|
|
typedef cell_t field_t[FIELD_XSIZE][FIELD_YSIZE];
|
|
|
|
/******************************************************************************/
|
|
|
|
void setcell(field_t pf, int x, int y, cell_t value){
|
|
pf[(x+FIELD_XSIZE)%FIELD_XSIZE][(y+FIELD_YSIZE)%FIELD_YSIZE] = value;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
cell_t getcell(field_t pf, int x, int y){
|
|
return pf[(x+FIELD_XSIZE)%FIELD_XSIZE][(y+FIELD_YSIZE)%FIELD_YSIZE];
|
|
}
|
|
|
|
#else /* BITSTUFFED */
|
|
|
|
#define FIELD_XSIZE ((XSIZE+7)/8)
|
|
#define FIELD_YSIZE YSIZE
|
|
|
|
typedef uint8_t field_t[FIELD_XSIZE][FIELD_YSIZE];
|
|
|
|
/******************************************************************************/
|
|
|
|
void setcell(field_t pf, int x, int y, cell_t value){
|
|
uint8_t t;
|
|
x = (x+XSIZE) % XSIZE;
|
|
y = (y+YSIZE) % YSIZE;
|
|
|
|
t = pf[x/8][y];
|
|
if(value==alive){
|
|
t |= 1<<(x&7);
|
|
} else {
|
|
t &= ~(1<<(x&7));
|
|
}
|
|
pf[x/8][y] = t;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
cell_t getcell(field_t pf, int x, int y){
|
|
x = (x+XSIZE) % XSIZE;
|
|
y = (y+YSIZE) % YSIZE;
|
|
|
|
return ((pf[x/8][y])&(1<<(x&7)))?alive:dead;
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************************/
|
|
|
|
uint8_t countsurroundingalive(field_t pf, int x, int y){
|
|
uint8_t ret=0;
|
|
ret += (getcell(pf, x-1, y-1)==alive)?1:0;
|
|
ret += (getcell(pf, x , y-1)==alive)?1:0;
|
|
ret += (getcell(pf, x+1, y-1)==alive)?1:0;
|
|
|
|
ret += (getcell(pf, x-1, y )==alive)?1:0;
|
|
ret += (getcell(pf, x+1, y )==alive)?1:0;
|
|
|
|
ret += (getcell(pf, x-1, y+1)==alive)?1:0;
|
|
ret += (getcell(pf, x , y+1)==alive)?1:0;
|
|
ret += (getcell(pf, x+1, y+1)==alive)?1:0;
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
void nextiteration(field_t dest, field_t src){
|
|
int x,y;
|
|
uint8_t tc;
|
|
for(y=0; y<YSIZE; ++y){
|
|
for(x=0; x<XSIZE; ++x){
|
|
tc=countsurroundingalive(src,x,y);
|
|
switch(tc){
|
|
// case 0:
|
|
// case 1:
|
|
// /* dead */
|
|
// setcell(dest, x,y, dead);
|
|
case 2:
|
|
/* keep */
|
|
setcell(dest, x,y, getcell(src,x,y));
|
|
break;
|
|
case 3:
|
|
/* alive */
|
|
setcell(dest, x,y, alive);
|
|
break;
|
|
default:
|
|
/* dead */
|
|
setcell(dest, x,y, dead);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
void printpf(field_t pf){
|
|
int x,y;
|
|
for(y=0; y<YSIZE; ++y){
|
|
for(x=0; x<XSIZE; ++x){
|
|
setpixel((pixel){x,y},(getcell(pf,x,y)==alive)?3:0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
void pfcopy(field_t dest, field_t src){
|
|
int x,y;
|
|
for(y=0; y<YSIZE; ++y){
|
|
for(x=0; x<XSIZE; ++x){
|
|
setcell(dest,x,y,getcell(src,x,y));
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
#ifndef BITSTUFFED
|
|
uint8_t pfcmp(field_t dest, field_t src){
|
|
int x,y;
|
|
for(y=0; y<YSIZE; ++y){
|
|
for(x=0; x<XSIZE; ++x){
|
|
if (getcell(src,x,y) != getcell(dest,x,y))
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
uint8 pfempty(field_t src){ int x,y;
|
|
for(y=0; y<YSIZE; ++y){
|
|
for(x=0; x<XSIZE; ++x){
|
|
if (getcell(src,x,y)==alive)
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#else
|
|
|
|
uint8_t pfcmp(field_t dest, field_t src){
|
|
int x,y;
|
|
for(y=0; y<FIELD_YSIZE; ++y){
|
|
for(x=0; x<FIELD_XSIZE; ++x){
|
|
if (src[x][y] != dest[x][y])
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
uint8_t pfempty(field_t src){
|
|
int x,y;
|
|
for(y=0; y<FIELD_YSIZE; ++y){
|
|
for(x=0; x<FIELD_XSIZE; ++x){
|
|
if (src[x][y]!=0)
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
/******************************************************************************/
|
|
|
|
void insertglider(field_t pf){
|
|
/*
|
|
* #
|
|
* #
|
|
* ###
|
|
*/
|
|
setcell(pf, 1, 0, alive);
|
|
setcell(pf, 2, 1, alive);
|
|
setcell(pf, 0, 2, alive); setcell(pf, 1, 2, alive); setcell(pf, 2, 2, alive);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int gameoflife(){
|
|
DEBUG_BYTE(0,0); // set debug bytes to zero
|
|
DEBUG_BYTE(1,0);
|
|
field_t pf1,pf2;
|
|
field_t ldbuf[LOOP_DETECT_BUFFER_SIZE]={{{0}}}; // loop detect buffer
|
|
uint8_t ldbuf_idx=0;
|
|
int x,y;
|
|
uint16_t cycle;
|
|
|
|
//start:
|
|
/* initalise the field with random */
|
|
for(y=0;y<YSIZE;++y){
|
|
for(x=0;x<XSIZE; ++x){
|
|
setcell(pf1,x,y,(random8()&1)?alive:dead);
|
|
}
|
|
}
|
|
#ifdef GLIDER_TEST
|
|
/* initialise with glider */
|
|
for(y=0;y<YSIZE;++y){
|
|
for(x=0;x<XSIZE; ++x){
|
|
setcell(pf1,x,y,dead);
|
|
}
|
|
}
|
|
insertglider(pf1);
|
|
#endif
|
|
|
|
/* the main part */
|
|
printpf(pf1);
|
|
for(cycle=1; cycle<GOL_CYCLES; ++cycle){
|
|
DEBUG_BYTE(0,(uint8_t)(GOL_CYCLES-cycle)&0xff);
|
|
DEBUG_BYTE(1, SREG);
|
|
wait(GOL_DELAY);
|
|
pfcopy(pf2,pf1);
|
|
nextiteration(pf1,pf2);
|
|
printpf(pf1);
|
|
/* loop detection */
|
|
if(!pfcmp(pf1, pf2)){
|
|
insertglider(pf1);
|
|
cycle=1;
|
|
}
|
|
if(pfempty(pf1)){
|
|
/* kill game */
|
|
return 0;
|
|
}
|
|
/* */
|
|
uint8_t i;
|
|
for(i=0; i<LOOP_DETECT_BUFFER_SIZE; ++i){
|
|
if(!pfcmp(pf1, ldbuf[i])){
|
|
insertglider(pf1);
|
|
cycle=1;
|
|
}
|
|
}
|
|
pfcopy(ldbuf[ldbuf_idx], pf1);
|
|
ldbuf_idx = (ldbuf_idx+1)%LOOP_DETECT_BUFFER_SIZE;
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|