freebsd-dev/contrib/ncurses/test/worm.c

373 lines
8.6 KiB
C
Raw Normal View History

/*
@@@ @@@ @@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@
@@@ @@@ @@@@@@@@@@@@ @@@@@@@@@@@@ @@@@@@@@@@@@@
@@@ @@@ @@@@ @@@@ @@@@ @@@@ @@@ @@@@
@@@ @@ @@@ @@@ @@@ @@@ @@@ @@@ @@@
@@@ @@@@ @@@ @@@ @@@ @@@ @@@ @@@ @@@
@@@@ @@@@ @@@@ @@@ @@@ @@@ @@@ @@@ @@@
@@@@@@@@@@@@ @@@@ @@@@ @@@ @@@ @@@ @@@
@@@@ @@@@ @@@@@@@@@@@@ @@@ @@@ @@@ @@@
@@ @@ @@@@@@@@@@ @@@ @@@ @@@ @@@
Eric P. Scott
Caltech High Energy Physics
October, 1980
Hacks to turn this into a test frame for cursor movement:
Eric S. Raymond <esr@snark.thyrsus.com>
January, 1995
July 1995 (esr): worms is now in living color! :-)
Options:
-f fill screen with copies of 'WORM' at start.
-l <n> set worm length
-n <n> set number of worms
-t make worms leave droppings
-T <start> <end> 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.23 1999/03/06 22:52:04 tom Exp $
*/
#include <test.priv.h>
#include <term.h> /* for tparm() */
#include <signal.h>
#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;x<argc;x++) {
register char *p;
p=argv[x];
if (*p=='-') p++;
switch (*p) {
case 'f':
field="WORM";
break;
case 'l':
if (++x==argc) goto usage;
if ((length=atoi(argv[x]))<2||length>1024) {
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;
}
}
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, bg);
init_pair(COLOR_RED, COLOR_RED, bg);
init_pair(COLOR_CYAN, COLOR_CYAN, bg);
init_pair(COLOR_WHITE, COLOR_WHITE, bg);
init_pair(COLOR_MAGENTA, COLOR_MAGENTA, bg);
init_pair(COLOR_BLUE, COLOR_BLUE, bg);
init_pair(COLOR_YELLOW, COLOR_YELLOW, bg);
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<LINES;) {
ref[n++]=ip; ip+=COLS;
}
for (ip=ref[0],n=LINES*COLS;--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');
}
}
napms(10);
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];n<number;n++,w++) {
if ((x=w->xpos[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]++;
}
napms(10);
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;
}