Wireworld/C: Difference between revisions
(Moving from main task page to shorten it) |
(replaced with much more usable code.) |
||
Line 1: | Line 1: | ||
Run the program to see a short help message about key bindings. |
|||
Evolution is key-driven instead of being time-driven: any key (except 'q' which terminates the program) evolves the system. |
|||
{{libheader|OpenGL}} |
{{libheader|OpenGL}} |
||
{{libheader|GLUT}} |
{{libheader|GLUT}} |
||
Compile with <code>gcc -lpthread -lglut -lGL -lGLU</code>. Run with <code>a.out file_name</code>. There are a couple of larger test files on the talk page. By default the program is compiled to use grayscale; compile time macro is available to switch to RGB. |
|||
<lang c>#include <stdio.h> |
<lang c>#include <stdio.h> |
||
#include <stdlib.h> |
#include <stdlib.h> |
||
#include <unistd.h> |
|||
#include <fcntl.h> |
|||
#include <sys/stat.h> |
|||
#include <sys/types.h> |
|||
#include <sys/mman.h> |
|||
#include <err.h> |
|||
#include <pthread.h> |
|||
#include <GL/glut.h> |
#include <GL/glut.h> |
||
#include < |
#include <GL/gl.h> |
||
#include <GL/glu.h> |
|||
enum { s_blank = 0, s_condu, s_etail, s_ehead }; |
|||
#define WIDTH 20 |
|||
typedef struct { unsigned char n, mark, t;} cell_t; |
|||
#define HEIGHT 20 |
|||
typedef struct { int x, y; } pos_t; |
|||
#define MAX_SIZE 256 |
|||
#define USE_RGB 0 /* enable this for hideous colors */ |
|||
// Pixels Per Point (every "point" is an "element" of the wireworld) |
|||
#if USE_RGB |
|||
int PPP = 10; |
|||
int width = WIDTH, height = HEIGHT; |
|||
typedef struct { unsigned char r, g, b; } rgb_t; |
|||
int win; |
|||
rgb_t colors[] = { |
|||
{0, 0, 0}, |
|||
{33, 45, 10}, |
|||
{30, 100, 80}, |
|||
{255, 0, 0}, |
|||
}; |
|||
#define TEX_COMP 3 |
|||
int fc = 0; |
|||
#define TEX_MODE GL_RGB |
|||
char *field[2]; |
|||
#else /* grayscale, less texture transfer */ |
|||
typedef unsigned char rgb_t; |
|||
// free mem hook |
|||
rgb_t colors[] = { 0, 64, 144, 255 }; |
|||
void freemem(void) |
|||
#define TEX_COMP 1 |
|||
#define TEX_MODE GL_LUMINANCE |
|||
#endif |
|||
rgb_t **tex; |
|||
int tex_w = 1; |
|||
cell_t **cells; |
|||
pos_t *heads, *bakup; |
|||
int n_heads, n_bakup; |
|||
int rows = 0, cols = 0, n_cond = 0; |
|||
int evolve_delay = 128, show_delay = 16; |
|||
int paused = 0, single = 0; |
|||
int min_y, max_y; |
|||
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; |
|||
int gwin; |
|||
GLuint texture; |
|||
void mark_neighbors(pos_t *p) |
|||
{ |
{ |
||
int x, y; |
|||
free(field[0]); |
|||
for (y = p->y - 1; y <= p->y + 1 && y < rows; y++) { |
|||
free(field[1]); |
|||
if (y < 0) continue; |
|||
for (x = p->x - 1; x <= p->x + 1 && x < cols; x++) { |
|||
if ( x < 0 |
|||
|| (x == p->x && y == p->y) |
|||
|| cells[y][x].t != s_condu ) continue; |
|||
cells[y][x].n++; |
|||
if (!cells[y][x].mark) { |
|||
cells[y][x].mark = 1; |
|||
bakup[n_bakup].x = x; |
|||
bakup[n_bakup].y = y; |
|||
n_bakup ++; |
|||
} |
|||
} |
|||
} |
|||
} |
} |
||
void evolve() |
|||
// BLACK |
|||
{ |
|||
#define EMPTY 0.0, 0.0, 0.0 |
|||
int i; |
|||
// head WHITE |
|||
pos_t *p; |
|||
#define E_HEAD 1.0, 1.0, 1.0 |
|||
cell_t *c; |
|||
// tail GRAY |
|||
n_bakup = 0; |
|||
#define E_TAIL 0.7, 0.7, 0.7 |
|||
for (i = 0, p = heads; i < n_heads; i++, p++) { |
|||
// conductor RED |
|||
c = &cells[p->y][p->x]; |
|||
#define CONDUCTOR 1.0, 0.0, 0.0 |
|||
switch(c->t) { |
|||
case s_ehead: mark_neighbors(p); |
|||
case s_etail: bakup[n_bakup++] = *p; |
|||
} |
|||
} |
|||
for ( i = 0; i < n_bakup; c->n = 0) { |
|||
#define cEMPTY ' ' |
|||
p = bakup + i; |
|||
#define cHEAD 'H' |
|||
c = &cells[p->y][p->x]; |
|||
#define cTAIL 't' |
|||
c->mark = 0; |
|||
#define cCONDUCTOR '.' |
|||
if (p->y < min_y) min_y = p->y; |
|||
if (p->y > max_y) max_y = p->y; |
|||
switch(c->t) { |
|||
// GL coords are from -1 to 1 |
|||
case s_ehead: c->t = s_etail; i++; |
|||
#define GLCOORD_X(a) ( (double)(2.0*(a))/(double)(width*PPP) - 1.0 ) |
|||
tex[p->y][p->x] = colors[s_etail]; |
|||
#define GLCOORD_Y(a) ( (double)(2.0*(a))/(double)(height*PPP) - 1.0 ) |
|||
continue; |
|||
case s_etail: c->t = s_condu; |
|||
tex[p->y][p->x] = colors[s_condu]; |
|||
break; |
|||
case s_condu: if (c->n > 2) break; |
|||
c->t = s_ehead; |
|||
tex[p->y][p->x] = colors[s_ehead]; |
|||
i++; |
|||
continue; |
|||
} |
|||
*p = bakup[--n_bakup]; |
|||
} |
|||
n_heads = n_bakup; |
|||
// show wireworld |
|||
p = heads; heads = bakup; bakup = p; |
|||
void wireworld_show(void) |
|||
if (single) |
|||
paused = 1; |
|||
} |
|||
#define die(act) err(1, "Can't %s %s", act, fn); |
|||
int read_file(char *fn) |
|||
{ |
{ |
||
struct stat st; |
|||
int j, i; |
|||
size_t ofs; |
|||
int fd = open(fn, O_RDONLY); |
|||
if (fd == -1) die("open"); |
|||
if (fstat(fd, &st)) die("stat"); |
|||
char *map = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); |
|||
if (map == (void*)-1) die("mmap"); |
|||
int i = 0; |
|||
glClearColor(EMPTY, 1.0); |
|||
/* pass 1, get various sizes */ |
|||
glClear(GL_COLOR_BUFFER_BIT); |
|||
for (ofs = 0; ofs < st.st_size; ofs++) { |
|||
switch(map[ofs]) { |
|||
case '\n': break; |
|||
case 't': |
|||
case 'H': |
|||
case '.': n_cond++; |
|||
case ' ': i++; |
|||
case '\r': continue; |
|||
default: printf("Bad char %c at row %d, col %d\n", |
|||
map[ofs], rows, i); |
|||
goto bail; |
|||
} |
|||
if (i > cols) cols = i; |
|||
for(j=0; j < height; j++) { |
|||
rows ++; |
|||
for(i=0; i < width; i++) { |
|||
i = 0; |
|||
switch( *(field[fc%2] + j*width + i) ) { |
|||
} |
|||
case cTAIL: |
|||
glColor3d(E_TAIL); |
|||
heads = malloc(sizeof(pos_t) * n_cond); |
|||
break; |
|||
bakup = malloc(sizeof(pos_t) * n_cond); |
|||
case cHEAD: |
|||
glColor3d(E_HEAD); |
|||
cells = malloc(sizeof(cell_t*) * rows); |
|||
break; |
|||
cells[0] = calloc(cols * rows, sizeof(cell_t)); |
|||
case cCONDUCTOR: |
|||
glColor3d(CONDUCTOR); |
|||
for (i = 1; i < rows; i++) |
|||
cells[i] = cells[i - 1] + cols; |
|||
default: |
|||
glColor3d(EMPTY); |
|||
while (tex_w < cols) tex_w <<= 1; |
|||
break; |
|||
while (tex_w < rows) tex_w <<= 1; |
|||
} |
|||
glRectd(GLCOORD_X(i*PPP), GLCOORD_Y(j*PPP), |
|||
tex = malloc(sizeof(rgb_t *) * tex_w); |
|||
GLCOORD_X((i+1)*PPP-1), GLCOORD_Y((j+1)*PPP-1)); |
|||
tex[0] = calloc(tex_w * tex_w, sizeof(rgb_t)); |
|||
} |
|||
} |
|||
for (i = 1; i < tex_w; i++) |
|||
glFlush(); |
|||
tex[i] = tex[i - 1] + tex_w; |
|||
/* pass 2, convert char file to data */ |
|||
int j = i = n_heads = 0; |
|||
for (ofs = 0; ofs < st.st_size; ofs++) { |
|||
switch(map[ofs]) { |
|||
default: continue; |
|||
case '\n': i++; j = 0; continue; |
|||
case '.': cells[i][j++].t = s_condu; continue; |
|||
case ' ': cells[i][j++].t = s_blank; continue; |
|||
case 't': cells[i][j].t = s_etail; goto add_cell; |
|||
case 'H': cells[i][j].t = s_ehead; goto add_cell; |
|||
} |
|||
add_cell: heads[n_heads].x = j++; |
|||
heads[n_heads].y = i; |
|||
n_heads++; |
|||
} |
|||
bail: munmap(map, st.st_size); |
|||
close(fd); |
|||
return 1; |
|||
} |
} |
||
void * updater(void * _) |
|||
{ |
|||
while (1) { |
|||
if (evolve_delay) |
|||
usleep(evolve_delay * 1000); |
|||
if (paused) continue; |
|||
pthread_mutex_lock(&lock); |
|||
#define THIS(I,J) *(field[fc%2] + (I) + (J)*width) |
|||
evolve(); |
|||
#define NEXT(I,J) *(field[(fc+1)%2] + (I) + (J)*width) |
|||
pthread_mutex_unlock(&lock); |
|||
} |
|||
return 0; |
|||
int count_heads(int i, int j) |
|||
} |
|||
void render() |
|||
{ |
{ |
||
double x, y; |
|||
int e = 0; |
|||
int ax, ay; |
|||
glClear(GL_COLOR_BUFFER_BIT); |
|||
for(ax=-1; ax <= 1; ax++) { |
|||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); |
|||
for(ay=-1; ay <= 1; ay++) { |
|||
if ( ((i+ax) < 0) || ( (j+ay) < 0) || ( (i+ax) >= width ) || |
|||
pthread_mutex_lock(&lock); |
|||
( (j+ay) >= height ) ) continue; |
|||
if ( (ax==0) && (ay==0) ) continue; |
|||
glBindTexture(GL_TEXTURE_2D, texture); |
|||
if ( THIS(i+ax, j+ay) == cHEAD ) e++; |
|||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, min_y, tex_w, max_y - min_y + 1, |
|||
} |
|||
TEX_MODE, GL_UNSIGNED_BYTE, tex[min_y]); |
|||
} |
|||
max_y = 0; min_y = rows; |
|||
return e; |
|||
glBegin(GL_QUADS); |
|||
x = (double)cols / tex_w; |
|||
y = (double)rows / tex_w; |
|||
glTexCoord2f(0, 0); glVertex2i( 0, 0); |
|||
glTexCoord2f(x, 0); glVertex2i(cols, 0); |
|||
glTexCoord2f(x, y); glVertex2i(cols, rows); |
|||
glTexCoord2f(0, y); glVertex2i( 0, rows); |
|||
glEnd(); |
|||
glFlush(); |
|||
glFinish(); |
|||
pthread_mutex_unlock(&lock); |
|||
x = (evolve_delay < show_delay) ? evolve_delay : show_delay; |
|||
if (x) usleep(x * 1000); |
|||
} |
} |
||
void |
void set_delay(int dec) |
||
{ |
{ |
||
if (dec) evolve_delay >>= 1; |
|||
int j, i; |
|||
else{ |
|||
int ehn; |
|||
evolve_delay <<= 1; |
|||
if (!evolve_delay) |
|||
for(i=0; i < width; i++) { |
|||
evolve_delay = 1; |
|||
for(j=0; j < height; j++) { |
|||
} |
|||
switch( THIS(i, j) ) |
|||
{ |
|||
if (evolve_delay > 1024) { |
|||
case cHEAD: |
|||
evolve_delay = 1024; |
|||
NEXT(i, j) = cTAIL; |
|||
paused = 1; |
|||
break; |
|||
} else |
|||
case cTAIL: |
|||
paused = 0; |
|||
NEXT(i, j) = cCONDUCTOR; |
|||
printf("Delay: %d ms\n", evolve_delay); |
|||
break; |
|||
if (paused) printf("Paused\n"); |
|||
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++; |
|||
} |
} |
||
void show_help() |
|||
{ |
|||
printf("Keys:\n\t'<': slow down\n\t'>': speed up\n\t'h': this message\n\t" |
|||
"'s': single step\n\t<space>: pause\n\t'q', Esc: quit\n"); |
|||
} |
|||
void keypress(unsigned char key, int x, int y) |
|||
// key hit |
|||
void key_hit(unsigned char k, int x, int y) |
|||
{ |
{ |
||
switch(key) { |
|||
if ( k == 'q' ) |
|||
case ' ': if ((paused = !paused)) printf("Paused\n"); |
|||
{ |
|||
return; |
|||
glFinish(); |
|||
case 'S': |
|||
glutDestroyWindow(win); |
|||
case 's': printf((single = !single) ? "Single step\n" : "Continuous\n"); |
|||
exit(EXIT_SUCCESS); |
|||
return; |
|||
} else { |
|||
case 'q': |
|||
evolve_wireworld(); |
|||
case 27: glFinish(); |
|||
wireworld_show(); |
|||
glutDestroyWindow(gwin); |
|||
} |
|||
return; |
|||
case ',': |
|||
case '<': set_delay(0); return; |
|||
case '.': |
|||
case '>': set_delay(1); return; |
|||
case 'h': |
|||
case 'H': show_help(); return; |
|||
} |
|||
} |
} |
||
void resize(int w, int h) |
|||
{ |
|||
int dx = 0, dy = 0; |
|||
double scale; |
|||
w -= 10; h -= 10; |
|||
glViewport(5, 5, w, h); |
|||
if (w * rows > h * cols) { |
|||
scale = (double)h / rows; |
|||
dx = (w / scale - cols) / 2; |
|||
} else { |
|||
scale = (double)w / cols; |
|||
dy = (h / scale - rows) / 2; |
|||
} |
|||
glMatrixMode(GL_PROJECTION); |
|||
glLoadIdentity(); |
|||
glOrtho(-dx, cols + dx, rows + dy, -dy, -1, 1); |
|||
glMatrixMode(GL_MODELVIEW); |
|||
} |
|||
void set_texture() |
|||
int main(int argc, char *argv[]) |
|||
{ |
{ |
||
int i, j; |
|||
FILE *p; |
|||
for (i = 0; i < rows; i++) |
|||
for (j = 0; j < cols; j++) |
|||
tex[i][j] = colors[cells[i][j].t]; |
|||
glEnable(GL_TEXTURE_2D); |
|||
glGenTextures(1, &texture); |
|||
if ( argc < 2 ) { |
|||
glBindTexture(GL_TEXTURE_2D, texture); |
|||
fprintf(stderr, "specify a wireworld initial state\n"); |
|||
exit(EXIT_FAILURE); |
|||
} |
|||
glTexImage2D(GL_TEXTURE_2D, 0, TEX_COMP, tex_w, tex_w, |
|||
0, TEX_MODE, GL_UNSIGNED_BYTE, tex[0]); |
|||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
|||
if ( (p = fopen(argv[1], "r")) != NULL ) { |
|||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
|||
min_y = rows; |
|||
max_y = 0; |
|||
} |
|||
void init_gfx(char *prog) |
|||
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); |
|||
int one = 1; |
|||
atexit(freemem); |
|||
glutInit(&one, &prog); |
|||
fc = 0; |
|||
glutInitDisplayMode(GLUT_RGB); |
|||
glutInitWindowSize(320, 240); |
|||
glutDisplayFunc(render); |
|||
glutIdleFunc(render); |
|||
glutDisplayFunc(render); |
|||
gwin = glutCreateWindow("Wireworld"); |
|||
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); |
|||
glutKeyboardFunc(keypress); |
|||
// no more error check :) |
|||
glutReshapeFunc(resize); |
|||
glutInit(&argc, argv); |
|||
set_texture(); |
|||
} |
|||
int main(int c, char **v) |
|||
{ |
|||
pthread_t th; |
|||
if (c < 2) { |
|||
win = glutCreateWindow("Wireworld"); |
|||
printf("Usage: %s input_file\n", v[0]); |
|||
glutInitWindowPosition(0,0); |
|||
return 0; |
|||
glutInitWindowSize(width*PPP, height*PPP); |
|||
} |
|||
if (!read_file(v[1])) return 0; |
|||
glutDisplayFunc(wireworld_show); |
|||
glutKeyboardFunc(key_hit); |
|||
show_help(); |
|||
// (-1,-1) from lower leftmost to upper leftmost corner |
|||
init_gfx(v[0]); |
|||
float matrix[16] = { |
|||
1, 0, 0, 0, |
|||
0, -1, 0, 0, |
|||
0, 0, 1, 0, |
|||
0, 0, 0, 1 |
|||
}; |
|||
glLoadMatrixf(matrix); |
|||
pthread_create(&th, 0, updater, 0); |
|||
glutMainLoop(); |
|||
pthread_detach(th); |
|||
} else { |
|||
fprintf(stderr, "cannot open %s\n", argv[1]); |
|||
exit(EXIT_FAILURE); |
|||
} |
|||
glutMainLoop(); |
|||
exit(EXIT_SUCCESS); |
|||
return 0; |
|||
}</lang> |
}</lang> |
Latest revision as of 20:14, 22 July 2011
Run the program to see a short help message about key bindings.
Compile with gcc -lpthread -lglut -lGL -lGLU
. Run with a.out file_name
. There are a couple of larger test files on the talk page. By default the program is compiled to use grayscale; compile time macro is available to switch to RGB.
<lang c>#include <stdio.h>
- include <stdlib.h>
- include <unistd.h>
- include <fcntl.h>
- include <sys/stat.h>
- include <sys/types.h>
- include <sys/mman.h>
- include <err.h>
- include <pthread.h>
- include <GL/glut.h>
- include <GL/gl.h>
- include <GL/glu.h>
enum { s_blank = 0, s_condu, s_etail, s_ehead }; typedef struct { unsigned char n, mark, t;} cell_t; typedef struct { int x, y; } pos_t;
- define USE_RGB 0 /* enable this for hideous colors */
- if USE_RGB
typedef struct { unsigned char r, g, b; } rgb_t; rgb_t colors[] = { {0, 0, 0}, {33, 45, 10}, {30, 100, 80}, {255, 0, 0}, };
- define TEX_COMP 3
- define TEX_MODE GL_RGB
- else /* grayscale, less texture transfer */
typedef unsigned char rgb_t; rgb_t colors[] = { 0, 64, 144, 255 };
- define TEX_COMP 1
- define TEX_MODE GL_LUMINANCE
- endif
rgb_t **tex;
int tex_w = 1;
cell_t **cells; pos_t *heads, *bakup; int n_heads, n_bakup; int rows = 0, cols = 0, n_cond = 0; int evolve_delay = 128, show_delay = 16; int paused = 0, single = 0; int min_y, max_y;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
int gwin; GLuint texture;
void mark_neighbors(pos_t *p) { int x, y; for (y = p->y - 1; y <= p->y + 1 && y < rows; y++) { if (y < 0) continue; for (x = p->x - 1; x <= p->x + 1 && x < cols; x++) { if ( x < 0 || (x == p->x && y == p->y) || cells[y][x].t != s_condu ) continue;
cells[y][x].n++;
if (!cells[y][x].mark) { cells[y][x].mark = 1; bakup[n_bakup].x = x; bakup[n_bakup].y = y; n_bakup ++; } } } }
void evolve() { int i; pos_t *p; cell_t *c; n_bakup = 0; for (i = 0, p = heads; i < n_heads; i++, p++) { c = &cells[p->y][p->x]; switch(c->t) { case s_ehead: mark_neighbors(p); case s_etail: bakup[n_bakup++] = *p; } }
for ( i = 0; i < n_bakup; c->n = 0) { p = bakup + i; c = &cells[p->y][p->x]; c->mark = 0; if (p->y < min_y) min_y = p->y; if (p->y > max_y) max_y = p->y;
switch(c->t) { case s_ehead: c->t = s_etail; i++; tex[p->y][p->x] = colors[s_etail]; continue; case s_etail: c->t = s_condu; tex[p->y][p->x] = colors[s_condu]; break; case s_condu: if (c->n > 2) break; c->t = s_ehead; tex[p->y][p->x] = colors[s_ehead]; i++; continue; } *p = bakup[--n_bakup]; }
n_heads = n_bakup; p = heads; heads = bakup; bakup = p; if (single) paused = 1; }
- define die(act) err(1, "Can't %s %s", act, fn);
int read_file(char *fn) { struct stat st; size_t ofs; int fd = open(fn, O_RDONLY); if (fd == -1) die("open"); if (fstat(fd, &st)) die("stat"); char *map = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (map == (void*)-1) die("mmap");
int i = 0; /* pass 1, get various sizes */ for (ofs = 0; ofs < st.st_size; ofs++) { switch(map[ofs]) { case '\n': break; case 't': case 'H': case '.': n_cond++; case ' ': i++; case '\r': continue; default: printf("Bad char %c at row %d, col %d\n", map[ofs], rows, i); goto bail; }
if (i > cols) cols = i; rows ++; i = 0; }
heads = malloc(sizeof(pos_t) * n_cond); bakup = malloc(sizeof(pos_t) * n_cond);
cells = malloc(sizeof(cell_t*) * rows); cells[0] = calloc(cols * rows, sizeof(cell_t));
for (i = 1; i < rows; i++) cells[i] = cells[i - 1] + cols;
while (tex_w < cols) tex_w <<= 1; while (tex_w < rows) tex_w <<= 1;
tex = malloc(sizeof(rgb_t *) * tex_w); tex[0] = calloc(tex_w * tex_w, sizeof(rgb_t));
for (i = 1; i < tex_w; i++) tex[i] = tex[i - 1] + tex_w;
/* pass 2, convert char file to data */ int j = i = n_heads = 0; for (ofs = 0; ofs < st.st_size; ofs++) { switch(map[ofs]) { default: continue; case '\n': i++; j = 0; continue; case '.': cells[i][j++].t = s_condu; continue; case ' ': cells[i][j++].t = s_blank; continue; case 't': cells[i][j].t = s_etail; goto add_cell; case 'H': cells[i][j].t = s_ehead; goto add_cell; } add_cell: heads[n_heads].x = j++; heads[n_heads].y = i; n_heads++; } bail: munmap(map, st.st_size); close(fd);
return 1; }
void * updater(void * _) { while (1) { if (evolve_delay) usleep(evolve_delay * 1000); if (paused) continue;
pthread_mutex_lock(&lock); evolve(); pthread_mutex_unlock(&lock); }
return 0; }
void render() { double x, y;
glClear(GL_COLOR_BUFFER_BIT); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
pthread_mutex_lock(&lock);
glBindTexture(GL_TEXTURE_2D, texture); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, min_y, tex_w, max_y - min_y + 1, TEX_MODE, GL_UNSIGNED_BYTE, tex[min_y]); max_y = 0; min_y = rows;
glBegin(GL_QUADS);
x = (double)cols / tex_w; y = (double)rows / tex_w; glTexCoord2f(0, 0); glVertex2i( 0, 0); glTexCoord2f(x, 0); glVertex2i(cols, 0); glTexCoord2f(x, y); glVertex2i(cols, rows); glTexCoord2f(0, y); glVertex2i( 0, rows);
glEnd();
glFlush(); glFinish(); pthread_mutex_unlock(&lock);
x = (evolve_delay < show_delay) ? evolve_delay : show_delay; if (x) usleep(x * 1000); }
void set_delay(int dec) { if (dec) evolve_delay >>= 1; else{ evolve_delay <<= 1; if (!evolve_delay) evolve_delay = 1; }
if (evolve_delay > 1024) { evolve_delay = 1024; paused = 1; } else paused = 0; printf("Delay: %d ms\n", evolve_delay); if (paused) printf("Paused\n"); }
void show_help() { printf("Keys:\n\t'<': slow down\n\t'>': speed up\n\t'h': this message\n\t" "'s': single step\n\t<space>: pause\n\t'q', Esc: quit\n"); }
void keypress(unsigned char key, int x, int y) { switch(key) { case ' ': if ((paused = !paused)) printf("Paused\n"); return; case 'S': case 's': printf((single = !single) ? "Single step\n" : "Continuous\n"); return; case 'q': case 27: glFinish(); glutDestroyWindow(gwin); return; case ',': case '<': set_delay(0); return; case '.': case '>': set_delay(1); return; case 'h': case 'H': show_help(); return; } }
void resize(int w, int h) { int dx = 0, dy = 0; double scale;
w -= 10; h -= 10; glViewport(5, 5, w, h); if (w * rows > h * cols) { scale = (double)h / rows; dx = (w / scale - cols) / 2; } else { scale = (double)w / cols; dy = (h / scale - rows) / 2; }
glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-dx, cols + dx, rows + dy, -dy, -1, 1); glMatrixMode(GL_MODELVIEW); }
void set_texture() { int i, j; for (i = 0; i < rows; i++) for (j = 0; j < cols; j++) tex[i][j] = colors[cells[i][j].t];
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, TEX_COMP, tex_w, tex_w, 0, TEX_MODE, GL_UNSIGNED_BYTE, tex[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); min_y = rows; max_y = 0; }
void init_gfx(char *prog) { int one = 1; glutInit(&one, &prog); glutInitDisplayMode(GLUT_RGB); glutInitWindowSize(320, 240); glutDisplayFunc(render); glutIdleFunc(render); glutDisplayFunc(render);
gwin = glutCreateWindow("Wireworld");
glutKeyboardFunc(keypress); glutReshapeFunc(resize);
set_texture(); }
int main(int c, char **v) { pthread_t th;
if (c < 2) { printf("Usage: %s input_file\n", v[0]); return 0; }
if (!read_file(v[1])) return 0;
show_help(); init_gfx(v[0]);
pthread_create(&th, 0, updater, 0); pthread_detach(th);
glutMainLoop(); return 0; }</lang>