/* ant.c -- a multi-state generealization of Chris Langton's ant Send comments to Jim Propp (propp@math.wisc.edu). To compile this program: type cc ant.c -DSIZE= -lcurses -ltermcap -o ant (where is the three less than the number of lines on your screen or two less than half the number of columns, whichever is lesser; default is 21) To use the program: type ant (where is a small positive integer; see below) Guided Tour for beginners: To try out the program without having to read much, simply start it up (type "ant") and then start hitting the "s" key over and over. You can also hold down the "s" key to speed up the action. At some point, you might want to type "f" if the ant goes off the screen. When even this gets to be too slow, type "g" and watch what happens shortly after step 10000. The structure you see the ant building is called a "highway". When you get the idea, interrupt with "control-c" and reinitialize with the command "i0". Now type "~" and then type "t". Then type "t" again. And again. Pay especial attention to the pattern at steps 8, 96, 184, 368, 384, and 472. Run-time Commands: s: Step g: Go until interrupted t: Go until trapped ^c: Interrupt r: Redraw screen c: Center ^H,J,K,L: Move window z: Zoom out (see below) i: Re-initialize screen in state 0 h,j,k,l: Move cursor : Change state at cursor location A: Put ant at cursor with specified heading ~: Put trap at cursor location f: Toggle follow-status flag d: Give on-screen description of site corresponding to current cursor position -: Make time go backwards [not implemented] +: Make time go forwards [not implemented] !: Set temporal resolution (see below) o: Output current state (ASCII-style) O: Output current state (Postscript-style) T: Output current state (Tiled, Postscript) L: Toggle log-status flag q: Quit The universe is a 1000-by-1000 square grid. Each site in the universe has a state between 0 and 9. At any instant, the ant occupies exactly one of the sites. The screen shows an N-by-N window on the universe (unless zoom-out is in force) with a moving cursor. State 0 is represented by a blank; all other states are represented by the respective digit-characters. The ant's current location is usually marked by the cursor. The program has two modes: setup-mode (interactive) and run-mode (non-interactive). There are three commands that switch the program from setup-mode to run-mode: "s" (Step), "g" (Go), and "t" (go until Trapped). During run-mode, no commands are recognized aside from control-c, which returns the program to setup-mode. "s" makes the ant-universe advance by one time-step. The ant: + increments the state of the site it currently occupies, modulo the number of states; + makes a right- or left-turn relative to its former heading, in accordance with its rule-string; and + moves on its new heading to a new site. The ant's initial position is at the center of the universe, and its initial heading is downwards. When the ant reaches the edge of the universe, it stops. "g" runs the universe forward indefinitely (i.e., until the ant hits the edge or until the user interrupts the program with control-c). "t" runs the universe the ant hits a trap. There can be only one trapped location; by default, it is the center of the universe. The user can move the trap elsewhere using the "~" command. "r" (Redraw) redraws the screen. "c" (Center) moves the window so that the current cursor position is at the center of the window. The shift-control command characters control-H, control-J, control-K, and control-L move the window in the four cardinal directions. The two-keystroke commands z0,z1,... (Zoom) set up various degrees of zoom-out. "z0" is the normal mode, showing an N-by-N block of the universe inside the N-by-N window; under the kth mode of zoom-out, each screen-location corresponds to a 2-to-the-k by 2-to-the-k block in the universe, and is marked with a " " if all sites in the block are in state 0 and is marked with a "*" otherwise. [Note: Zooming has not yet been debugged; it can cause the program to bomb. Also, it is very slow. I suggest going no further than "z3".] To re-initialize the universe, the command "i" will return every site in the universe to state 0 (as at start-up). To move the cursor, use the commands "h", "j", "k", and "l". To change the state at the site currently occupied by the cursor, simply type the digit of the desired state. The command "~" puts a trap at the current cursor location. To create an ant with initial heading to the right, one would type "Al". More generally, the command "A" (where is "h", "j", "k", or "l") creates an ant heading in the specified direction. By default, when the ant steps outside the current display window is moved, so that the ant always stays in view; the other possible display mode is one in which the window stays fixed no matter where the ant goes. To toggle this mode, type "f" (Follow / don't Follow). This command puts the cursor on the site occupied by the ant. To get information about the site marked by the cursor, type "d". (This is useful for learning the state of the site currently occupied by the ant.) The commands "+" and "-" make time go backward and forward respectively. [Note: I didn't implement "+" and "-" properly with respect to interrupt handling, so the results of using "-" are unpredictable.] The command "!", when followed by a positive integer n and a carriage return, tells the program that you want it to show the universe only at steps that are multiples of n. This resolution will be in effect for only the "g" and "t" run-modes (not for single-stepping). "o" (Output, ASCII-style) will output the current state of the universe to the file ant.out, in a human-readable way. "O" (Output, Postscript-style) outputs the current state to a new file in Postscript form; running lpr on this file will generate a gray-scale picture of the universe. For Postscript-style output, the name of the output file is ..PS, where is the ant's numerical code and gives the number of steps that the ant has taken. In both cases, only the portion of the universe that the ant has actually explored (together with a thin fringe of univisited sites) is shown. The command "L" (Log / don't Log) toggles the state of the "log-status" flag (initially 0). When the log-status flag is set to 1, the program will output to ant.out the sequence of states associated with the sites visited by the ant; this can be useful when one wants to determine highway-structure. When the log-status is 0, no information is output. When started, the program sets up a universe in which all sites are in a state 0 and a single ant is at the center of the universe, headed downward. To quit the program, type "q". Command-line rule-specification: The argument passed to the program from the command-line is interpreted as a code for the rule to be used by the ant. The program writes the argument in binary, and replaces each 1 by an R and each 0 by an L. E.g., 12 -> RRLL. This is the "rule-string". Read from left to right (with indexing starting at 0), this specifies the action that an ant takes when it passes through a site that has state k: it increments the state by 1 (modulo the length of the string), makes either a right turn or a left turn (according to whether the kth element in the rule-string is an R or an L), and proceeds one step forward. (Actually, the program internally represents 'R' by -1 and 'L' by +1.) Bugs: It is possible to crash the program by positioning the window in such a way some of the locations inside it do not correspond to sites in the ant-universe. More generally, the Center() subroutine needs work, as does Follow-mode. Enhancements to be added later: Allow time-reversal Include command to find center of universe */ #include #include #include void exit(); /* size parameters: */ #define U_WIDTH 1000 #define U_DEPTH 1000 /* 1000-by-1000 universe */ #ifndef SIZE #define SIZE 20 #endif #define W_WIDTH SIZE #define W_DEPTH SIZE /* SIZE-by-SIZE window */ /* Note: screen may look peculiar if universe size is not a multiple of window size. */ /* status codes (for runstat flag): */ #define CONTINUE 0 #define INTERRUPT 1 #define TRAP 2 #define STOP 3 /* control characters: */ #define BEL '\007' #define CTRL_H '\010' #define CTRL_J '\012' #define CTRL_K '\013' #define CTRL_L '\014' #define LAST_ROW W_DEPTH+2 /* last row of screen */ #define MOVE(a, b) move( 1 + (b) , 2 + 2 * (a) ) /* switch order of coordinates from y,x to x,y; allow 1 extra space for displaying frame; double horizontal scale for esthetic reasons */ #define IN_WINDOW(x, y) ((x) >= w_left && (x) <= w_right && \ (y) >= w_top && (y) <= w_bottom) /* see if cell (x,y) is in the current window */ #define MAX(p, q) ((p) > (q) ? (p) : (q)) #define MIN(p, q) ((p) < (q) ? (p) : (q)) /* GLOBAL VARIABLES */ char universe[U_WIDTH][U_DEPTH]; /* ant universe */ int numstates; /* number of states */ int rule[10]; /* rule describing ant behavior */ int rulecode; /* code number for rule-string */ struct { int x_coord; int y_coord; int heading; } ant, ant_old; /* location and heading of ant (code for heading: 0 = right 1 = up 2 = left 3 = down) */ char *direction[] = {"right", "up", "left", "down"}; int motion[4][2] = { {1, 0}, {0, -1}, {-1, 0}, {0, 1} }; /* increments in coordinates for the respective headings */ int xmin, xmax, ymin, ymax; /* coordinates describing smallest rectangle in which the ant has stayed so far; used by output routines to avoid printing data on unvisited parts of the universe */ int w_left, w_right, w_top, w_bottom; /* coordinates describing rectangle to be displayed on screen ("window" on universe) */ int xtrap, ytrap; /* coordinates of trap location */ int counter; /* move-counter */ int time_sign; /* direction of time */ int stride; /* number of steps per frame */ int zoom; /* zoom factor */ FILE *outfile; /* pointer to output file "ant.out" */ char runstat; /* run status */ char logstat; /* log status */ char folstat; /* follow-mode status */ char message[64]; /* buffer for printing messages */ int power[11] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024}; /* DECLARATION OF SUBROUTINES */ int Interrupt(); char *getenv(); FILE *fopen(); char Sign(); /* MAIN PROGRAM */ main(argc, argv) int argc; char **argv; { int i, j; signal(SIGINT, Interrupt); outfile = fopen("ant.out", "a"); /* open "ant.out" for appending */ if (outfile == NULL) { printf("cannot open ant.out\n"); exit(1); } if (argc < 2) rulecode = 2; /* Langton rule is default */ else rulecode = atoi(argv[1]); if (rulecode < 0 || rulecode >= 2048) { printf("argument out of range\n"); exit(1); } fprintf(outfile, "%% Ant type: %d\n", rulecode); j = rulecode; for (i = 0; i <= 10 && j >= power[i]; i++) ; numstates = i; for (i = numstates-1; i >= 0; i--) { if (j >= power[i]) { rule[numstates-i-1]=-1; j -= power[i]; } else { rule[numstates-i-1]=1; } } stride = 1; /* default: one step at a time */ initscr(); crmode(); noecho(); xmin = 0; xmax = U_WIDTH-1; ymin = 0; ymax = U_DEPTH-1; Initialize(); /* initialize to a field of zeroes, ant in center */ Center(); while (1) { Execute(); /* execute main loop forever */ } } /* SUBPROGRAMS */ Execute() { /* Get next command and execute it */ char c, d; int row, col; getyx(stdscr, row, col); c = getchar(); /* get next command-character */ if (c == EOF) Quit(); move(row, col); refresh(); switch(c) { case EOF: case 'q': Quit(); break; case 'i': Initialize(); break; case 'r': endwin(); initscr(); crmode(); noecho(); Redraw(); break; case 'o': Output_A(); break; case 'O': Output_P(); break; case 'T': Output_T(); break; case 'L': logstat = 1-logstat; break; case 'z': c = getchar(); if (c == EOF) Quit(); Zoom(c - '0'); break; case 'c': Center(); break; case CTRL_H: case CTRL_J: case CTRL_K: case CTRL_L: Move_Window(c); break; case 'h': case 'j': case 'k': case 'l': Move_Cursor(c); break; case ' ': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': State(c); break; case '~': Trap(); break; case 'A': d = getchar(); if (d == EOF) Quit(); if (d == 'h' || d == 'j' || d == 'k' || d == 'l') Ant(d); break; case 'f': if (folstat) folstat = 0; else { folstat = 1; if (IN_WINDOW(ant.x_coord, ant.y_coord)) MOVE((ant.x_coord - w_left) / zoom, (ant.y_coord - w_top ) / zoom); else Center(); refresh(); } break; case 'd': Describe(); break; case 's': Step(); refresh(); break; case 'g': Go(); break; case 't': Go_Trap(); Redraw(); break; case '-': break; case '+': break; case '!': stride = 0; sprintf(message, "Enter temporal resolution, followed by a carriage return:"); MESSAGE(message); d = getchar(); while (d >= '0' && d <= '9') { stride = 10 * stride + (d-'0'); sprintf(message, "Temporal resolution is %d", stride); MESSAGE(message); d = getchar(); } if (d == EOF) Quit(); if (stride <= 0) stride = 1; MESSAGE(""); Counter(counter); refresh(); break; default: break; } } Initialize() { /* Start universe with all cells in state 0, ant in center */ int x, y; runstat = STOP; logstat = 0; /* don't output log */ for (x = xmin; x <= xmax; x++) for (y = ymin; y <= ymax; y++) universe[x][y] = 0; /* reset universe */ Counter(0); /* reset counter */ time_sign = 1; /* forward time */ zoom = 1; /* no zoom-out */ folstat = 1; /* follow ant */ ant.x_coord = U_WIDTH / 2; ant.y_coord = U_DEPTH / 2; xtrap = ant.x_coord; /* default location of trap */ ytrap = ant.y_coord; ant.heading = 3; /* ant heads downward */ xmin = xmax = ant.x_coord; ymin = ymax = ant.y_coord; New_Window(ant.x_coord, ant.y_coord); /* w_left = U_WIDTH / 2 - W_WIDTH / 2; w_top = U_DEPTH / 2 - W_DEPTH / 2; */ w_right = w_left + W_WIDTH - 1; w_bottom = w_top + W_DEPTH - 1; /* make window centered */ sprintf(message, "w_left = %d, w_right = %d, w_top = %d, w_bottom = %d", w_left, w_right, w_top, w_bottom); Redraw(); } Redraw() { /* Redraw screen (window with frame) */ int a, b, i, j, x, y, max; for (a = -1; a <= W_WIDTH; a++) { MOVE(a, -1); addch('-'); MOVE(a, W_DEPTH); addch('-'); } for (b = 0; b <= W_DEPTH - 1; b++) { MOVE(-1, b); addch('|'); MOVE(W_WIDTH, b); addch('|'); } if (zoom > 1) { sprintf(message, "Magnification factor: %d", zoom); MOVE(W_WIDTH + 2, W_DEPTH / 2); addstr(message); } else { MOVE(W_WIDTH + 2, W_DEPTH / 2); clrtoeol(); } Counter(counter); for (a = 0; a < W_WIDTH; a++) { for (b = 0; b < W_DEPTH; b++) { max = 0; for (i = 0; i < zoom && max == 0; i++) for (j = 0; j < zoom && max == 0; j++) /***/ max = /***/ MAX(max, (int) universe[w_left + a * zoom + i][w_top + b * zoom + j]); MOVE(a, b); if (zoom == 1 || max == 0) addch(Sign(max)); else addch('*'); } } x = ant.x_coord; y = ant.y_coord; if (IN_WINDOW(x, y)) { a = (x - w_left) / zoom; b = (y - w_top ) / zoom; MOVE(a, b); /* put cursor on ant */ } else MOVE(W_WIDTH / 2 , W_DEPTH / 2); /* put cursor in middle of window */ refresh(); } Move_Cursor(c) /* Adjust position of cursor */ char c; { int row, col, a, b; if (zoom > 1) return; getyx(stdscr, row, col); a = col / 2 - 1; b = row - 1; switch (c) { case 'h': if (a >= 1) MOVE(a-1, b); else BEEP(); break; case 'l': if (a <= W_WIDTH-2) MOVE(a+1, b); else BEEP(); break; case 'j': if (b <= W_DEPTH-2) MOVE(a, b+1); else BEEP(); break; case 'k': if (b >= 1) MOVE(a, b-1); else BEEP(); break; } refresh(); } State(c) /* Change the state of the site at the cursor position */ char c; { int row, col, a, b, x, y; if (zoom > 1) return; getyx(stdscr, row, col); a = col / 2 - 1; b = row - 1; x = (w_left + a) % U_WIDTH; y = (w_top + b) % U_DEPTH; if (c >= '1' && c <= '9') universe[x][y] = c - '0'; else universe[x][y] = 0; MOVE(a, b); addch(c); MOVE(a, b); refresh(); if (x < xmin) xmin = x; if (x < xmax) xmax = x; if (y < ymin) ymin = y; if (y < ymax) ymax = y; } Trap() { /* Create a trap at the cursor position */ int row, col, a, b, x, y; if (zoom > 1) return; getyx(stdscr, row, col); a = col / 2 - 1; b = row - 1; x = (w_left + a) % U_WIDTH; y = (w_top + b) % U_DEPTH; xtrap = x; ytrap = y; } Ant(c) /* Put ant #n at the cursor position, with heading c */ char c; { int newx, newy, newh, a, b, row, col; if (zoom > 1) return; getyx(stdscr, row, col); a = col / 2 - 1; b = row - 1; newx = (w_left + a) % U_WIDTH; newy = (w_top + b) % U_DEPTH; /* get ant's new coordinates */ if (c == 'l') newh = 0; else if (c == 'k') newh = 1; else if (c == 'h') newh = 2; else if (c == 'j') newh = 3; else newh = 1; /* default direction is upward */ ant.x_coord = newx; ant.y_coord = newy; ant.heading = newh; } Describe() { /* Give coordinates of current cell */ int row, col, a, b, x, y; char c; if (zoom > 1) return; getyx(stdscr, row, col); a = col / 2 - 1; b = row - 1; x = (w_left + a) % U_WIDTH; y = (w_top + b) % U_DEPTH; if (universe[x][y] == 0) c = ' '; else c = '0' + universe[x][y]; sprintf(message, "(%3d,%3d) = '%c' [press any key to continue]", x, y, c); MESSAGE(message); CONFIRM(); if (ant.x_coord == x && ant.y_coord == y) { /* */ sprintf(message, "Ant heading %s [press any key to continue]", direction[ant.heading]); MESSAGE(message); CONFIRM(); } MESSAGE(""); Counter(counter); refresh(); } Zoom(n) /* Change zoom mode */ int n; { int xmid, ymid; if (n < 0 || n > 10) return; if (n * W_WIDTH > U_WIDTH || n * W_DEPTH > U_DEPTH) return; zoom = power[n]; xmid = (w_left + w_right) / 2; ymid = (w_top + w_bottom) / 2; if (folstat && ! (IN_WINDOW(ant.x_coord, ant.y_coord))) { xmid = ant.x_coord; ymid = ant.y_coord; } New_Window(xmid, ymid); Redraw(); } Center() { /* Move window so that cursor is at center */ int row, col, a, b, xmid, ymid; getyx(stdscr, row, col); a = col / 2 - 1; b = row - 1; xmid = (w_left + a * zoom) % U_WIDTH; ymid = (w_top + b * zoom) % U_DEPTH; fprintf(outfile, "Center: xmid=%d, ymid=%d\n", xmid, ymid); New_Window(xmid, ymid); MOVE(W_WIDTH/2, W_DEPTH/2); Redraw(); } Move_Window(c) /* Move window left, right, up, or down */ char c; { int xmid, ymid; xmid = ((w_left + w_right) / 2); ymid = ((w_top + w_bottom) / 2); if (c == CTRL_L) xmid = xmid + zoom * W_WIDTH; if (c == CTRL_H) xmid = xmid - zoom * W_WIDTH; if (c == CTRL_J) ymid = ymid + zoom * W_DEPTH; if (c == CTRL_K) ymid = ymid - zoom * W_DEPTH; New_Window(xmid, ymid); Redraw(); } New_Window(xmid, ymid) /* Move window */ int xmid, ymid; { w_left = xmid - (zoom * W_WIDTH) / 2; if (w_left < 0) w_left = 0; w_top = ymid - (zoom * W_DEPTH - 1) / 2; if (w_top < 0) w_top = 0; w_right = w_left + zoom * W_WIDTH - 1; if (w_right >= U_WIDTH) { w_right = U_WIDTH - 1; w_left = w_right - zoom * W_WIDTH + 1; } w_bottom = w_top + zoom * W_DEPTH - 1; if (w_bottom >= U_DEPTH) { w_bottom = U_DEPTH - 1; w_top = w_bottom - zoom * W_DEPTH + 1; } } Step() { /* Update universe 1 time step */ char cell; int a, b, x, y, max; int oldx, oldy, oldh, newx, newy; oldx = ant.x_coord; oldy = ant.y_coord; oldh = ant.heading; ant_old.x_coord = oldx; ant_old.y_coord = oldy; ant_old.heading = oldh; newx = oldx + motion[oldh][0]; newy = oldy + motion[oldh][1]; if (newx < 0 || newx >= U_WIDTH || newy < 0 || newy >= U_DEPTH) { Oops(); return; } cell = universe[newx][newy]; ant.x_coord = newx; ant.y_coord = newy; ant.heading = (4 + oldh + rule[cell]) % 4; if (newx < xmin) xmin = newx; if (newy < ymin) ymin = newy; if (newx > xmax) xmax = newx; if (newy > ymax) ymax = newy; Counter(counter + time_sign); universe[oldx][oldy] = (universe[oldx][oldy]+1) % numstates; /* increment state by 1 */ if (IN_WINDOW(oldx, oldy)) { a = (oldx - w_left) / zoom; b = (oldy - w_top ) / zoom; MOVE(a, b); if (zoom == 1) addch(Sign(universe[oldx][oldy])); else { max = 0; /* */ for (x = a*zoom + w_left; x < (a+1)*zoom + w_left && max == 0; x++) /* */ for (y = b*zoom + w_top; y < (b+1)*zoom + w_top && max == 0; y++) /* */ max = MAX(max, (int) universe[x][y]); if (zoom == 1 || max == 0) addch(Sign(max)); else addch('*'); } } if (counter == 0 && time_sign == -1) { runstat = STOP; } if (counter % stride == 0 || runstat == STOP) { if ( ! IN_WINDOW(newx,newy) && folstat) Center(); if ( IN_WINDOW(newx, newy)) { a = (newx - w_left) / zoom; b = (newy - w_top ) / zoom; MOVE(a, b); } else { a = W_WIDTH / 2; b = W_DEPTH / 2; MOVE(a, b); } refresh(); } if (logstat) { fprintf(outfile, "%c", '0'+cell); if (counter % 64 == 0) { fprintf(outfile, "\n"); fflush(outfile); } } return; } Oops() { /* ant has hit the edge */ fprintf(outfile, "ant has hit the edge at step %d!\n", counter); runstat = INTERRUPT; } Counter(t) /* set counter (but do not refresh screen) */ int t; { int row, col; counter = t; getyx(stdscr, row, col); sprintf(message, "Step %12d", counter); MOVE(W_WIDTH / 2, W_DEPTH + 1); addstr(message); move(row, col); } Go() { /* Step() until interrupted */ runstat = CONTINUE; while (runstat == CONTINUE) Step(); /* Redraw(); */ } Go_Trap() { /* Step() until trapped (or interrupted) */ runstat = CONTINUE; while (runstat == CONTINUE) { Step(); if (ant.x_coord == xtrap && ant.y_coord == ytrap) runstat = TRAP; } runstat = STOP; } Output_A() { /* Write state of universe to file "ant.out", ASCII-style */ int x, y; fprintf(outfile, "Step #%d\n", counter); fprintf(outfile, "xmin = %d, xmax = %d; ymin = %d, ymax = %d\n", xmin, xmax, ymin, ymax); fprintf(outfile, "Ant at (%3d,%3d), heading %s\n", ant.x_coord, ant.y_coord, direction[ant.heading]); for (x = xmin - 1; x <= xmax + 1; x++) fprintf(outfile, "-"); fprintf(outfile, "\n"); for (y = ymin; y <= ymax; y++) { fprintf(outfile, "|"); for (x = xmin; x <= xmax; x++) fprintf(outfile, "%c", Sign(universe[x][y])); fprintf(outfile, "|\n"); } for (x = xmin - 1; x <= xmax + 1; x++) fprintf(outfile, "-"); fprintf(outfile, "\n\n"); fflush(outfile); } Output_P() { /* Write state of universe to file "ant.out", Postscript-style */ int x, y; int rows, columns; FILE *PSfile; /* pointer to Postscript output file */ char filename[32]; rows = ymax-ymin+3; columns = xmax-xmin+3; sprintf(filename, "%d.%d.ps", rulecode, counter); PSfile = fopen(filename, "w"); /* open file for writing */ if (PSfile == NULL) { fprintf(outfile, "cannot open %s\n", filename); return; } fprintf(PSfile, "%%!\n"); fprintf(PSfile, "%%%%BoundingBox: 17 107 595 685\n"); fprintf(PSfile, "%% Step #%d\n", counter); fprintf(PSfile, "%% xmin = %d, xmax = %d; ymin = %d, ymax = %d\n", xmin, xmax, ymin, ymax); fprintf(PSfile, "%% Ant at (%3d,%3d), heading %s\n", ant.x_coord, ant.y_coord, direction[ant.heading]); fprintf(PSfile, "/inch { 72 mul } def\n"); fprintf(PSfile, "/boxpath {\n"); fprintf(PSfile, "\tnewx newy moveto\n"); fprintf(PSfile, "\tboxsize 0 rlineto\n"); fprintf(PSfile, "\t0 boxsize neg rlineto\n"); fprintf(PSfile, "\tboxsize neg 0 rlineto\n"); fprintf(PSfile, "\t0 boxsize rlineto\n"); fprintf(PSfile, "\tclosepath } def\n"); fprintf(PSfile, "/box {\n"); fprintf(PSfile, "\t%d exch sub %d div\n", numstates-1, numstates-1); fprintf(PSfile, "\tnewpath\n"); fprintf(PSfile, "\tboxpath\n"); fprintf(PSfile, "\tsetgray\n"); fprintf(PSfile, "\tfill\n"); fprintf(PSfile, "\tnewpath\n"); fprintf(PSfile, "\tboxpath\n"); fprintf(PSfile, "\t.0 setgray\n"); fprintf(PSfile, "\tstroke } def\n"); fprintf(PSfile, "/start {\n"); fprintf(PSfile, "\tboxsize 100 div setlinewidth\n"); fprintf(PSfile, "\t/newx leftedge def\n"); fprintf(PSfile, "\t/newy topedge def } def\n"); fprintf(PSfile, "/over {\n"); fprintf(PSfile, "\t/newx newx boxsize add def } def\n"); fprintf(PSfile, "/down {\n"); fprintf(PSfile, "\t/newx leftedge def\n"); fprintf(PSfile, "\t/newy newy boxsize sub def } def\n"); fprintf(PSfile, "/rows %d def\n", rows); fprintf(PSfile, "/columns %d def\n\n", columns); fprintf(PSfile, "/boxsize %f inch def\n", MIN(8.0/columns,10.0/rows)); fprintf(PSfile, "/pagewidth 8.5 inch def\n"); fprintf(PSfile, "/pageheight 11 inch def\n"); fprintf(PSfile, "/figwidth columns boxsize mul def\n"); fprintf(PSfile, "/figheight rows boxsize mul def\n"); fprintf(PSfile, "/leftedge pagewidth figwidth sub 2 div def\n"); fprintf(PSfile, "/topedge pageheight figheight sub 2 div "); fprintf(PSfile, "pageheight exch sub def\n"); fprintf(PSfile, "%% hack to fix L/R trouble\n"); fprintf(PSfile, "-1 1 scale\n"); fprintf(PSfile, "pagewidth neg 0 translate\n"); fprintf(PSfile, "\nstart\n\n"); for (y = ymin-1; y <= ymax+1; y++) { for (x = xmin-1; x <= xmax+1; x++) fprintf(PSfile, "%d box over\n", (int) universe[x][y]); fprintf(PSfile, "down\n\n"); } fprintf(PSfile, "\n"); fprintf(PSfile, "showpage\n"); fflush(PSfile); fclose(PSfile); } Output_T() { /* Write state of universe to file "ant.out", Postscript tiles*/ int x, y, i; int rows, columns; FILE *PSfile; /* pointer to Postscript output file */ FILE *header; char filename[32]; char str[256]; int ruleparity; rows = ymax-ymin+3; columns = xmax-xmin+3; sprintf(filename, "%d.%d-t.ps", rulecode, counter); PSfile = fopen(filename, "w"); /* open file for writing */ if (PSfile == NULL) { fprintf(outfile, "cannot open %s\n", filename); return; } #define PROLOGUE "ant.pro" if ((header = fopen(PROLOGUE, "r")) == NULL) { /* open prologue file */ fprintf(outfile, "cannot open %s\n", PROLOGUE); return; } fprintf(PSfile, "%%!\n"); fprintf(PSfile, "%%%%BoundingBox: 17 107 595 685\n"); fprintf(PSfile, "%% Step #%d\n", counter); fprintf(PSfile, "%% xmin = %d, xmax = %d; ymin = %d, ymax = %d\n", xmin, xmax, ymin, ymax); fprintf(PSfile, "%% Ant at (%3d,%3d), heading %s\n", ant_old.x_coord, ant_old.y_coord, direction[ant_old.heading]); fprintf(PSfile, "/antxloc %d def\n", ant_old.x_coord); fprintf(PSfile, "/antyloc %d def\n", ant_old.y_coord); fprintf(PSfile, "/antdirection %d def\n", ant_old.heading); fprintf(PSfile, "/rows %d def\n", rows); fprintf(PSfile, "/columns %d def\n", columns); fprintf(PSfile, "/xmin %d def\n", xmin-1); fprintf(PSfile, "/ymin %d def\n", ymin-1); for (ruleparity=i=0; i 0) fprintf(PSfile, "0 "); else fprintf(PSfile, "1 "); } fprintf(PSfile, "] def \n%%%%%%%%%% start of %s\n", PROLOGUE); while ( fgets(str, 255, header) != NULL ) fputs(str, PSfile); fclose(header); fprintf(PSfile, "\n%%%%%%%%%% end of %s\n\n", PROLOGUE); fprintf(PSfile, "/boxsize %f inch def\n", MIN(8.0/columns,10.0/rows)); /* fprintf(PSfile, "/pagewidth 8.5 inch def\n"); */ /* fprintf(PSfile, "/pageheight 11 inch def\n"); */ fprintf(PSfile, "/figwidth columns boxsize mul def\n"); fprintf(PSfile, "/figheight rows boxsize mul def\n"); fprintf(PSfile, "/leftedge pagewidth figwidth sub 2 div def\n"); fprintf(PSfile, "/topedge pageheight figheight sub 2 div "); fprintf(PSfile, "pageheight exch sub def\n"); fprintf(PSfile, "\nstart\n\n"); for (y = ymin-1; y <= ymax+1; y++) { for (x = xmin-1; x <= xmax+1; x++) fprintf(PSfile, "%d box over\n", (int) universe[x][y]); fprintf(PSfile, "down\n\n"); } fprintf(PSfile, "\n"); fprintf(PSfile, "drawant\n"); fprintf(PSfile, "\n"); fprintf(PSfile, "showpage\n"); fflush(PSfile); fclose(PSfile); } char Sign(x) /* Return symbolic form of cell-state */ char x; { if (x == 0) return(' '); return('0'+x); } Quit() { /* Quit */ fclose(outfile); move(LAST_ROW, 0); refresh(); endwin(); exit(0); } Interrupt() { runstat = INTERRUPT; /* note the occurrence of an interrupt */ signal(SIGINT, Interrupt); /* catch future interrupts! */ return; } /* DIAGNOSTIC PROGRAMS */ MESSAGE(string) /* post message */ char *string; { int row, col; getyx(stdscr, row, col); move(LAST_ROW, 0); clrtoeol(); addstr(string); move(row, col); refresh(); } CONFIRM() { /* confirm receipt of message */ char c; int row, col; getyx(stdscr, row, col); c = getchar(); if (c == EOF) Quit(); move(row, col); refresh(); return; } BEEP() { sprintf(message, ""); MESSAGE(message); sprintf(message, "%c", BEL); MESSAGE(message); }