1 /* $OpenBSD: worm.c,v 1.27 2014/11/03 22:14:54 deraadt 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 <sys/types.h> 38 #include <ctype.h> 39 #include <curses.h> 40 #include <err.h> 41 #include <signal.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <time.h> 45 #include <unistd.h> 46 #include <poll.h> 47 48 #define HEAD '@' 49 #define BODY 'o' 50 #define LENGTH 7 51 #define RUNLEN 8 52 #define CNTRL(p) (p-'A'+1) 53 54 WINDOW *tv; 55 WINDOW *stw; 56 struct body { 57 int x; 58 int y; 59 struct body *prev; 60 struct body *next; 61 } *head, *tail, goody; 62 int growing = 0; 63 int running = 0; 64 int slow = 0; 65 int score = 0; 66 int start_len = LENGTH; 67 int visible_len; 68 int lastch; 69 char outbuf[BUFSIZ]; 70 71 volatile sig_atomic_t wantleave = 0; 72 volatile sig_atomic_t wantsuspend = 0; 73 74 void crash(void); 75 void display(struct body *, char); 76 void leave(int); 77 void life(void); 78 void newpos(struct body *); 79 struct body *newlink(void); 80 void process(int); 81 void prize(void); 82 int rnd(int); 83 void setup(void); 84 void suspend(int); 85 86 int 87 main(int argc, char **argv) 88 { 89 int retval; 90 struct timeval t, tod; 91 struct timezone tz; 92 struct pollfd pfd[1]; 93 const char *errstr; 94 95 setbuf(stdout, outbuf); 96 signal(SIGINT, leave); 97 signal(SIGQUIT, leave); 98 signal(SIGTSTP, suspend); /* process control signal */ 99 initscr(); 100 cbreak(); 101 noecho(); 102 keypad(stdscr, TRUE); 103 slow = (baudrate() <= 1200); 104 clear(); 105 if (COLS < 18 || LINES < 5) { 106 endwin(); 107 errx(1, "screen too small"); 108 } 109 if (argc >= 2) { 110 start_len = strtonum(argv[1], 1, ((LINES-3) * (COLS-2)) / 3, 111 &errstr); 112 if (errstr) { 113 endwin(); 114 errx(1, "length argument is %s.", errstr); 115 } 116 } 117 stw = newwin(1, COLS-1, 0, 0); 118 tv = newwin(LINES-1, COLS-1, 1, 0); 119 box(tv, '*', '*'); 120 scrollok(tv, FALSE); 121 scrollok(stw, FALSE); 122 wmove(stw, 0, 0); 123 wprintw(stw, " Worm"); 124 refresh(); 125 wrefresh(stw); 126 wrefresh(tv); 127 life(); /* Create the worm */ 128 prize(); /* Put up a goal */ 129 wmove(tv, head->y, head->x); /* Leave cursor on worm */ 130 wrefresh(tv); 131 while(1) 132 { 133 if (wantleave) { 134 endwin(); /* XXX signal race */ 135 exit(0); 136 } 137 if (wantsuspend) { 138 move(LINES-1, 0); 139 refresh(); 140 endwin(); 141 fflush(stdout); 142 kill(getpid(), SIGSTOP); 143 signal(SIGTSTP, suspend); 144 cbreak(); 145 noecho(); 146 setup(); 147 wantsuspend = 0; 148 } 149 150 if (running) 151 { 152 running--; 153 process(lastch); 154 } 155 else 156 { 157 /* fflush(stdout); */ 158 /* Delay could be a command line option */ 159 t.tv_sec = 1; 160 t.tv_usec = 0; 161 (void)gettimeofday(&tod, &tz); 162 pfd[0].fd = STDIN_FILENO; 163 pfd[0].events = POLLIN; 164 retval = poll(pfd, 1, t.tv_sec * 1000 + t.tv_usec / 1000); 165 if (retval > 0) 166 process(getch()); 167 else 168 process(lastch); 169 } 170 } 171 } 172 173 void 174 life(void) 175 { 176 struct body *bp, *np; 177 int i,j = 1; 178 179 head = newlink(); 180 head->x = start_len % (COLS-5) + 2; 181 head->y = LINES / 2; 182 head->next = NULL; 183 display(head, HEAD); 184 for (i = 0, bp = head; i < start_len; i++, bp = np) { 185 np = newlink(); 186 np->next = bp; 187 bp->prev = np; 188 if (((bp->x <= 2) && (j == 1)) || ((bp->x >= COLS-4) && (j == -1))) { 189 j *= -1; 190 np->x = bp->x; 191 np->y = bp->y + 1; 192 } else { 193 np->x = bp->x - j; 194 np->y = bp->y; 195 } 196 display(np, BODY); 197 } 198 tail = np; 199 tail->prev = NULL; 200 visible_len = start_len + 1; 201 } 202 203 void 204 display(struct body *pos, char chr) 205 { 206 wmove(tv, pos->y, pos->x); 207 waddch(tv, chr); 208 } 209 210 void 211 leave(int dummy) 212 { 213 wantleave = 1; 214 } 215 216 int 217 rnd(int range) 218 { 219 return arc4random_uniform(range); 220 } 221 222 void 223 newpos(struct body *bp) 224 { 225 if (visible_len == (LINES-3) * (COLS-3) - 1) { 226 endwin(); 227 printf("\nYou won!\nYour final score was %d\n\n", score); 228 exit(0); 229 } 230 do { 231 bp->y = rnd(LINES-3)+ 1; 232 bp->x = rnd(COLS-3) + 1; 233 wmove(tv, bp->y, bp->x); 234 } while(winch(tv) != ' '); 235 } 236 237 void 238 prize(void) 239 { 240 int value; 241 242 value = rnd(9) + 1; 243 newpos(&goody); 244 waddch(tv, value+'0'); 245 wrefresh(tv); 246 } 247 248 void 249 process(int ch) 250 { 251 int x,y; 252 struct body *nh; 253 254 x = head->x; 255 y = head->y; 256 switch(ch) 257 { 258 #ifdef KEY_LEFT 259 case KEY_LEFT: 260 #endif 261 case 'h': 262 x--; break; 263 #ifdef KEY_DOWN 264 case KEY_DOWN: 265 #endif 266 case 'j': 267 y++; break; 268 #ifdef KEY_UP 269 case KEY_UP: 270 #endif 271 case 'k': 272 y--; break; 273 #ifdef KEY_RIGHT 274 case KEY_RIGHT: 275 #endif 276 case 'l': 277 x++; break; 278 case 'H': x--; running = RUNLEN; ch = tolower(ch); break; 279 case 'J': y++; running = RUNLEN/2; ch = tolower(ch); break; 280 case 'K': y--; running = RUNLEN/2; ch = tolower(ch); break; 281 case 'L': x++; running = RUNLEN; ch = tolower(ch); break; 282 case '\f': setup(); return; 283 case CNTRL('Z'): suspend(0); return; 284 case CNTRL('C'): crash(); return; 285 case CNTRL('D'): crash(); return; 286 case ERR: leave(0); return; 287 default: return; 288 } 289 lastch = ch; 290 if (growing == 0) 291 { 292 display(tail, ' '); 293 tail->next->prev = NULL; 294 nh = tail->next; 295 free(tail); 296 tail = nh; 297 visible_len--; 298 } 299 else growing--; 300 display(head, BODY); 301 wmove(tv, y, x); 302 if (isdigit(ch = winch(tv))) 303 { 304 growing += ch-'0'; 305 prize(); 306 score += growing; 307 running = 0; 308 wmove(stw, 0, COLS - 12); 309 wprintw(stw, "Score: %3d", score); 310 wrefresh(stw); 311 } 312 else if(ch != ' ') crash(); 313 nh = newlink(); 314 nh->next = NULL; 315 nh->prev = head; 316 head->next = nh; 317 nh->y = y; 318 nh->x = x; 319 display(nh, HEAD); 320 head = nh; 321 visible_len++; 322 if (!(slow && running)) { 323 wmove(tv, head->y, head->x); 324 wrefresh(tv); 325 } 326 } 327 328 struct body * 329 newlink(void) 330 { 331 struct body *tmp; 332 333 if ((tmp = (struct body *) malloc(sizeof (struct body))) == NULL) { 334 endwin(); 335 errx(1, "out of memory"); 336 } 337 return (tmp); 338 } 339 340 void 341 crash(void) 342 { 343 sleep(2); 344 clear(); 345 endwin(); 346 printf("Well, you ran into something and the game is over.\n"); 347 printf("Your final score was %d\n", score); 348 exit(0); /* leave() calls endwin(), which would hose the printf()'s */ 349 } 350 351 void 352 suspend(int dummy) 353 { 354 wantsuspend = 1; 355 } 356 357 void 358 setup(void) 359 { 360 clear(); 361 refresh(); 362 touchwin(stw); 363 wrefresh(stw); 364 touchwin(tv); 365 wrefresh(tv); 366 } 367