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