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
main(int argc,char ** argv)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
life(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
display(struct body * pos,char chr)221 display(struct body *pos, char chr)
222 {
223 wmove(tv, pos->y, pos->x);
224 waddch(tv, chr);
225 }
226
227 void
leave(int dummy)228 leave(int dummy)
229 {
230 wantleave = 1;
231 }
232
233 int
rnd(int range)234 rnd(int range)
235 {
236 return arc4random_uniform(range);
237 }
238
239 void
newpos(struct body * bp)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
prize(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
process(int ch)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 *
newlink(void)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
crash(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
suspend(int dummy)399 suspend(int dummy)
400 {
401 wantsuspend = 1;
402 }
403
404 void
setup(void)405 setup(void)
406 {
407 clear();
408 refresh();
409 touchwin(stw);
410 wrefresh(stw);
411 touchwin(tv);
412 wrefresh(tv);
413 }
414