ncurses 5.5
[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.39 2005/08/20 20:26:29 tom Exp $
38 */
39
40 #include <test.priv.h>
41
42 static chtype flavor[] =
43 {
44     'O', '*', '#', '$', '%', '0', '@',
45 };
46 static 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 };
53 static struct worm {
54     int orientation, head;
55     short *xpos, *ypos;
56 } worm[40];
57
58 static const char *field;
59 static int length = 16, number = 3;
60 static chtype trail = ' ';
61
62 #ifdef TRACE
63 static int generation, trace_start, trace_end, singlestep;
64 #endif /* TRACE */
65 /* *INDENT-OFF* */
66 static 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
153 static void
154 cleanup(void)
155 {
156     standend();
157     refresh();
158     curs_set(1);
159     endwin();
160 }
161
162 static RETSIGTYPE
163 onsig(int sig GCC_UNUSED)
164 {
165     cleanup();
166     ExitProgram(EXIT_FAILURE);
167 }
168
169 static float
170 ranf(void)
171 {
172     long r = (rand() & 077777);
173     return ((float) r / 32768.);
174 }
175
176 int
177 main(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 }