Path: utzoo!attcan!utgpu!watmath!uunet!ginosko!gem.mps.ohio-state.edu!tut.cis.ohio-state.edu!mstar!zip!karl From: karl@zip.UUCP (Karl F. Fox) Newsgroups: unix-pc.sources Subject: Life game for the UNIX PC Message-ID: <1989Oct25.151053.3963@zip.UUCP> Date: 25 Oct 89 15:10:53 GMT Organization: Zip. Lines: 1465 Here's another graphics game for the UNIX PC. Please send me bug reports, bug fixes, interesting new patterns, and suggestions for improvements. Note: This code is designed to be fast, not small. Look: % size /usr/games/life 7432(.text) + 1148(.data) + 518884(.bss) + 0(.lib) = 527464 My apologies if you only have a .5M machine. #!/bin/sh # # This is a SHAR archive. To extract, remove everything # above the #!/bin/sh line and type "sh filename" # if `test ! -s ./README` then echo "Writing ./README" sed 's/^X//' > ./README << '\End\of\File\' XThis is John Horton Conway's game of life for the AT&T UNIX PC. X X XHow to make it: X XType "make". X X XHow to use it: X XJust run it. There are no arguments. Stop it by giving it a SIGINT or XSIGTERM or by typing any key (except a digit) to the display window. XTyping digit n will set the time to sleep between generations to n/10 of Xa second. X XPressing the middle mouse button freezes or resumes animation, and Xpressing the right mouse button kills all the cells. When the left Xmouse button is pressed *down* the cursor changes from a tiny dot X(which, by the way, will disappear completely if places in the far upper Xright corner of the screen) to a life cell, which can be placed on the Xscreen by releasing the button. Depositing the new cell on an old cell Xkills it. X XWhen the arena gets into a stable configuration (all dead, completely Xstatic or a pattern repeating every 63 generations or less), a glider or Xspaceship will be injected to stir up the action. After a few tries at Xthis, the screen will be cleared and a random starting pattern will be Xselected from the file /usr/local/lib/life-patterns. Note that the cell Xarena extends a ways beyond the boundaries of the visible screen, so an Xapparently static configuration may be active off-screen. X XPlease send me any interesting new startup patterns that you discover X(karl@MorningStar.Com or karl@zip.UUCP). X XHave fun! X XP.S. Send me email if you want my xlock driver. X X@(#)README 1.1 \End\of\File\ else echo "Not overwriting ./README" fi if `test ! -s ./Makefile` then echo "Writing ./Makefile" sed 's/^X//' > ./Makefile << '\End\of\File\' X# @(#)Makefile 1.1 X X# X# Makefile for the game of life X# X# karl@MorningStar.Com or karl@zip.UUCP X# X XLDFLAGS = /lib/crt0s.o /lib/shlib.ifile X Xlife: life.o X $(LD) -o $@ life.o $(LDFLAGS) X Xlife.o: life.c lifegame.c X $(CC) $(CFLAGS) -c life.c X Xclean: X rm -f life *.o core Xlint: X lint -p life.c X XREADME: s.README X $(GET) $(GFLAGS) -p s.README > $@ X Xlife.shar: README Makefile life.c lifegame.c X -shar README Makefile life.c lifegame.c > life.shar \End\of\File\ else echo "Not overwriting ./Makefile" fi if `test ! -s ./life.c` then echo "Writing ./life.c" sed 's/^X//' > ./life.c << '\End\of\File\' X/* X * Copyright (c) 1989 Karl F. Fox, all rights reserved X * X * You may use this software for any purpose as long as you include X * this copyright notice. X */ X X# ifndef lint Xstatic char life_sccs_id[] = "@(#)life.c 1.1"; X# endif /* lint */ X X/* X * life.c X * X * John Horton Conway's game of life for the AT&T UNIX PC. X * X * X * How to use it: X * X * X * Just run it. There are no arguments. Stop it by giving it a X * SIGINT or SIGTERM or by typing any key (except a digit) to the X * display window. Typing digit n will set the time to sleep X * between generations to n/10 of a second. X * X * Pressing the middle mouse button freezes or resumes animation, X * and pressing the right mouse button kills all the cells. When X * the left mouse button is pressed *down* the cursor changes from X * a tiny dot (which, by the way, will disappear completely if X * places in the far upper right corner of the screen) to a life X * cell, which can be placed on the screen by releasing the button. X * Depositing the new cell on an old cell kills it. X * X * When the arena gets into a stable configuration (all dead, X * completely static or a pattern repeating every 63 generations or X * less), a glider or spaceship will be injected to stir up the X * action. After a few tries at this, the screen will be cleared X * and a random starting pattern will be selected from the file X * /usr/local/lib/life-patterns. Note that the cell arena extends X * a ways beyond the boundaries of the visible screen, so an X * apparently static configuration may be active off-screen. X * X * Please send me any interesting new startup patterns that you X * discover (karl@MorningStar.Com or karl@zip.UUCP). X * X * Have fun! X */ X X# include X# include X# include X# include X# include X# include X# include X# include X X# define K_OK 0 X# define K_OTHER 1 X# define K_EXIT 2 X X# define ESC '\033' X X/* X * Mouse event string character classes X */ X# define ESCAPE 0 X# define SEMI 1 X# define M 2 X# define O 3 X# define DIGIT 4 X# define DEFAULT 5 X Xint mouse_state = 0, X mouse_x = 0, X mouse_y = 0, X mouse_buttons = 0, X mouse_reason = 0; X Xstruct termio orig_tm; X X# define PATTERN_FILE "/usr/local/lib/life-patterns" X X# define SYSV X X# ifdef SYSV X# define srandom(seed) srand((unsigned)(seed)) X# define random() rand() X# define bcmp(a,b,c) memcmp((char *)(b), (char *)(a), (int)(c)) X# define bzero(a,c) memset((char *)(a), 0, (int)(c)) X# endif /* SYSV */ X Xstatic int width, height, xs, ys, xb, yb, xp, yp; X X# define NROWS 128 X# define NCOLS 256 X X# define XS1 4 X# define YS1 3 X# define NR1 64 X# define NC1 64 X# define XS2 1 X# define YS2 1 X Xextern int open(), close(), read(), write(), ioctl(), rand(), memcmp(), X fprintf(), fcntl(); Xextern void srand(), exit(), perror(); Xextern unsigned sleep(); Xextern long time(); X Xint wd; X Xvoid main() X { X extern void process(); X X if ((wd = open("/dev/window", O_RDWR)) < 0) X { X (void)perror("Can't open '/dev/window'\7"); X (void)exit(1); X } X X process(); X (void)close(wd); X (void)exit(0); X /*NOTREACHED*/ X } X Xvoid terminate() X { X extern void cursor_visible(), mouse_off(), term_reset(); X X static int in_terminate = 0; X X if ( ! in_terminate) X { X ++in_terminate; X cursor_visible(); X mouse_off(); X term_reset(); X (void)close(wd); X (void)exit(1); X } X } X Xvoid cursor_invisible() X { X if (write(wd, "\033[=1C", 5) != 5) X { X (void)perror("Can't set cursor invisibility\7"); X terminate(); X } X } X Xvoid cursor_visible() X { X if (write(wd, "\033[=0C", 5) != 5) X { X (void)perror("Can't restore cursor visibility\7"); X terminate(); X } X } X X# define WIDTH 720 X# define HEIGHT 300 X Xint running = 1, delay = 0; X Xvoid process() X { X extern void mouse_on(), term_cbreak(); X extern int set_window_parameters(); X X if (set_window_parameters(0, 0, WIDTH, HEIGHT, NBORDER) < 0) X return; X X term_cbreak(0, 0); X mouse_on(1, 1, WIDTH, HEIGHT); X (void)signal(SIGTERM, (int (*)())terminate); X (void)signal(SIGINT, (int (*)())terminate); X X cursor_invisible(); X X for (;;) X { X extern void initlife(), draw_life_game(); X extern int life_game_done(); X X initlife(WIDTH, HEIGHT); X X do X { X register int i, n; X char buffer[64]; X X do X { X extern int do_key(); X X if ((n = read(wd, buffer, sizeof buffer)) > 0) X for (i = 0; i < n; ++i) X switch (do_key(buffer[i])) X { X case K_OK: X break; X X case K_OTHER: X case K_EXIT: X terminate(); X } X } X while ( ! running); X X X draw_life_game(); X } X while ( ! life_game_done()); X } X X /*NOTREACHED*/ X } X Xint set_window_parameters(x, y, w, h, uflags) Xregister int x, y, w, h, uflags; X { X struct uwdata uwdata; X struct utdata utdata; X X if (ioctl(wd, WIOCGETD, &uwdata) < 0) X { X (void)perror("Can't perform WIOCGETD\7"); X return (-1); X } X X uwdata.uw_x = x; X uwdata.uw_y = y; X uwdata.uw_width = w; X uwdata.uw_height = h; X uwdata.uw_uflags = uflags; X X if (ioctl(wd, WIOCSETD, &uwdata) < 0) X { X (void)perror("Can't perform WIOCSETD\7"); X return (-1); X } X X (void)strncpy(utdata.ut_text, "life", WTXTLEN); X utdata.ut_num = WTXTUSER; X X if (ioctl(wd, WIOCSETTEXT, &utdata) < 0) X { X (void)perror("Can't perform WIOCSETTEXT\7"); X return (-1); X } X X return (0); X } X Xint window_rastop(srcbase, srcwidth, dstbase, dstwidth, srcx, srcy, dstx, X dsty, bltwidth, bltheight, srcop, dstop, pattern) Xunsigned short *srcbase, srcwidth, *dstbase, dstwidth, srcx, srcy, dstx, X dsty, bltwidth, bltheight, *pattern; Xchar srcop, dstop; X { X struct urdata urdata; X X urdata.ur_srcbase = srcbase; X urdata.ur_srcwidth = srcwidth; X urdata.ur_dstbase = dstbase; X urdata.ur_dstwidth = dstwidth; X urdata.ur_srcx = srcx; X urdata.ur_srcy = srcy; X urdata.ur_dstx = dstx; X urdata.ur_dsty = dsty; X urdata.ur_width = bltwidth; X urdata.ur_height = bltheight; X urdata.ur_srcop = srcop; X urdata.ur_dstop = dstop; X urdata.ur_pattern = pattern; X X if (ioctl(wd, WIOCRASTOP, &urdata) < 0) X { X (void)perror("Can't perform WIOCRASTOP\7"); X return (-1); X } X X return (0); X } X X# define DISPLAY_WIDTH ((WIDTH + 7) / 8) X Xunsigned short display[DISPLAY_WIDTH/2 * HEIGHT]; X Xunsigned short white_pattern[] = X { X 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, X 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF X }; X Xunsigned short black_pattern[] = X { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; X Xvoid draw_rectangle(x, y, xsize, ysize) Xint x, y, xsize, ysize; X { X register unsigned short *s = &display[(y * DISPLAY_WIDTH/2) + (x >> 4)]; X X if (xsize == 3 && ysize == 2) X switch (x & 15) X { X case 0: *s |= 0x0007; s += DISPLAY_WIDTH/2; X *s |= 0x0007; X return; X case 4: *s |= 0x0070; s += DISPLAY_WIDTH/2; X *s |= 0x0070; X return; X case 8: *s |= 0x0700; s += DISPLAY_WIDTH/2; X *s |= 0x0700; X return; X case 12: *s |= 0x7000; s += DISPLAY_WIDTH/2; X *s |= 0x7000; X return; X } X X if (window_rastop(0, 0, display, DISPLAY_WIDTH, 0, 0, (unsigned short)x, X (unsigned short)y, (unsigned short)xsize, (unsigned short)ysize, X SRCPAT, DSTSRC, white_pattern) < 0) X (void)fprintf(stderr, X "window_rastop() failed in draw_rectangle(%d, %d, %d, %d)\n", X x, y, xsize, ysize); X } X Xvoid erase_rectangle(x, y, xsize, ysize) Xint x, y, xsize, ysize; X { X register unsigned short *s = &display[(y * DISPLAY_WIDTH/2) + (x >> 4)]; X X if (xsize == 4 && ysize == 3) X switch (x & 15) X { X case 0: *s &= ~0x000F; s += DISPLAY_WIDTH/2; X *s &= ~0x000F; s += DISPLAY_WIDTH/2; X *s &= ~0x000F; X return; X case 4: *s &= ~0x00F0; s += DISPLAY_WIDTH/2; X *s &= ~0x00F0; s += DISPLAY_WIDTH/2; X *s &= ~0x00F0; X return; X case 8: *s &= ~0x0F00; s += DISPLAY_WIDTH/2; X *s &= ~0x0F00; s += DISPLAY_WIDTH/2; X *s &= ~0x0F00; X return; X case 12: *s &= ~0xF000; s += DISPLAY_WIDTH/2; X *s &= ~0xF000; s += DISPLAY_WIDTH/2; X *s &= ~0xF000; X return; X } X X if (window_rastop(0, 0, display, DISPLAY_WIDTH, 0, 0, (unsigned short)x, X (unsigned short)y, (unsigned short)xsize, (unsigned short)ysize, X SRCPAT, DSTSRC, black_pattern) < 0) X (void)fprintf(stderr, X "window_rastop() failed in erase_rectangle(%d, %d, %d, %d)\n", X x, y, xsize, ysize); X } X Xvoid update_display() X { X if (window_rastop(display, DISPLAY_WIDTH, 0, 0, 0, 0, 0, 0, X (unsigned short)width, (unsigned short)height, X SRCSRC, DSTSRC, 0) < 0) X (void)fprintf(stderr, X "window_rastop() failed in update_display()\n"); X } X X/*ARGSUSED*/ Xstatic int new_color(color) Xint color; X { X return (0); X } X X/*ARGSUSED*/ Xstatic void draw(row, col, age) Xint row, col, age; X { X draw_rectangle(xb + xs * col, yb + ys * row, xp, yp); X } X Xstatic void erase(row, col) Xint row, col; X { X erase_rectangle(xb + xs * col, yb + ys * row, xs, ys); X } X Xvoid initlife(w, h) Xint w, h; X { X extern void init_life_game(); X X int rows, cols; X X width = w; X height = h; X X erase_rectangle(0, 0, width, height); X X xs = XS1; X ys = YS1; X X if (width / xs < NC1 || height / ys < NR1) X { X xs = XS2; X ys = YS2; X } X X cols = width / xs; X rows = height / ys; X X if (xs <= 2 || ys <= 2) X xp = xs, yp = ys; X else if (xs <= 4 || ys <= 4) X xp = xs - 1, yp = ys - 1; X else X xp = xs - 2, yp = ys - 2; X X xb = (width - xs * cols) / 2; X yb = (height - ys * rows) / 2; X X init_life_game(rows, cols, 0); X } X Xvoid term_cbreak(vmin, vtime) Xint vmin, vtime; X { X static int initialized = 0; X struct termio tm; X X if ( ! initialized) X { X if (ioctl(wd, TCGETA, &orig_tm) < 0) X { X perror("Can't get terminal characteristics\7"); X terminate(); X } X X ++initialized; X } X X tm = orig_tm; X X tm.c_cc[VMIN] = vmin; X tm.c_cc[VTIME] = vtime; X tm.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL); X X if (ioctl(wd, TCSETA, &tm) < 0) X { X perror("Can't set terminal characteristics\7"); X terminate(); X } X } X Xvoid term_reset() X { X if (ioctl(wd, TCSETA, &orig_tm) < 0) X { X perror("Can't reset terminal characteristics\7"); X terminate(); X } X } X X/* X * Life cell cursor: X * X * *** X * *** X */ Xstruct icon cell_icon = X { X 0, /* ic_flags (ignored) */ X X { /* struct fcdef ic_fc */ X 3, /* fc_hs */ X 2, /* fc_vs */ X -2, /* fc_ha */ X -1, /* fc_va */ X 0, /* fc_hi */ X 0, /* fc_vi */ X 0 /* fc_mr */ X }, X X { /* unsigned short ic_raster[64] */ X 7, X 7 X } X }; X Xstruct icon tiny_icon = X { X 0, /* ic_flags (ignored) */ X X { /* struct fcdef ic_fc */ X 1, /* fc_hs */ X 1, /* fc_vs */ X -1, /* fc_ha */ X -1, /* fc_va */ X 0, /* fc_hi */ X 0, /* fc_vi */ X 0 /* fc_mr */ X }, X X { /* unsigned short ic_raster[64] */ X 1 X } X }; X Xstruct umdata umdata; X Xvoid mouse_cursor(cursor) Xstruct icon *cursor; X { X umdata.um_icon = cursor; X X if (ioctl(wd, WIOCSETMOUSE, &umdata) < 0) X { X perror("Can't set mouse characteristics\7"); X terminate(); X } X } X Xvoid mouse_on(x, y, w, h) Xregister int x, y, w, h; X { X umdata.um_flags = MSUP | MSDOWN; X umdata.um_x = x; X umdata.um_y = y; X umdata.um_w = w; X umdata.um_h = h; X mouse_cursor(&tiny_icon); X } X Xvoid mouse_off() X { X umdata.um_flags = 0; X mouse_cursor((struct icon *)0); X } X X/*ARGSUSED*/ Xint m_null(c) Xchar c; X { X return (K_OK); X } X X/*ARGSUSED*/ Xint m_err(c) Xchar c; X { X mouse_state = 0; X return (K_OTHER); X } X X/*ARGSUSED*/ Xint m_init(c) Xchar c; X { X mouse_x = mouse_y = mouse_buttons = mouse_reason = 0; X mouse_state = 1; X return (K_OK); X } X Xint m_digit(c) Xchar c; X { X term_cbreak( ! running, delay = c - '0'); X mouse_state = 0; X return (K_OK); X } X Xint m_x(c) Xchar c; X { X mouse_x = (mouse_x * 10) + c - '0'; X return (K_OK); X } X Xint m_y(c) Xchar c; X { X mouse_y = (mouse_y * 10) + c - '0'; X return (K_OK); X } X Xint m_buttons(c) Xchar c; X { X mouse_buttons = (mouse_buttons * 10) + c - '0'; X return (K_OK); X } X Xint m_reason(c) Xchar c; X { X mouse_reason = (mouse_reason * 10) + c - '0'; X return (K_OK); X } X Xint m_key(c) Xchar c; X { X switch (c) X { X case 'w': case 'W': /* Exit key or close box selection */ X return (K_EXIT); X X default: X return (K_OTHER); X } X } X X/*ARGSUSED*/ Xint m_2(c) Xchar c; X { X mouse_state = 2; X return (K_OK); X } X X/*ARGSUSED*/ Xint m_3(c) Xchar c; X { X mouse_state = 3; X return (K_OK); X } X X/*ARGSUSED*/ Xint m_4(c) Xchar c; X { X mouse_state = 4; X return (K_OK); X } X X/*ARGSUSED*/ Xint m_5(c) Xchar c; X { X mouse_state = 5; X return (K_OK); X } X X# define LEFT_BUTTON 4 X# define MIDDLE_BUTTON 2 X# define RIGHT_BUTTON 1 X X/*ARGSUSED*/ Xint m_push(c) Xchar c; X { X extern void reversecell(), clear_the_board(); X X static int old_mouse_buttons = 0; X X if (mouse_reason & MSDOWN) X { X if (mouse_buttons & LEFT_BUTTON) X mouse_cursor(&cell_icon); X X if (mouse_buttons & MIDDLE_BUTTON) X { X running = ! running; X term_cbreak( ! running, delay); X } X X if (mouse_buttons & RIGHT_BUTTON) X clear_the_board(); X } X X if (mouse_reason & MSUP) X if (old_mouse_buttons & LEFT_BUTTON) X { X reversecell((mouse_y - yb) / ys, (mouse_x - xb) / xs); X mouse_cursor(&tiny_icon); X } X X old_mouse_buttons = mouse_buttons; X mouse_state = 0; X return (K_OK); X } X Xint (*(mouse_action[][6]))() = X { X/* State ESCAPE SEMI M O DIGIT DEFAULT */ X/* 0 */ { m_init, m_err, m_err, m_err, m_digit, m_err }, X/* 1 */ { m_err, m_2, m_err, m_5, m_x, m_null }, X/* 2 */ { m_err, m_3, m_err, m_err, m_y, m_err }, X/* 3 */ { m_err, m_4, m_err, m_err, m_buttons, m_err }, X/* 4 */ { m_err, m_err, m_push, m_err, m_reason, m_err }, X/* 5 */ { m_err, m_err, m_err, m_err, m_err, m_key }, X }; X Xint do_key(c) Xregister char c; X { X register int class; X register int (*action)(); X X/* # define ECHO_INPUT */ X# ifdef ECHO_INPUT X fprintf(stderr, c == ESC ? "" : "%c", c); X# endif /* ECHO_INPUT */ X X switch (c) X { X case ESC: class = ESCAPE; break; X case ';': class = SEMI; break; X case 'M': class = M; break; X case 'O': class = O; break; X case '0': case '1': case '2': case '3': case '4': X case '5': case '6': case '7': case '8': case '9': X class = DIGIT; break; X default: class = DEFAULT; X } X X action = mouse_action[mouse_state][class]; X return ((*action)(c)); X } X X# include "lifegame.c" X Xvoid reversecell(row, col) Xint row, col; X { X register int index; X X row += firstrow; X col += firstcol; X index = row * NCOLS + col; X X /* X * Draw/erase a life cell X */ X if (alive[index]) X death(row, col); X else X birth(row, col); X X update_display(); X } X Xvoid clear_the_board() X { X (void)bzero(neighbors, sizeof(neighbors)); X (void)bzero(alive, sizeof(alive)); X (void)bzero(ages, sizeof(ages)); X (void)bzero(links, sizeof(links)); X head = links; X head -> l_next = 0; X erase_rectangle(0, 0, width, height); X births = deaths = cells = 0; X update_display(); X } \End\of\File\ else echo "Not overwriting ./life.c" fi if `test ! -s ./lifegame.c` then echo "Writing ./lifegame.c" sed 's/^X//' > ./lifegame.c << '\End\of\File\' X/* X * Copyright (c) 1989 Karl F. Fox, all rights reserved X * X * You may use this software for any purpose as long as you include X * this copyright notice. X */ X X# ifndef lint Xstatic char lifegame_sccs_id[] = "@(#)lifegame.c 1.1"; X# endif /* lint */ X X/* X * lifegame.c X * X * Machine-independent (more or less) implementation of John Horton X * Conway's game of life. X * X * X * How to use it: X * X * Include this file (# include "lifegame.c") from a driver program. X * The driver should call init_life_game(rows, cols, color) once and X * then call draw_life_game() followed by life_game_done() until X * the latter returns non-zero. The including file should #define X * NROWS and NCOLS (the maximum size of the cell grid), both of X * which should be powers of two unless you don't mind glacial-speed X * animation. The driver code should supply the following routines: X * X * draw(row, col, color) Display a cell. Color X * is ignored if init_life_game() X * was passed a zero color flag. X * erase(row, col) Erase a cell from the display. X * new_color(color) When using color, return a new X * color if aging cells should X * gradually color-shift. X * update_display() Flush changes to the display. X * X * Also, # define the following if you're not on a BSD system: X * X * # define srandom(seed) srand(seed) X * # define random() rand() X * # define bcmp(a,b,c) memcmp((b),(a),(c)) X * # define bzero(a,c) memset((a),0,(c)) X */ X X# include X Xstatic int n_patterns, births = 0, deaths = 0, cells = 0, shot_time = 0, X shots = 0, use_color, nrows, ncols, firstrow, firstcol, lastrow, X lastcol; X Xtypedef struct link X { X struct link *l_next; X } link_t; X Xstatic link_t links[NROWS * NCOLS], *head; Xstatic unsigned char neighbors[NROWS * NCOLS], alive[NROWS * NCOLS], X ages[NROWS * NCOLS]; X Xtypedef struct X { X enum X { X c_age, X c_birth, X c_death X } c_op_code; X X union X { X int cu_index; X unsigned short cu_coord[2]; X } c_un; X# define c_index c_un.cu_index X# define c_row c_un.cu_coord[0] X# define c_col c_un.cu_coord[1] X } change_t; X Xstatic change_t changevec[NROWS * NCOLS]; X X# define HISTORY_SIZE 128 X Xstatic short history[HISTORY_SIZE], X *historyp = history, X *history_end = &history[HISTORY_SIZE]; X X# define DRAW(row,col,age) if (row >= firstrow && row <= lastrow \ X && col >= firstcol && col <= lastcol) \ X draw(row - firstrow, col - firstcol, age) X X# define ERASE(row,col) if (row >= firstrow && row <= lastrow \ X && col >= firstcol && col <= lastcol) \ X erase(row - firstrow, col - firstcol) X X# define ENQUEUE(lp) if ((lp) -> l_next == 0) \ X ((lp) -> l_next = head, \ X head = (lp)) X X# define NB_ALIVE(np, lp) if (++*(np) == 3 || *(np) == 4) \ X ENQUEUE(lp) X X# define NB_DEAD(np, lp) if (--*(np) == 3 || *(np) == 1) \ X ENQUEUE(lp) X Xstatic void birth(row, col) Xint row, col; X { X register int index = row * NCOLS + col; X register unsigned char *np; X register link_t *lp; X X alive[index] = 1; X ++cells; X ++births; X ages[index] = 0; X DRAW(row, col, 0); X X np = neighbors + index - NCOLS - 1; lp = links + index - NCOLS - 1; X NB_ALIVE(np, lp); X X ++np; ++lp; X NB_ALIVE(np, lp); X X ++np; ++lp; X NB_ALIVE(np, lp); X X np += NCOLS - 2; lp += NCOLS - 2; X NB_ALIVE(np, lp); X X ENQUEUE(lp + 1); /* This line is only needed for patterns or */ X /* shooting gliders or spaceships */ X X np += 2; lp += 2; X NB_ALIVE(np, lp); X X np += NCOLS - 2; lp += NCOLS - 2; X NB_ALIVE(np, lp); X X ++np; ++lp; X NB_ALIVE(np, lp); X X ++np; ++lp; X NB_ALIVE(np, lp); X } X Xstatic void death(row, col) Xint row, col; X { X register int index = row * NCOLS + col; X register unsigned char *np; X register link_t *lp; X X alive[index] = 0; X --cells; X ++deaths; X ERASE(row, col); X X np = neighbors + index - NCOLS - 1; lp = links + index - NCOLS - 1; X NB_DEAD(np, lp); X X ++np; ++lp; X NB_DEAD(np, lp); X X ++np; ++lp; X NB_DEAD(np, lp); X X np += NCOLS - 2; lp += NCOLS - 2; X NB_DEAD(np, lp); X X ENQUEUE(lp + 1); /* This line is only needed for patterns or */ X /* shooting gliders or spaceships */ X X np += 2; lp += 2; X NB_DEAD(np, lp); X X np += NCOLS - 2; lp += NCOLS - 2; X NB_DEAD(np, lp); X X ++np; ++lp; X NB_DEAD(np, lp); X X ++np; ++lp; X NB_DEAD(np, lp); X } X Xvoid draw_life_game() X { X register link_t *lp = head; X register change_t *cp = changevec, *endp; X X births = deaths = 0; X X while (lp) X { X register int index = lp - links; X register int row = index / NCOLS, col = index % NCOLS; X X link_t *ltmp; X X ltmp = lp; X lp = lp -> l_next; X ltmp -> l_next = 0; X X if (row > 0 && row < NROWS - 1 && col > 0 && col < NCOLS - 1) X switch (neighbors[index]) X { X case 2: /* 2 neighbors -- survives if alive */ X if (alive[index]) X { X register unsigned char *ageptr = ages + index, age; X X if (use_color X && (age = new_color((int)*ageptr)) != *ageptr) X { X *ageptr = age; X DRAW(row, col, (int)age); X cp -> c_op_code = c_age; X cp -> c_index = index; X ++cp; X } X } X X break; X X case 3: /* 3 neighbors -- birth if none yet */ X if (alive[index]) X { X register unsigned char *ageptr = ages + index, age; X X if (use_color X && (age = new_color((int)*ageptr)) != *ageptr) X { X *ageptr = age; X DRAW(row, col, (int)age); X cp -> c_op_code = c_age; X cp -> c_index = index; X ++cp; X } X } X else X { X cp -> c_op_code = c_birth; X cp -> c_row = row; X cp -> c_col = col; X ++cp; X } X X break; X X default: /* anything else -- cell dies */ X if (alive[index]) X { X cp -> c_op_code = c_death; X cp -> c_row = row; X cp -> c_col = col; X ++cp; X } X } X } X X head = links; X head -> l_next = 0; X X for (endp = cp, cp = changevec; cp < endp; ++cp) X switch (cp -> c_op_code) X { X case c_birth: birth((int)cp -> c_row, (int)cp -> c_col); break; X case c_death: death((int)cp -> c_row, (int)cp -> c_col); break; X case c_age: ENQUEUE(links + cp -> c_index); X } X X update_display(); X } X Xstatic void read_pattern(patternp) Xint *patternp; X { X extern int fclose(); X X register FILE *fp; X register int c; X register change_t *cp = changevec; X int pattern = 0, in_comment = 0, in_pattern = 0; X unsigned short row, col; X X if ((fp = fopen(PATTERN_FILE, "r")) == NULL) X { X *patternp = 0; X return; X } X X while ((c = getc(fp)) != EOF) X if (in_comment) X { X if (c == '\n') X in_comment = 0; X } X else X { X if ( ! in_pattern) X switch (c) X { X case '#': ++in_comment; continue; X case '\n': continue; X default: ++in_pattern; row = col = 1; X } X X switch (c) X { X case ' ': X ++col; X break; X X case '\t': X col = ((col + 7) & ~7) + 1; X break; X X case '\n': X col = 1; X ++row; X break; X X case '#': X if (*patternp == pattern) X { X cp -> c_op_code = c_death; X (void)fclose(fp); X return; X } X X in_pattern = 0; X ++in_comment; X ++pattern; X break; X X default: X if (*patternp == pattern) X { X cp -> c_op_code = c_birth; X cp -> c_row = row; X cp -> c_col = col; X ++cp; X } X X ++col; X } X } X X if (in_pattern) X if (*patternp == pattern) X { X cp -> c_op_code = c_death; X (void)fclose(fp); X return; X } X else X ++pattern; X X *patternp = pattern; X (void)fclose(fp); X } X Xstatic void count_patterns() X { X n_patterns = 10000; X read_pattern(&n_patterns); X } X Xvoid init_life_game(rows, cols, color) Xint rows, cols, color; X { X static int first_time = 1; X register change_t *cp; X int row, col, pattern, maxrow, maxcol, minrow, mincol, X row_offset, col_offset; X X nrows = rows; X ncols = cols; X use_color = color; X X if (first_time) X { X srandom(time((long *)0)); X count_patterns(); X first_time = 0; X } X X births = deaths = cells = shot_time = shots = 0; X historyp = history; X X firstrow = (NROWS - nrows) / 2; X firstcol = (NCOLS - ncols) / 2; X lastrow = firstrow + nrows - 1; X lastcol = firstcol + ncols - 1; X X (void)bzero(neighbors, sizeof(neighbors)); X (void)bzero(alive, sizeof(alive)); X (void)bzero(ages, sizeof(ages)); X (void)bzero(links, sizeof(links)); X X head = links; X head -> l_next = 0; X X pattern = random() % n_patterns; X read_pattern(&pattern); X X maxrow = maxcol = 0; X minrow = mincol = 10000; X X for (cp = changevec; cp -> c_op_code == c_birth; ++cp) X { X if (cp -> c_row > maxrow) maxrow = cp -> c_row; X if (cp -> c_col > maxcol) maxcol = cp -> c_col; X if (cp -> c_row < minrow) minrow = cp -> c_row; X if (cp -> c_col < mincol) mincol = cp -> c_col; X } X X row_offset = firstrow + nrows / 2 - minrow - (maxrow - minrow + 1) / 2; X col_offset = firstcol + ncols / 2 - mincol - (maxcol - mincol + 1) / 2; X X for (cp = changevec; cp -> c_op_code == c_birth; ++cp) X { X row = cp -> c_row + row_offset; X col = cp -> c_col + col_offset; X X if (row > 0 && row < NROWS - 1 && col > 0 && col < NCOLS - 1) X birth(row, col); X } X X update_display(); X (void)sleep(1); X } X Xstatic void find_target(rowp, colp) Xint *rowp, *colp; X { X register int row, col; X X for (row = firstrow; row <= lastrow; ++row) X for (col = firstcol; col <= lastcol; ++col) X if (alive[row * NCOLS + col]) X { X *rowp = row; X *colp = col; X X /* X * Don't always just pick the topmost cell. X * Look around a little bit. X */ X if ((random() & 15) == 0) X return; X } X } X Xint life_game_done() X { X int cycle = 0; X X if (cells == 0) X return (1); X X /* X * Quick and dirty way to detect cycles. Sometimes a bit hasty. X */ X *historyp++ = (births << 8) | (deaths & 0xFF); X X /* X * Check for cycles every HISTORY_SIZE generations X */ X if (historyp >= history_end) X { X register unsigned short cycle_size; X X for (cycle_size = 1; X ! cycle && cycle_size < HISTORY_SIZE / 2; X ++cycle_size) X { X register int n_cycles = HISTORY_SIZE / cycle_size, i, no_cycle = 0; X X if (n_cycles > 10) X n_cycles = 10; X X for (i = 2; ! no_cycle && i <= n_cycles; ++i) X if (bcmp(historyp - cycle_size, X historyp - (cycle_size * i), X cycle_size * sizeof *historyp) != 0) X no_cycle = 1; X X if ( ! no_cycle) X cycle = cycle_size; X } X X historyp = history; X } X X if (births == 0 && deaths == 0) X cycle = 1; X X if (shot_time) X --shot_time; X X if (shot_time == 0 && cycle) X { X int row, col; X X if (shots && (random() & 1) == 0) X return (1); X X ++shots; X find_target(&row, &col); X X if (row < 5 X || row > nrows - 5 X || (random() & 15) < 5) X { X X /* X * Shoot a (light, middleweight or heavyweight) spaceship X */ X int ship_size = (random() & 63) / 22; /* ~1/3 */ X X shot_time = 2 * col + 20; X X col = (random() & 3) + firstcol / 2; X row += (random() & 3); X X if (col > NCOLS - 6 - 1) X col = NCOLS - 6 - 1; X X if (row > NROWS - 3 - 1) X row = NROWS - 3 - 1; X X birth(1 + row, 0 + col + ship_size); X X if (ship_size < 2) X birth(0 + row, 1 + col + ship_size); X X if (ship_size < 1) X birth(0 + row, 2 + col + ship_size); X X birth(0 + row, 3 + col); X birth(0 + row, 4 + col); X birth(0 + row, 5 + col); X birth(0 + row, 6 + col); X birth(1 + row, 6 + col); X birth(2 + row, 6 + col); X birth(3 + row, 5 + col); X } X else X { X /* X * Shoot a glider X */ X shot_time = 4 * (col > row ? row : col) + 20; X X if (col > row) X col -= row, row = 0; X else X row -= col, col = 0; X X col += (random() & 3); X row += (random() & 3); X X if (firstrow < firstcol) X col += firstrow / 2, row += firstrow / 2; X else X col += firstcol / 2, row += firstcol / 2; X X if (col > NCOLS - 2 - 1) X col = NCOLS - 2 - 1; X X if (row > NROWS - 2 - 1) X row = NROWS - 2 - 1; X X birth(0 + row, 2 + col); X birth(1 + row, 2 + col); X birth(2 + row, 2 + col); X birth(2 + row, 1 + col); X birth(1 + row, 0 + col); X } X } X X return (0); X } \End\of\File\ else echo "Not overwriting ./lifegame.c" fi echo "Finished archive 1 of 1" exit -- Karl F. Fox, Morning Star Technologies, Inc. karl@MorningStar.COM