[net.math] one-dimensional cellular automata

ado@elsie.UUCP (Arthur David Olson) (09/22/84)

: to unbundle, "sh" this file -- DO NOT use csh
:  SHAR archive format.  Archive created Fri Sep 21 18:49:17 EDT 1984
echo x - ocelot.c
sed 's/^X//' >ocelot.c <<'+FUNKY+STUFF+'
X#include <stdio.h>
X
X#define TRUE	1
X#define FALSE	0
X
Xchar *	progname;
Xint	hflag;		/* output a header line */
Xint	lflag;		/* label lines with generation numbers */
Xint	nflag;		/* output only number of repeating generation */
Xlong	gvalue;		/* output only every so many generations */
Xlong	wvalue;		/* work with this many cells */
Xlong	xvalue;		/* exit after so many generations */
Xchar *	istring;	/* initial state(s) */
X
Xlong	generation;
Xchar	t_s_map[28];	/* maps Totals of states of neighbors to new State */	
X
Xmain(argc, argv)
Xint	argc;
Xchar *	argv[];
X{
X	register char *	cp;
X	register	i;
X	char *		now;
X	char *		new;
X	char *		tmp;
X	char **		oldargv;
X	int		oldargc;
X	char		dum;
X	long		l;
X	unsigned	n;
X	extern char *	malloc();
X
X	oldargc = argc;
X	oldargv = argv;
X	progname = cp = argv[0];
X	while (*cp != '\0')
X		if (*cp++ == '/')
X			progname = cp;
X	++argv;
X	--argc;
X	if (argc == 1 && argv[1][0] == '=' && argv[1][1] == '\0')
X		for ( ; ; )
X			usage();
X	while (argc > 0) {
X		cp = argv[0];
X		if (*cp++ != '-')
X			break;
X		if (*cp == '-') {	/* end of options */
X			++argv;
X			--argc;
X			break;
X		}
X		switch (*cp) {
X			case '\0':
X				for ( ; ; )
X					wildexit("lone dash as argument");
X			case 'g':
X			case 'w':
X			case 'x':
X			case 'i':	/* Value-taking flags */
X				if (*(cp + 1) != '\0')
X					for ( ; ; )
Xwildexit("stuff after value-taking flag");
X				++argv;
X				--argc;
X				if (argc == 0)
X					for ( ; ; )
Xwildexit("option without value");
X				dum = '\0';
X				i = sscanf(argv[0], "%ld%c", &l, &dum);
X				if (i != 1 || dum != '\0' ||
X					(*cp != 'i' && l <= 0))
X						for ( ; ; )
Xwildexit("option value");
X				switch (*cp) {
X					case 'i':
X						istring = argv[0];
X						break;
X					case 'g':
X						gvalue = l;
X						break;
X					case 'w':
X						wvalue = l;
X						break;
X					case 'x':
X						xvalue = l;
X						break;
X				}
X				break;
X			default:		/* Handle flag options */
X				while (*cp != '\0') switch (*cp++) {
X					case 'h':
X						hflag = TRUE;
X						break;
X					case 'l':
X						lflag = TRUE;
X						break;
X					case 'n':
X						nflag = TRUE;
X						break;
X					default:
X						for ( ; ; )
X							usage();
X				}
X				break;
X		}
X		++argv;
X		--argc;
X	}
X	if (argc != 1)
X		for ( ; ; )
X			usage();
X	/*
X	** Crack the code.
X	*/
X	for (cp = argv[0]; *cp != '\0'; ++cp)
X		;
X	for (i = 0; --cp >= argv[0]; ++i) {
X		if (ctoi(*cp) < 0)
X			for ( ; ; )
X				wildexit("code argument");
X		if (i >= (sizeof t_s_map / sizeof t_s_map[0]))
X			for ( ; ; )
X				wildexit("long code argument");
X		t_s_map[i] = ctoi(*cp);
X	}
X	/*
X	** Handle defaults.
X	*/
X	if (gvalue == 0)
X		gvalue = 1;
X	if (wvalue == 0)
X		wvalue = lflag ? 72 : 80;
X	if (istring != 0 && strlen(istring) > wvalue)
X		for ( ; ; )
X			wildexit("length of -i string (greater than width)");
X	for (n = 0; n < wvalue; ++n);	/* Don't ask! */
X	now = malloc(n);
X	new = malloc(n);
X	if (now == NULL || new == NULL)
X		for ( ; ; )
X			wildexit("result from malloc");
X	for (l = 0; l < wvalue; ++l)
X		now[l] = 0;
X	if (istring == 0)
X		now[wvalue / 2] = 1;
X	else	for (i = 0; istring[i] != '\0'; ++i)
X			now[((wvalue - strlen(istring)) / 2) + i] = 
X				ctoi(istring[i]);
X	if (!nflag) {
X		if (hflag) {
X			for (i = 0; i < oldargc; ++i) {
X				if (i != 0)
X					putchar(' ');
X				printf("%s", oldargv[i]);
X			}
X			putchar('\n');
X		}
X		dump(now);
X	}
X	for (generation = 1; xvalue == 0 || generation <= xvalue; ++generation){
X		update(now, new);
X		if (same(now, new)) {
X			if (nflag)
X				printf("%ld\n", generation);
X			for ( ; ; )
X				exit(0);
X		}
X		if (!nflag && (generation % gvalue) == 0)
X			dump(new);
X		tmp = now;
X		now = new;
X		new = tmp;
X	}
X	if (nflag)
X		printf("0\n");
X	for ( ; ; )
X		exit(0);
X}
X
Xwildexit(string)
Xchar *	string;
X{
X	fprintf(stderr, "%s: wild %s\n", progname, string);
X	for ( ; ; )
X		exit(1);
X}
X
Xusage()
X{
X	fprintf(stderr, "%s: Usage: %s ", progname, progname);
X	fprintf(stderr, 
X	"[-h] [-l] [-n] [-g gap] [-w width] [-i initial] [-x exitgen] code\n");
X	for ( ; ; )
X		exit(1);
X}
X
Xdump(buf)
Xregister char *	buf;
X{
X	register long	n;
X
X	if (nflag)
X		return;
X	if (lflag)
X		printf("%7ld-", generation);
X	n = wvalue;
X	do putchar(" 123456789"[*buf++]);
X		while (--n > 0);
X	putchar('\n');
X}
X
Xupdate(now, new)
Xregister char *	now;
Xregister char *	new;
X{
X	register char *	map;
X	register long	n;
X
X	map = t_s_map;
X	n = wvalue;
X	if (n == 1)
X		new[0] = map[now[0]];
X	else if (n == 2)
X		new[0] = new[1] = map[now[0] + now[1]];
X	else {
X		*new++ = map[*now + *(now + 1)];
X		++now;
X		n -= 2;
X		do {
X			*new++ = map[*(now - 1) + *now + *(now + 1)];
X			++now;
X		} while (--n > 0);
X		*new = map[*(now - 1) + *now];
X	}
X}
X
Xint		same(now, new)
Xregister char *	now;
Xregister char *	new;
X{
X	register long	n;
X
X	n = wvalue;
X	do if (*now++ != *new++)
X		return FALSE;
X			while (--n > 0);
X	return TRUE;
X}
X
Xint	ctoi(c)
X{
X	int	i;
X	char	buf[2];
X
X	buf[0] = c;
X	buf[1] = '\0';
X	return (sscanf(buf, "%d", &i) == 1) ? i : -1;
X}
+FUNKY+STUFF+
ls -l ocelot.c
echo x - ocelot.n
sed 's/^X//' >ocelot.n <<'+FUNKY+STUFF+'
X.TH OCELOT new
X.SH NAME
Xocelot \- one-dimensional cellular automata
X.SH SYNOPSIS
X.B ocelot
X[ option ... ] code
X.br
X.B ocelot =
X(to get a `how to use it' message)
X.SH DESCRIPTION
X.I Ocelot
Xlets you observe one-dimensional cellular automata through time.
XThe automata systems handled are the `code-numbered' ilk described by
XWolfram in the 9/84
X.ul
XScientific American.
X.PP
XAt each instant,
Xeach automaton is in a numbered state.
XIt's state at the next instant is determined by the sum of the
Xstates of it and its neighbors.
XThe low-order digit of the
X.I code
Xargument to
X.I ocelot
Xis the state at the next instant if the sum is zero;
Xthe next-to-low-order digit is the state at the next instant if the sum is one;
Xand so forth.
XIf a sum is greater than the number of digits in the
X.IR code ,
Xthe state at the next instant is zero.
X.PP
XIn the absence of command line options,
X.I ocelot
Xcreates a system of eighty automata with the fortieth automaton in state one
Xand all others in state zero.
XIt lists these states on the standard output,
Xwith state zero represented by a blank and other states represented by a digit.
XIt then loops,
Xdetermining and listing the states of the automata at later times.
XIt exits if the states of all automata at two successive instants are the same.
X.PP
X.PP
XThese options are available:
X.TP
X.BI \-g \ number
XOnly output each
X.IR number th
Xgeneration (the number is known as the `generation gap').
X.TP
X.BI \-w \ number
XWork with
X.I number
Xautomata.
X.TP
X.BI \-x \ number
XExit after at most
X.I number
Xgenerations.
X.TP
X.BI \-i \ string
XInitialize the central automata to the state(s) given in
X.IR string .
X.TP
X.B \-h
XOutput a header line.
X.TP
X.B \-l
XLabel each line with its `generation number.'
XIf this option is used and no `w' option is used,
Xa system of 72 automata is created.
X.TP
X.B \-n
XOnly output the number of the generation which repeats its predecessor.
XIf this option and the `x' option are used,
Xzero is output if the number of generations specified with the `x' option
Xpasses without a generation repeating its predecessor.
X.PP
XAutomata may have at most ten states.
X.SH EXAMPLES
XThe command
X.ti +.5i
X.B "ocelot 2213310"
X.br
Xruns the system illustrated in the upper-left-hand corner of page 199 of
XWolfram's article.
X.PP
XThe command
X.ti +.5i
X.B "ocelot 6345789"
X.br
Xproves that inspiring automata systems are distinct from inspiring pop tunes.
+FUNKY+STUFF+
ls -l ocelot.n
exit 0