htmllife.c


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

#define MAX_EDGE	256
#define MAX_LINE	512
#define MAX_TITLE	64
#define WORLDSIZE	(worldedge.x * worldedge.y * 2)


typedef struct {
	int x;
	int y;
} coordination;

static coordination worldedge;
typedef char celltype;


static coordination StandardizeCoordination(coordination cd) {
	if (cd.x < 0) {
		cd.x = worldedge.x - 1;
	}
	else if (worldedge.x <= cd.x) {
		cd.x = 0;
	}
	if (cd.y < 0) {
		cd.y = worldedge.y - 1;
	}
	else if (worldedge.y <= cd.y) {
		cd.y = 0;
	}
	return cd;
}
static coordination GetAroundCoordination(int n, coordination cd) {
	switch (n) {
	case 0:
	case 3:
	case 5:
		cd.x--;
		break;
	case 2:
	case 4:
	case 7:
		cd.x++;
		break;
	}
	switch (n) {
	case 0:
	case 1:
	case 2:
		cd.y--;
		break;
	case 5:
	case 6:
	case 7:
		cd.y++;
		break;
	}
	return StandardizeCoordination(cd);
}
static celltype *Cell(celltype *world, coordination cd, int generation) {
	int idx;

	assert(-1 <= cd.x && cd.x <= worldedge.x);
	assert(-1 <= cd.y && cd.y <= worldedge.y);
	idx = (cd.x * worldedge.y + cd.y) * 2 + (generation & 1);
	assert(0 <= idx && idx < WORLDSIZE);
	return world + idx;
}
static void NextGeneration(celltype *world, int generation) {
	coordination cd;
	int nbr, i;

	for (cd.x = 0; cd.x < worldedge.x; cd.x++) {
		for (cd.y = 0; cd.y < worldedge.y; cd.y++) {
			nbr = 0;
			for (i = 0; i < 8; i++) {
				if (*Cell(world, GetAroundCoordination(i, cd), generation)) {
					nbr++;
				}
			}
			if (nbr == 3) {
				*Cell(world, cd, generation + 1) = 1;
			}
			else if (nbr == 2) {
				*Cell(world, cd, generation + 1) = *Cell(world, cd, generation);
			}
			else {
				*Cell(world, cd, generation + 1) = 0;
			}
		}
	}
}
static celltype *InitWorld(const char *patternfile, char *title) {
	FILE *pf;
	celltype *world;
	char line[MAX_LINE];
	char *p;
	const char delimiter[] = "\t";
	coordination cd;

	pf = fopen(patternfile, "rt");
	if (!pf) {
		perror(patternfile);
		exit(3);
	}

	/* ヘッダを読む */
	fgets(line, sizeof(line), pf);
	/* タイトル */
	p = strtok(line, delimiter);
	if (!p) {
		fputs("タイトルがない", stderr);
		exit(3);
	}
	strncpy(title, p, MAX_TITLE);
	title[MAX_TITLE - 1] = '\0';
	/* edge-x */
	p = strtok(NULL, delimiter);
	if (!p) {
		fputs("xがない", stderr);
		exit(3);
	}
	worldedge.x = atoi(p);
	if (worldedge.x <= 0 || MAX_EDGE < worldedge.x) {
		fputs("xが変", stderr);
		exit(3);
	}
	/* edge-y */
	p = strtok(NULL, delimiter);
	if (!p) {
		fputs("yがない", stderr);
		exit(3);
	}
	worldedge.y = atoi(p);
	if (worldedge.y <= 0 || MAX_EDGE < worldedge.y) {
		fputs("yが変", stderr);
		exit(3);
	}

	/* パターン読みこみ */
	world = (celltype *)malloc(WORLDSIZE);
	if (!world) {
		fputs("メモリ足りん", stderr);
		exit(3);
	}
	memset(world, 0, WORLDSIZE);
	for (cd.y = 0; cd.y < worldedge.y; cd.y++) {
		if (!fgets(line, sizeof(line), pf)) {
			break;
		}
		for (cd.x = 0, p = line; cd.x < worldedge.x && *p && *p != '\n'; cd.x++, p++) {
			*Cell(world, cd, 0) = (*p == '*' || *p == 'O') ? 1 : 0;
		}
	}
	fclose(pf);
	return world;
}

static FILE *OpenHtml(int generation, const char *fileprefix) {
	FILE *pf;
	char filename[64];

	assert(fileprefix);
	sprintf(filename, "%s%03d.html", fileprefix, generation);
	pf = fopen(filename, "wt");
	if (!pf) {
		perror(filename);
		exit(3);
	}
	return pf;
}
static void PrintWorld(celltype *world, int generation, int maxgen, const char *title, const char *fileprefix) {
	coordination cd;
	FILE *pf;
	int i, xcnt, ycnt;

	pf = OpenHtml(generation, fileprefix);
	fprintf(pf, "<HTML>\n<HEAD>\n<TITLE>%s</TITLE>\n", title);
	fprintf(pf, "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=Shift_JIS\">\n");
	fprintf(pf, "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"0; URL=%s%03d.html\">", fileprefix, generation + 1);
	fprintf(pf, "\n</HEAD>\n<BODY>\n<P>%d / %d</P>\n<HR>\n<P><TT><SMALL>\n", generation + 1, maxgen);
	ycnt = 0;
	for (cd.y = 0; cd.y < worldedge.y; cd.y++) {
		xcnt = 0;
		for (cd.x = 0; cd.x < worldedge.x; cd.x++) {
			if (*Cell(world, cd, generation)) {
				for (i = 0; i < ycnt; i++) {
					fputs("<BR>\n", pf);
				}
				ycnt = 0;
				for (i = 0; i < xcnt; i++) {
					fputs(" ", pf);
				}
				xcnt = 0;
				fputs("●", pf);
			}
			else {
				xcnt++;
			}
		}
		ycnt++;
	}
	fputs("\n</SMALL></TT></P>\n</BODY></HTML>", pf);
	fclose(pf);
}
static void Finish(int generation, const char *title, const char *fileprefix) {
	FILE *pf;

	pf = OpenHtml(generation, fileprefix);
	fprintf(pf, 
		"<HTML>\n"
		"<HEAD>\n"
		"<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=Shift_JIS\">\n"
		"<TITLE>%s</TITLE>\n"
		"</HEAD>\n"
		"<BODY>\n"
		"<H1>おしまい</H1>\n"
		"<HR>\n"
		"<P><A HREF=\"./%s000.html\">もう一度</A> <A HREF=\"./\">戻る</A></P>\n"
		"</BODY>\n"
		"</HTML>\n"
		,title ,fileprefix);
	fclose(pf);
}
int main(int argc, char **argv) {
	int generation, maxgen;
	celltype *world;
	char title[MAX_TITLE];
	char *fileprefix;

	if (argc != 3) {
		fputs("usage: htmllife <number-generation> <pattern-file>", stderr);
		return 0;
	}
	maxgen = (int)strtoul(argv[1], NULL, 10);
	fileprefix = argv[2];
	world = InitWorld(fileprefix, title);
	for (generation = 0; generation < maxgen; generation++) {
		PrintWorld(world, generation, maxgen, title, fileprefix);
		printf("%d/%d\r", generation, maxgen);
		NextGeneration(world, generation);
	}
	Finish(generation, title, fileprefix);
	free(world);
	return 0;
}