/* @@@ @@@ @@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@ @@@ @@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@@ @@@ @@@ @@@@ @@@@ @@@@ @@@@ @@@ @@@@ @@@ @@ @@@ @@@ @@@ @@@ @@@ @@@ @@@ @@@ @@@@ @@@ @@@ @@@ @@@ @@@ @@@ @@@ @@@@ @@@@ @@@@ @@@ @@@ @@@ @@@ @@@ @@@ @@@@@@@@@@@@ @@@@ @@@@ @@@ @@@ @@@ @@@ @@@@ @@@@ @@@@@@@@@@@@ @@@ @@@ @@@ @@@ @@ @@ @@@@@@@@@@ @@@ @@@ @@@ @@@ Eric P. Scott Caltech High Energy Physics October, 1980 Hacks to turn this into a test frame for cursor movement: Eric S. Raymond January, 1995 July 1995 (esr): worms is now in living color! :-) Options: -f fill screen with copies of 'WORM' at start. -l set worm length -n set number of worms -t make worms leave droppings -T set trace interval -S set single-stepping during trace interval -N suppress cursor-movement optimization This program makes a good torture-test for the ncurses cursor-optimization code. You can use -T to set the worm move interval over which movement traces will be dumped. The program stops and waits for one character of input at the beginning and end of the interval. $Id: worm.c,v 1.20 1997/05/03 18:38:57 tom Exp $ */ #include #include /* for tparm() */ #include #define cursor(col,row) move(row,col) short *ref[128]; static chtype flavor[]={ 'O' , '*', '#', '$', '%', '0', '@', }; #define MAXWORMS (sizeof(flavor)/sizeof(chtype)) static const short xinc[]={ 1, 1, 1, 0, -1, -1, -1, 0 }, yinc[]={ -1, 0, 1, 1, 1, 0, -1, -1 }; static struct worm { int orientation, head; short *xpos, *ypos; } worm[40]; static const char *field; static int length=16, number=3; static chtype trail=' '; #ifdef TRACE int generation, trace_start, trace_end, singlestep; #endif /* TRACE */ static const struct options { int nopts; int opts[3]; } normal[8]={ { 3, { 7, 0, 1 } }, { 3, { 0, 1, 2 } }, { 3, { 1, 2, 3 } }, { 3, { 2, 3, 4 } }, { 3, { 3, 4, 5 } }, { 3, { 4, 5, 6 } }, { 3, { 5, 6, 7 } }, { 3, { 6, 7, 0 } } }, upper[8]={ { 1, { 1, 0, 0 } }, { 2, { 1, 2, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 2, { 4, 5, 0 } }, { 1, { 5, 0, 0 } }, { 2, { 1, 5, 0 } } }, left[8]={ { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 2, { 2, 3, 0 } }, { 1, { 3, 0, 0 } }, { 2, { 3, 7, 0 } }, { 1, { 7, 0, 0 } }, { 2, { 7, 0, 0 } } }, right[8]={ { 1, { 7, 0, 0 } }, { 2, { 3, 7, 0 } }, { 1, { 3, 0, 0 } }, { 2, { 3, 4, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 2, { 6, 7, 0 } } }, lower[8]={ { 0, { 0, 0, 0 } }, { 2, { 0, 1, 0 } }, { 1, { 1, 0, 0 } }, { 2, { 1, 5, 0 } }, { 1, { 5, 0, 0 } }, { 2, { 5, 6, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } } }, upleft[8]={ { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 1, { 3, 0, 0 } }, { 2, { 1, 3, 0 } }, { 1, { 1, 0, 0 } } }, upright[8]={ { 2, { 3, 5, 0 } }, { 1, { 3, 0, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 1, { 5, 0, 0 } } }, lowleft[8]={ { 3, { 7, 0, 1 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 1, { 1, 0, 0 } }, { 2, { 1, 7, 0 } }, { 1, { 7, 0, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } } }, lowright[8]={ { 0, { 0, 0, 0 } }, { 1, { 7, 0, 0 } }, { 2, { 5, 7, 0 } }, { 1, { 5, 0, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } }, { 0, { 0, 0, 0 } } }; static RETSIGTYPE onsig(int sig); static float ranf(void); int main(int argc, char *argv[]) { int x, y; int n; struct worm *w; const struct options *op; int h; short *ip; int last, bottom; for (x=1;x1024) { fprintf(stderr,"%s: Invalid length\n",*argv); return EXIT_FAILURE; } break; case 'n': if (++x==argc) goto usage; if ((number=atoi(argv[x]))<1||number>40) { fprintf(stderr,"%s: Invalid number of worms\n",*argv); return EXIT_FAILURE; } break; case 't': trail='.'; break; #ifdef TRACE case 'S': singlestep = TRUE; break; case 'T': trace_start = atoi(argv[++x]); trace_end = atoi(argv[++x]); break; case 'N': _nc_optimize_enable ^= OPTIMIZE_ALL; /* declared by ncurses */ break; #endif /* TRACE */ default: usage: fprintf(stderr, "usage: %s [-field] [-length #] [-number #] [-trail]\n",*argv); return EXIT_FAILURE; break; } } signal(SIGINT, onsig); initscr(); noecho(); cbreak(); nonl(); curs_set(0); bottom = LINES-1; last = COLS-1; #ifdef A_COLOR if (has_colors()) { int bg = COLOR_BLACK; start_color(); #ifdef NCURSES_VERSION if (use_default_colors() == OK) bg = -1; #endif init_pair(COLOR_GREEN, COLOR_GREEN, -1); init_pair(COLOR_RED, COLOR_RED, -1); init_pair(COLOR_CYAN, COLOR_CYAN, -1); init_pair(COLOR_WHITE, COLOR_WHITE, -1); init_pair(COLOR_MAGENTA, COLOR_MAGENTA, -1); init_pair(COLOR_BLUE, COLOR_BLUE, -1); init_pair(COLOR_YELLOW, COLOR_YELLOW, -1); flavor[0] |= COLOR_PAIR(COLOR_GREEN) | A_BOLD; flavor[1] |= COLOR_PAIR(COLOR_RED) | A_BOLD; flavor[2] |= COLOR_PAIR(COLOR_CYAN) | A_BOLD; flavor[3] |= COLOR_PAIR(COLOR_WHITE) | A_BOLD; flavor[4] |= COLOR_PAIR(COLOR_MAGENTA) | A_BOLD; flavor[5] |= COLOR_PAIR(COLOR_BLUE) | A_BOLD; flavor[6] |= COLOR_PAIR(COLOR_YELLOW) | A_BOLD; } #endif /* A_COLOR */ ip=(short *)malloc(LINES*COLS*sizeof (short)); for (n=0;n=0;) *ip++=0; #ifdef BADCORNER /* if addressing the lower right corner doesn't work in your curses */ ref[bottom][last]=1; #endif /* BADCORNER */ for (n=number, w= &worm[0];--n>=0;w++) { w->orientation=w->head=0; if (!(ip=(short *)malloc((length+1)*sizeof (short)))) { fprintf(stderr,"%s: out of memory\n",*argv); return EXIT_FAILURE; } w->xpos=ip; for (x=length;--x>=0;) *ip++ = -1; if (!(ip=(short *)malloc((length+1)*sizeof (short)))) { fprintf(stderr,"%s: out of memory\n",*argv); return EXIT_FAILURE; } w->ypos=ip; for (y=length;--y>=0;) *ip++ = -1; } if (field) { register const char *p; p=field; for (y=bottom;--y>=0;) { for (x=COLS;--x>=0;) { addch((chtype)(*p++)); if (!*p) p=field; } addch('\n'); } } refresh(); for (;;) { #ifdef TRACE if (trace_start || trace_end) { if (generation == trace_start) { trace(TRACE_CALLS); getch(); } else if (generation == trace_end) { trace(0); getch(); } if (singlestep && generation > trace_start && generation < trace_end) getch(); generation++; } #endif /* TRACE */ for (n=0,w= &worm[0];nxpos[h=w->head])<0) { cursor(x=w->xpos[h]=0,y=w->ypos[h]=bottom); addch(flavor[n % MAXWORMS]); ref[y][x]++; } else y=w->ypos[h]; if (++h==length) h=0; if (w->xpos[w->head=h]>=0) { register int x1, y1; x1=w->xpos[h]; y1=w->ypos[h]; if (--ref[y1][x1]==0) { cursor(x1,y1); addch(trail); } } op= &(x==0 ? (y==0 ? upleft : (y==bottom ? lowleft : left)) : (x==last ? (y==0 ? upright : (y==bottom ? lowright : right)) : (y==0 ? upper : (y==bottom ? lower : normal))))[w->orientation]; switch (op->nopts) { case 0: refresh(); curs_set(1); endwin(); return EXIT_SUCCESS; case 1: w->orientation=op->opts[0]; break; default: w->orientation=op->opts[(int)(ranf()*(float)op->nopts)]; } cursor(x+=xinc[w->orientation], y+=yinc[w->orientation]); if (y < 0 ) y = 0; addch(flavor[n % MAXWORMS]); ref[w->ypos[h]=y][w->xpos[h]=x]++; } refresh(); } } static RETSIGTYPE onsig(int sig GCC_UNUSED) { standend(); refresh(); curs_set(1); endwin(); exit(EXIT_FAILURE); } static float ranf(void) { float rv; long r = rand(); r &= 077777; rv =((float)r/32767.); return rv; }