1 /* $OpenBSD: worm.c,v 1.39 2018/08/24 11:14:49 mestre Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * Worm. Written by Michael Toy 34 * UCSC 35 */ 36 37 #include <ctype.h> 38 #include <curses.h> 39 #include <err.h> 40 #include <poll.h> 41 #include <signal.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 45 #define HEAD '@' 46 #define BODY 'o' 47 #define LENGTH 7 48 #define RUNLEN 8 49 #define CNTRL(p) (p-'A'+1) 50 51 WINDOW *tv; 52 WINDOW *stw; 53 struct body { 54 int x; 55 int y; 56 struct body *prev; 57 struct body *next; 58 } *head, *tail, goody; 59 int growing = 0; 60 int growthscale = 1; 61 int running = 0; 62 int slow = 0; 63 int score = 0; 64 int start_len = LENGTH; 65 int visible_len; 66 int lastch; 67 char outbuf[BUFSIZ]; 68 69 volatile sig_atomic_t wantleave = 0; 70 volatile sig_atomic_t wantsuspend = 0; 71 72 __dead void crash(void); 73 void display(struct body *, char); 74 void leave(int); 75 void life(void); 76 void newpos(struct body *); 77 struct body *newlink(void); 78 int process(int); 79 void prize(void); 80 int rnd(int); 81 void setup(void); 82 void suspend(int); 83 84 int 85 main(int argc, char **argv) 86 { 87 int retval; 88 struct pollfd pfd[1]; 89 const char *errstr; 90 struct timespec t, tn, tdiff; 91 92 timespecclear(&t); 93 94 setvbuf(stdout, outbuf, _IOFBF, sizeof outbuf); 95 signal(SIGINT, leave); 96 signal(SIGQUIT, leave); 97 signal(SIGTSTP, suspend); /* process control signal */ 98 initscr(); 99 100 if (pledge("stdio tty", NULL) == -1) 101 err(1, "pledge"); 102 103 cbreak(); 104 noecho(); 105 keypad(stdscr, TRUE); 106 slow = (baudrate() <= 1200); 107 clear(); 108 if (COLS < 18 || LINES < 5) { 109 endwin(); 110 errx(1, "screen too small"); 111 } 112 growthscale = COLS * LINES / 2000; 113 if (growthscale == 0) 114 growthscale = 1; 115 if (argc >= 2) { 116 start_len = strtonum(argv[1], 1, ((LINES-3) * (COLS-2)) / 3, 117 &errstr); 118 if (errstr) { 119 endwin(); 120 errx(1, "length argument is %s.", errstr); 121 } 122 } 123 stw = newwin(1, COLS-1, 0, 0); 124 tv = newwin(LINES-1, COLS-1, 1, 0); 125 box(tv, '*', '*'); 126 scrollok(tv, FALSE); 127 scrollok(stw, FALSE); 128 wmove(stw, 0, 0); 129 wprintw(stw, " Worm"); 130 refresh(); 131 wrefresh(stw); 132 wrefresh(tv); 133 life(); /* Create the worm */ 134 prize(); /* Put up a goal */ 135 wmove(tv, head->y, head->x); /* Leave cursor on worm */ 136 wrefresh(tv); 137 while (1) { 138 if (wantleave) { 139 endwin(); 140 return 0; 141 } 142 if (wantsuspend) { 143 move(LINES-1, 0); 144 refresh(); 145 endwin(); 146 fflush(stdout); 147 kill(getpid(), SIGSTOP); 148 signal(SIGTSTP, suspend); 149 cbreak(); 150 noecho(); 151 setup(); 152 wantsuspend = 0; 153 } 154 155 if (running) { 156 running--; 157 process(lastch); 158 } else { 159 /* Check for timeout. */ 160 clock_gettime(CLOCK_MONOTONIC, &tn); 161 if (timespeccmp(&t, &tn, <=)) { 162 t = tn; 163 t.tv_sec += 1; 164 165 process(lastch); 166 continue; 167 } 168 169 /* Prepare next read */ 170 pfd[0].fd = STDIN_FILENO; 171 pfd[0].events = POLLIN; 172 timespecsub(&t, &tn, &tdiff); 173 retval = ppoll(pfd, 1, &tdiff, NULL); 174 175 /* Nothing to do if timed out or signal. */ 176 if (retval <= 0) 177 continue; 178 179 /* Only update timer if valid key was pressed. */ 180 if (process(getch()) == 0) 181 continue; 182 183 /* Update using clock_gettime(), tn is too old now. */ 184 clock_gettime(CLOCK_MONOTONIC, &t); 185 t.tv_sec += 1; 186 } 187 } 188 } 189 190 void 191 life(void) 192 { 193 struct body *bp, *np; 194 int i,j = 1; 195 196 head = newlink(); 197 head->x = start_len % (COLS-5) + 2; 198 head->y = LINES / 2; 199 head->next = NULL; 200 display(head, HEAD); 201 for (i = 0, bp = head; i < start_len; i++, bp = np) { 202 np = newlink(); 203 np->next = bp; 204 bp->prev = np; 205 if (((bp->x <= 2) && (j == 1)) || ((bp->x >= COLS-4) && (j == -1))) { 206 j *= -1; 207 np->x = bp->x; 208 np->y = bp->y + 1; 209 } else { 210 np->x = bp->x - j; 211 np->y = bp->y; 212 } 213 display(np, BODY); 214 } 215 tail = np; 216 tail->prev = NULL; 217 visible_len = start_len + 1; 218 } 219 220 void 221 display(struct body *pos, char chr) 222 { 223 wmove(tv, pos->y, pos->x); 224 waddch(tv, chr); 225 } 226 227 void 228 leave(int dummy) 229 { 230 wantleave = 1; 231 } 232 233 int 234 rnd(int range) 235 { 236 return arc4random_uniform(range); 237 } 238 239 void 240 newpos(struct body *bp) 241 { 242 if (visible_len == (LINES-3) * (COLS-3) - 1) { 243 endwin(); 244 printf("\nYou won!\nYour final score was %d\n\n", score); 245 exit(0); 246 } 247 do { 248 bp->y = rnd(LINES-3)+ 1; 249 bp->x = rnd(COLS-3) + 1; 250 wmove(tv, bp->y, bp->x); 251 } while(winch(tv) != ' '); 252 } 253 254 void 255 prize(void) 256 { 257 int value; 258 259 value = rnd(9) + 1; 260 newpos(&goody); 261 waddch(tv, value+'0'); 262 wrefresh(tv); 263 } 264 265 int 266 process(int ch) 267 { 268 int x,y; 269 struct body *nh; 270 271 x = head->x; 272 y = head->y; 273 switch(ch) { 274 #ifdef KEY_LEFT 275 case KEY_LEFT: 276 #endif 277 case 'h': 278 x--; 279 break; 280 #ifdef KEY_DOWN 281 case KEY_DOWN: 282 #endif 283 case 'j': 284 y++; 285 break; 286 #ifdef KEY_UP 287 case KEY_UP: 288 #endif 289 case 'k': 290 y--; 291 break; 292 #ifdef KEY_RIGHT 293 case KEY_RIGHT: 294 #endif 295 case 'l': 296 x++; 297 break; 298 case 'H': 299 x--; 300 running = RUNLEN; 301 ch = tolower(ch); 302 break; 303 case 'J': 304 y++; 305 running = RUNLEN/2; 306 ch = tolower(ch); 307 break; 308 case 'K': 309 y--; 310 running = RUNLEN/2; 311 ch = tolower(ch); 312 break; 313 case 'L': 314 x++; 315 running = RUNLEN; 316 ch = tolower(ch); 317 break; 318 case '\f': 319 setup(); 320 return (0); 321 case CNTRL('Z'): 322 suspend(0); 323 return (0); 324 case CNTRL('C'): 325 crash(); 326 return (0); 327 case CNTRL('D'): 328 crash(); 329 return (0); 330 case ERR: 331 leave(0); 332 return (0); 333 default: 334 return (0); 335 } 336 lastch = ch; 337 if (growing == 0) { 338 display(tail, ' '); 339 tail->next->prev = NULL; 340 nh = tail->next; 341 free(tail); 342 tail = nh; 343 visible_len--; 344 } else 345 growing--; 346 display(head, BODY); 347 wmove(tv, y, x); 348 if (isdigit(ch = winch(tv))) { 349 int amt = ch - '0'; 350 growing += amt * growthscale; 351 prize(); 352 score += amt; 353 running = 0; 354 wmove(stw, 0, COLS - 12); 355 wprintw(stw, "Score: %3d", score); 356 wrefresh(stw); 357 } else if(ch != ' ') 358 crash(); 359 nh = newlink(); 360 nh->next = NULL; 361 nh->prev = head; 362 head->next = nh; 363 nh->y = y; 364 nh->x = x; 365 display(nh, HEAD); 366 head = nh; 367 visible_len++; 368 if (!(slow && running)) { 369 wmove(tv, head->y, head->x); 370 wrefresh(tv); 371 } 372 return (1); 373 } 374 375 struct body * 376 newlink(void) 377 { 378 struct body *tmp; 379 380 if ((tmp = malloc(sizeof (struct body))) == NULL) { 381 endwin(); 382 errx(1, "out of memory"); 383 } 384 return (tmp); 385 } 386 387 void 388 crash(void) 389 { 390 sleep(2); 391 clear(); 392 endwin(); 393 printf("Well, you ran into something and the game is over.\n"); 394 printf("Your final score was %d\n", score); 395 exit(0); /* leave() calls endwin(), which would hose the printf()'s */ 396 } 397 398 void 399 suspend(int dummy) 400 { 401 wantsuspend = 1; 402 } 403 404 void 405 setup(void) 406 { 407 clear(); 408 refresh(); 409 touchwin(stw); 410 wrefresh(stw); 411 touchwin(tv); 412 wrefresh(tv); 413 } 414