1/*
2
3	 @@@        @@@    @@@@@@@@@@     @@@@@@@@@@@    @@@@@@@@@@@@
4	 @@@        @@@   @@@@@@@@@@@@    @@@@@@@@@@@@   @@@@@@@@@@@@@
5	 @@@        @@@  @@@@      @@@@   @@@@           @@@@ @@@  @@@@
6	 @@@   @@   @@@  @@@        @@@   @@@            @@@  @@@   @@@
7	 @@@  @@@@  @@@  @@@        @@@   @@@            @@@  @@@   @@@
8	 @@@@ @@@@ @@@@  @@@        @@@   @@@            @@@  @@@   @@@
9	  @@@@@@@@@@@@   @@@@      @@@@   @@@            @@@  @@@   @@@
10	   @@@@  @@@@     @@@@@@@@@@@@    @@@            @@@  @@@   @@@
11	    @@    @@       @@@@@@@@@@     @@@            @@@  @@@   @@@
12
13				 Eric P. Scott
14			  Caltech High Energy Physics
15				 October, 1980
16
17		Hacks to turn this into a test frame for cursor movement:
18			Eric S. Raymond <esr@snark.thyrsus.com>
19				January, 1995
20
21		July 1995 (esr): worms is now in living color! :-)
22
23Options:
24	-f			fill screen with copies of 'WORM' at start.
25	-l <n>			set worm length
26	-n <n>			set number of worms
27	-t			make worms leave droppings
28	-T <start> <end>	set trace interval
29	-S			set single-stepping during trace interval
30	-N			suppress cursor-movement optimization
31
32  This program makes a good torture-test for the ncurses cursor-optimization
33  code.  You can use -T to set the worm move interval over which movement
34  traces will be dumped.  The program stops and waits for one character of
35  input at the beginning and end of the interval.
36
37  $Id: worm.c,v 1.39 2005/08/20 20:26:29 tom Exp $
38*/
39
40#include <test.priv.h>
41
42static chtype flavor[] =
43{
44    'O', '*', '#', '$', '%', '0', '@',
45};
46static const short xinc[] =
47{
48    1, 1, 1, 0, -1, -1, -1, 0
49}, yinc[] =
50{
51    -1, 0, 1, 1, 1, 0, -1, -1
52};
53static struct worm {
54    int orientation, head;
55    short *xpos, *ypos;
56} worm[40];
57
58static const char *field;
59static int length = 16, number = 3;
60static chtype trail = ' ';
61
62#ifdef TRACE
63static int generation, trace_start, trace_end, singlestep;
64#endif /* TRACE */
65/* *INDENT-OFF* */
66static const struct options {
67    int nopts;
68    int opts[3];
69} normal[8]={
70    { 3, { 7, 0, 1 } },
71    { 3, { 0, 1, 2 } },
72    { 3, { 1, 2, 3 } },
73    { 3, { 2, 3, 4 } },
74    { 3, { 3, 4, 5 } },
75    { 3, { 4, 5, 6 } },
76    { 3, { 5, 6, 7 } },
77    { 3, { 6, 7, 0 } }
78}, upper[8]={
79    { 1, { 1, 0, 0 } },
80    { 2, { 1, 2, 0 } },
81    { 0, { 0, 0, 0 } },
82    { 0, { 0, 0, 0 } },
83    { 0, { 0, 0, 0 } },
84    { 2, { 4, 5, 0 } },
85    { 1, { 5, 0, 0 } },
86    { 2, { 1, 5, 0 } }
87}, left[8]={
88    { 0, { 0, 0, 0 } },
89    { 0, { 0, 0, 0 } },
90    { 0, { 0, 0, 0 } },
91    { 2, { 2, 3, 0 } },
92    { 1, { 3, 0, 0 } },
93    { 2, { 3, 7, 0 } },
94    { 1, { 7, 0, 0 } },
95    { 2, { 7, 0, 0 } }
96}, right[8]={
97    { 1, { 7, 0, 0 } },
98    { 2, { 3, 7, 0 } },
99    { 1, { 3, 0, 0 } },
100    { 2, { 3, 4, 0 } },
101    { 0, { 0, 0, 0 } },
102    { 0, { 0, 0, 0 } },
103    { 0, { 0, 0, 0 } },
104    { 2, { 6, 7, 0 } }
105}, lower[8]={
106    { 0, { 0, 0, 0 } },
107    { 2, { 0, 1, 0 } },
108    { 1, { 1, 0, 0 } },
109    { 2, { 1, 5, 0 } },
110    { 1, { 5, 0, 0 } },
111    { 2, { 5, 6, 0 } },
112    { 0, { 0, 0, 0 } },
113    { 0, { 0, 0, 0 } }
114}, upleft[8]={
115    { 0, { 0, 0, 0 } },
116    { 0, { 0, 0, 0 } },
117    { 0, { 0, 0, 0 } },
118    { 0, { 0, 0, 0 } },
119    { 0, { 0, 0, 0 } },
120    { 1, { 3, 0, 0 } },
121    { 2, { 1, 3, 0 } },
122    { 1, { 1, 0, 0 } }
123}, upright[8]={
124    { 2, { 3, 5, 0 } },
125    { 1, { 3, 0, 0 } },
126    { 0, { 0, 0, 0 } },
127    { 0, { 0, 0, 0 } },
128    { 0, { 0, 0, 0 } },
129    { 0, { 0, 0, 0 } },
130    { 0, { 0, 0, 0 } },
131    { 1, { 5, 0, 0 } }
132}, lowleft[8]={
133    { 3, { 7, 0, 1 } },
134    { 0, { 0, 0, 0 } },
135    { 0, { 0, 0, 0 } },
136    { 1, { 1, 0, 0 } },
137    { 2, { 1, 7, 0 } },
138    { 1, { 7, 0, 0 } },
139    { 0, { 0, 0, 0 } },
140    { 0, { 0, 0, 0 } }
141}, lowright[8]={
142    { 0, { 0, 0, 0 } },
143    { 1, { 7, 0, 0 } },
144    { 2, { 5, 7, 0 } },
145    { 1, { 5, 0, 0 } },
146    { 0, { 0, 0, 0 } },
147    { 0, { 0, 0, 0 } },
148    { 0, { 0, 0, 0 } },
149    { 0, { 0, 0, 0 } }
150};
151/* *INDENT-ON* */
152
153static void
154cleanup(void)
155{
156    standend();
157    refresh();
158    curs_set(1);
159    endwin();
160}
161
162static RETSIGTYPE
163onsig(int sig GCC_UNUSED)
164{
165    cleanup();
166    ExitProgram(EXIT_FAILURE);
167}
168
169static float
170ranf(void)
171{
172    long r = (rand() & 077777);
173    return ((float) r / 32768.);
174}
175
176int
177main(int argc, char *argv[])
178{
179    short **ref;
180    int x, y;
181    int n;
182    struct worm *w;
183    const struct options *op;
184    int h;
185    short *ip;
186    int last, bottom;
187
188    setlocale(LC_ALL, "");
189
190    for (x = 1; x < argc; x++) {
191	char *p;
192	p = argv[x];
193	if (*p == '-')
194	    p++;
195	switch (*p) {
196	case 'f':
197	    field = "WORM";
198	    break;
199	case 'l':
200	    if (++x == argc)
201		goto usage;
202	    if ((length = atoi(argv[x])) < 2 || length > 1024) {
203		fprintf(stderr, "%s: Invalid length\n", *argv);
204		ExitProgram(EXIT_FAILURE);
205	    }
206	    break;
207	case 'n':
208	    if (++x == argc)
209		goto usage;
210	    if ((number = atoi(argv[x])) < 1 || number > 40) {
211		fprintf(stderr, "%s: Invalid number of worms\n", *argv);
212		ExitProgram(EXIT_FAILURE);
213	    }
214	    break;
215	case 't':
216	    trail = '.';
217	    break;
218#ifdef TRACE
219	case 'S':
220	    singlestep = TRUE;
221	    break;
222	case 'T':
223	    trace_start = atoi(argv[++x]);
224	    trace_end = atoi(argv[++x]);
225	    break;
226	case 'N':
227	    _nc_optimize_enable ^= OPTIMIZE_ALL;	/* declared by ncurses */
228	    break;
229#endif /* TRACE */
230	default:
231	  usage:
232	    fprintf(stderr,
233		    "usage: %s [-field] [-length #] [-number #] [-trail]\n", *argv);
234	    ExitProgram(EXIT_FAILURE);
235	}
236    }
237
238    signal(SIGINT, onsig);
239    initscr();
240    noecho();
241    cbreak();
242    nonl();
243
244    curs_set(0);
245
246    bottom = LINES - 1;
247    last = COLS - 1;
248
249#ifdef A_COLOR
250    if (has_colors()) {
251	int bg = COLOR_BLACK;
252	start_color();
253#if HAVE_USE_DEFAULT_COLORS
254	if (use_default_colors() == OK)
255	    bg = -1;
256#endif
257
258#define SET_COLOR(num, fg) \
259	    init_pair(num+1, fg, bg); \
260	    flavor[num] |= COLOR_PAIR(num+1) | A_BOLD
261
262	SET_COLOR(0, COLOR_GREEN);
263	SET_COLOR(1, COLOR_RED);
264	SET_COLOR(2, COLOR_CYAN);
265	SET_COLOR(3, COLOR_WHITE);
266	SET_COLOR(4, COLOR_MAGENTA);
267	SET_COLOR(5, COLOR_BLUE);
268	SET_COLOR(6, COLOR_YELLOW);
269    }
270#endif /* A_COLOR */
271
272    ref = typeMalloc(short *, LINES);
273    for (y = 0; y < LINES; y++) {
274	ref[y] = typeMalloc(short, COLS);
275	for (x = 0; x < COLS; x++) {
276	    ref[y][x] = 0;
277	}
278    }
279
280#ifdef BADCORNER
281    /* if addressing the lower right corner doesn't work in your curses */
282    ref[bottom][last] = 1;
283#endif /* BADCORNER */
284
285    for (n = number, w = &worm[0]; --n >= 0; w++) {
286	w->orientation = w->head = 0;
287	if (!(ip = typeMalloc(short, (length + 1)))) {
288	    fprintf(stderr, "%s: out of memory\n", *argv);
289	    ExitProgram(EXIT_FAILURE);
290	}
291	w->xpos = ip;
292	for (x = length; --x >= 0;)
293	    *ip++ = -1;
294	if (!(ip = typeMalloc(short, (length + 1)))) {
295	    fprintf(stderr, "%s: out of memory\n", *argv);
296	    ExitProgram(EXIT_FAILURE);
297	}
298	w->ypos = ip;
299	for (y = length; --y >= 0;)
300	    *ip++ = -1;
301    }
302    if (field) {
303	const char *p;
304	p = field;
305	for (y = bottom; --y >= 0;) {
306	    for (x = COLS; --x >= 0;) {
307		addch((chtype) (*p++));
308		if (!*p)
309		    p = field;
310	    }
311	}
312    }
313    napms(10);
314    refresh();
315#ifndef TRACE
316    nodelay(stdscr, TRUE);
317#endif
318
319    for (;;) {
320#ifdef TRACE
321	if (trace_start || trace_end) {
322	    if (generation == trace_start) {
323		trace(TRACE_CALLS);
324		getch();
325	    } else if (generation == trace_end) {
326		trace(0);
327		getch();
328	    }
329
330	    if (singlestep && generation > trace_start && generation < trace_end)
331		getch();
332
333	    generation++;
334	}
335#else
336	int ch;
337
338	if ((ch = getch()) > 0) {
339#ifdef KEY_RESIZE
340	    if (ch == KEY_RESIZE) {
341		if (last != COLS - 1) {
342		    for (y = 0; y <= bottom; y++) {
343			ref[y] = typeRealloc(short, COLS, ref[y]);
344			for (x = last + 1; x < COLS; x++)
345			    ref[y][x] = 0;
346		    }
347		    last = COLS - 1;
348		}
349		if (bottom != LINES - 1) {
350		    for (y = LINES; y <= bottom; y++)
351			free(ref[y]);
352		    ref = typeRealloc(short *, LINES, ref);
353		    for (y = bottom + 1; y < LINES; y++) {
354			ref[y] = typeMalloc(short, COLS);
355			for (x = 0; x < COLS; x++)
356			    ref[y][x] = 0;
357		    }
358		    bottom = LINES - 1;
359		}
360	    }
361#endif
362	    /*
363	     * Make it simple to put this into single-step mode, or resume
364	     * normal operation -T.Dickey
365	     */
366	    if (ch == 'q') {
367		cleanup();
368		ExitProgram(EXIT_SUCCESS);
369	    } else if (ch == 's') {
370		nodelay(stdscr, FALSE);
371	    } else if (ch == ' ') {
372		nodelay(stdscr, TRUE);
373	    }
374	}
375#endif /* TRACE */
376
377	for (n = 0, w = &worm[0]; n < number; n++, w++) {
378	    if ((x = w->xpos[h = w->head]) < 0) {
379		move(y = w->ypos[h] = bottom, x = w->xpos[h] = 0);
380		addch(flavor[n % SIZEOF(flavor)]);
381		ref[y][x]++;
382	    } else {
383		y = w->ypos[h];
384	    }
385	    if (x > last)
386		x = last;
387	    if (y > bottom)
388		y = bottom;
389	    if (++h == length)
390		h = 0;
391	    if (w->xpos[w->head = h] >= 0) {
392		int x1, y1;
393		x1 = w->xpos[h];
394		y1 = w->ypos[h];
395		if (y1 < LINES
396		    && x1 < COLS
397		    && --ref[y1][x1] == 0) {
398		    move(y1, x1);
399		    addch(trail);
400		}
401	    }
402	    op = &(x == 0 ? (y == 0 ? upleft : (y == bottom ? lowleft :
403						left)) :
404		   (x == last ? (y == 0 ? upright : (y == bottom ? lowright :
405						     right)) :
406		    (y == 0 ? upper : (y == bottom ? lower : normal))))[w->orientation];
407	    switch (op->nopts) {
408	    case 0:
409		cleanup();
410		ExitProgram(EXIT_SUCCESS);
411	    case 1:
412		w->orientation = op->opts[0];
413		break;
414	    default:
415		w->orientation = op->opts[(int) (ranf() * (float) op->nopts)];
416	    }
417	    move(y += yinc[w->orientation], x += xinc[w->orientation]);
418
419	    if (y < 0)
420		y = 0;
421	    addch(flavor[n % SIZEOF(flavor)]);
422	    ref[w->ypos[h] = y][w->xpos[h] = x]++;
423	}
424	napms(10);
425	refresh();
426    }
427}
428