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