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