Wireworld/C
Evolution is key-driven instead of being time-driven: any key (except 'q' which terminates the program) evolves the system.
<lang c>#include <stdio.h>
- include <stdlib.h>
- include <GL/glut.h>
- include <assert.h>
- define WIDTH 20
- define HEIGHT 20
- define MAX_SIZE 256
// Pixels Per Point (every "point" is an "element" of the wireworld) int PPP = 10; int width = WIDTH, height = HEIGHT;
int win;
int fc = 0; char *field[2];
// free mem hook
void freemem(void)
{
free(field[0]); free(field[1]);
}
// BLACK
- define EMPTY 0.0, 0.0, 0.0
// head WHITE
- define E_HEAD 1.0, 1.0, 1.0
// tail GRAY
- define E_TAIL 0.7, 0.7, 0.7
// conductor RED
- define CONDUCTOR 1.0, 0.0, 0.0
- define cEMPTY ' '
- define cHEAD 'H'
- define cTAIL 't'
- define cCONDUCTOR '.'
// GL coords are from -1 to 1
- define GLCOORD_X(a) ( (double)(2.0*(a))/(double)(width*PPP) - 1.0 )
- define GLCOORD_Y(a) ( (double)(2.0*(a))/(double)(height*PPP) - 1.0 )
// show wireworld void wireworld_show(void) {
int j, i;
glClearColor(EMPTY, 1.0); glClear(GL_COLOR_BUFFER_BIT);
for(j=0; j < height; j++) { for(i=0; i < width; i++) { switch( *(field[fc%2] + j*width + i) ) { case cTAIL: glColor3d(E_TAIL); break; case cHEAD: glColor3d(E_HEAD); break; case cCONDUCTOR: glColor3d(CONDUCTOR); break; default: glColor3d(EMPTY); break; } glRectd(GLCOORD_X(i*PPP), GLCOORD_Y(j*PPP), GLCOORD_X((i+1)*PPP-1), GLCOORD_Y((j+1)*PPP-1)); } } glFlush();
}
- define THIS(I,J) *(field[fc%2] + (I) + (J)*width)
- define NEXT(I,J) *(field[(fc+1)%2] + (I) + (J)*width)
int count_heads(int i, int j) {
int e = 0; int ax, ay; for(ax=-1; ax <= 1; ax++) { for(ay=-1; ay <= 1; ay++) { if ( ((i+ax) < 0) || ( (j+ay) < 0) || ( (i+ax) >= width ) || ( (j+ay) >= height ) ) continue; if ( (ax==0) && (ay==0) ) continue; if ( THIS(i+ax, j+ay) == cHEAD ) e++; } } return e;
}
void evolve_wireworld(void) {
int j, i; int ehn; for(i=0; i < width; i++) { for(j=0; j < height; j++) { switch( THIS(i, j) ) { case cHEAD: NEXT(i, j) = cTAIL; break; case cTAIL: NEXT(i, j) = cCONDUCTOR; break; case cCONDUCTOR: ehn = count_heads(i, j); if ( (ehn == 1) || (ehn == 2) ) NEXT(i, j) = cHEAD; else NEXT(i, j) = cCONDUCTOR; break; default: NEXT(i, j) = THIS(i, j); } } } fc++;
}
// key hit
void key_hit(unsigned char k, int x, int y)
{
if ( k == 'q' ) { glFinish(); glutDestroyWindow(win); exit(EXIT_SUCCESS); } else { evolve_wireworld(); wireworld_show(); }
}
int main(int argc, char *argv[])
{
FILE *p; int i, j;
if ( argc < 2 ) { fprintf(stderr, "specify a wireworld initial state\n"); exit(EXIT_FAILURE); }
if ( (p = fopen(argv[1], "r")) != NULL ) {
field[0] = malloc(MAX_SIZE*MAX_SIZE*sizeof(char)); assert(field[0] != NULL); //lazy a-check field[1] = malloc(MAX_SIZE*MAX_SIZE*sizeof(char)); assert(field[1] != NULL); atexit(freemem); fc = 0;
char buf[MAX_SIZE]; j = 0; while( !feof(p) && fgets(buf, MAX_SIZE, p) && ( j < MAX_SIZE )) { for(i=0; (buf[i] != 0) && (buf[i] != 10) && (i < MAX_SIZE); i++ ) { *(field[fc%2] + j*width + i) = buf[i]; } for( ; i < MAX_SIZE; i++) *(field[fc%2] + j*width + i) = 0; j++; } fclose(p);
// no more error check :) glutInit(&argc, argv);
win = glutCreateWindow("Wireworld"); glutInitWindowPosition(0,0); glutInitWindowSize(width*PPP, height*PPP);
glutDisplayFunc(wireworld_show); glutKeyboardFunc(key_hit);
// (-1,-1) from lower leftmost to upper leftmost corner float matrix[16] = { 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; glLoadMatrixf(matrix);
glutMainLoop(); } else { fprintf(stderr, "cannot open %s\n", argv[1]); exit(EXIT_FAILURE); }
exit(EXIT_SUCCESS);
}</lang>