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