/*	life.c

	Play Conway's Game of Life

	Reads a P1 file in, outputs a P1 file.

	Does as many timesteps as stated on the command line.

	Uses a classic 1-element fake zone internally.

	Jan. 30, 2013 by H. Dietz
*/

#include <stdio.h>
#include <stdlib.h>

unsigned char *
read_P1(int *xdim,
int *ydim)
{
	register int c, x, y;
	register unsigned char *p;

	if ((c = getchar()) != 'P') {
pbm_bad:
		fprintf(stderr, "Bad PBM input\n");
		exit(1);
	}
	if ((c = getchar()) != '1') goto pbm_bad;
	c = getchar();

#define	Eat_Space \
	while ((c == ' ') || \
	       (c == '\t') || \
	       (c == '\n') || \
	       (c == '\r') || \
	       (c == '#')) { \
		if (c == '#') while ((c = getchar()) != '\n') ; \
		c = getchar(); \
	}

	Eat_Space;		/* Eat white space and comments */

#define	Get_Number(n) \
	{ \
		if ((c < '0') || (c > '9')) goto pbm_bad; \
 \
		n = (c - '0'); \
		c = getchar(); \
		while ((c >= '0') && (c <= '9')) { \
			n *= 10; \
			n += (c - '0'); \
			c = getchar(); \
		} \
	}

	Get_Number(*xdim);	/* Get image width */

	Eat_Space;		/* Eat white space and comments */
	Get_Number(*ydim);	/* Get image height */

	p = ((unsigned char *)
	     calloc(((*xdim + 2) * (*ydim + 2)),
		    sizeof(unsigned char)));
	if (p == 0) goto pbm_bad;

	Eat_Space;

	for (y=0; y<*ydim; ++y) {
		for (x=0; x<*xdim; ++x) {
			Eat_Space;
			switch (c) {
			case '1':	p[x+1+((y+1)*(*xdim+2))] = 1; break;
			case '0':	/* 0 from calloc() */ break;
			default:	goto pbm_bad;
			}
			c = getchar();
		}
	}

	return(p);
}

#undef	Eat_Space
#undef	Get_Number


write_P1(register int xdim,
register int ydim,
register unsigned char *p)
{
	register int x, y, pos = 0;

	printf("P1\n"
	       "# CREATOR: P1IO\n"
	       "%d %d\n",
	       xdim,
	       ydim);

	for (y=0; y<ydim; ++y) {
		for (x=0; x<xdim; ++x) {
			int c = "01"[ p[x+1+((y+1)*(xdim+2))] != 0 ];
			putchar(c);

			/* Don't allow more than 64 chars per line */
			if ((++pos & 63) == 0) {
				putchar('\n');
			}
		}
	}
	putchar('\n');
}


int
main(int argc, char **argv)
{
	int xdim, ydim;
	register unsigned char *p, *q;
	register int x, y, t, timesteps;

	if (argc != 2) {
		fprintf(stderr, "Usage: %s timesteps\n", argv[0]);
		exit(2);
	}
	timesteps = atoi(argv[1]);

	/* Read the initial state image */
	p = read_P1(&xdim, &ydim);

	/* Make a target image (with blank fake zone) */
	q = ((unsigned char *)
	     calloc(((xdim + 2) * (ydim + 2)),
		    sizeof(unsigned char)));
	if (q == 0) {
		fprintf(stderr, "Calloc failed\n");
		exit(3);
	}

	/* Iterate over the timesteps */
	for (t=0; t<timesteps; ++t) {
#ifdef	VERBOSE
		fprintf(stderr, "Timestep %d...\n", t);
#endif

		for (y=0; y<ydim; ++y) {
			for (x=0; x<xdim; ++x) {

#define	FAKE(P, X, Y) P[((X)+1)+(((Y)+1)*(xdim+2))]

				register int live = FAKE(p, x-1, y-1) +
						    FAKE(p, x, y-1) +
						    FAKE(p, x+1, y-1) +
						    FAKE(p, x-1, y) +
						    FAKE(p, x+1, y) +
						    FAKE(p, x-1, y+1) +
						    FAKE(p, x, y+1) +
						    FAKE(p, x+1, y+1);

				/* Apply Conway's rules...
				   (yes, I know this is clever code!)
				*/
				FAKE(q, x, y) = ((live == 3) ||
						 ((live == 2) &&
						  FAKE(p, x, y)));
			}
		}

		/* Swap our notion of p and q...
		   this saves us copying every timestep
		*/
		{
			register unsigned char *pt = p;
			p = q;
			q = pt;
		}
	}
	
	/* Done; write-out results */
	write_P1(xdim, ydim, p);
	exit(0);
}
