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