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