xref: /netbsd/games/worm/worm.c (revision 6550d01e)
1 /*	$NetBSD: worm.c,v 1.29 2009/08/12 08:56:41 dholland 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 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
35  The Regents of the University of California.  All rights reserved.");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)worm.c	8.1 (Berkeley) 5/31/93";
41 #else
42 __RCSID("$NetBSD: worm.c,v 1.29 2009/08/12 08:56:41 dholland Exp $");
43 #endif
44 #endif /* not lint */
45 
46 /*
47  * Worm.  Written by Michael Toy
48  * UCSC
49  */
50 
51 #include <ctype.h>
52 #include <curses.h>
53 #include <err.h>
54 #include <signal.h>
55 #include <stdlib.h>
56 #include <termios.h>
57 #include <unistd.h>
58 
59 #define newlink() (struct body *) malloc(sizeof (struct body));
60 #define HEAD '@'
61 #define BODY 'o'
62 #define LENGTH 7
63 #define RUNLEN 8
64 #define CNTRL(p) (p-'A'+1)
65 
66 struct body {
67 	int x;
68 	int y;
69 	struct body *prev;
70 	struct body *next;
71 };
72 
73 static WINDOW *tv;
74 static WINDOW *stw;
75 static struct body *head, *tail, goody;
76 static int growing = 0;
77 static int running = 0;
78 static int slow = 0;
79 static int score = 0;
80 static int start_len = LENGTH;
81 static int visible_len;
82 static int lastch;
83 static char outbuf[BUFSIZ];
84 
85 int	main(int, char **);
86 static void crash(void) __dead;
87 static void display(const struct body *, char);
88 static void leave(int) __dead;
89 static void life(void);
90 static void newpos(struct body *);
91 static void process(int);
92 static void prize(void);
93 static int rnd(int);
94 static void setup(void);
95 static void wake(int);
96 
97 int
98 main(argc, argv)
99 	int argc;
100 	char **argv;
101 {
102 
103 	/* Revoke setgid privileges */
104 	setgid(getgid());
105 
106 	setbuf(stdout, outbuf);
107 	srand(getpid());
108 	signal(SIGALRM, wake);
109 	signal(SIGINT, leave);
110 	signal(SIGQUIT, leave);
111 	if (!initscr())
112 		errx(0, "couldn't initialize screen");
113 	cbreak();
114 	noecho();
115 #ifdef KEY_LEFT
116 	keypad(stdscr, TRUE);
117 #endif
118 	slow = (baudrate() <= 1200);
119 	clear();
120 	if (COLS < 18 || LINES < 5) {
121 		/*
122 		 * Insufficient room for the line with " Worm" and the
123 		 * score if fewer than 18 columns; insufficient room for
124 		 * anything much if fewer than 5 lines.
125 		 */
126 		endwin();
127 		errx(1, "screen too small");
128 	}
129 	if (argc == 2)
130 		start_len = atoi(argv[1]);
131 	if ((start_len <= 0) || (start_len > ((LINES-3) * (COLS-2)) / 3))
132 		start_len = LENGTH;
133 	stw = newwin(1, COLS-1, 0, 0);
134 	tv = newwin(LINES-1, COLS-1, 1, 0);
135 	box(tv, '*', '*');
136 	scrollok(tv, FALSE);
137 	scrollok(stw, FALSE);
138 	wmove(stw, 0, 0);
139 	wprintw(stw, " Worm");
140 	refresh();
141 	wrefresh(stw);
142 	wrefresh(tv);
143 	life();			/* Create the worm */
144 	prize();		/* Put up a goal */
145 	while(1)
146 	{
147 		if (running)
148 		{
149 			running--;
150 			process(lastch);
151 		}
152 		else
153 		{
154 		    fflush(stdout);
155 		    process(getch());
156 		}
157 	}
158 }
159 
160 static void
161 life()
162 {
163 	struct body *bp, *np;
164 	int i, j = 1;
165 
166 	np = NULL;
167 	head = newlink();
168 	if (head == NULL)
169 		err(1, NULL);
170 	head->x = start_len % (COLS-5) + 2;
171 	head->y = LINES / 2;
172 	head->next = NULL;
173 	display(head, HEAD);
174 	for (i = 0, bp = head; i < start_len; i++, bp = np) {
175 		np = newlink();
176 		if (np == NULL)
177 			err(1, NULL);
178 		np->next = bp;
179 		bp->prev = np;
180 		if (((bp->x <= 2) && (j == 1)) || ((bp->x >= COLS-4) && (j == -1))) {
181 			j *= -1;
182 			np->x = bp->x;
183 			np->y = bp->y + 1;
184 		} else {
185 			np->x = bp->x - j;
186 			np->y = bp->y;
187 		}
188 		display(np, BODY);
189 	}
190 	tail = np;
191 	tail->prev = NULL;
192 	visible_len = start_len + 1;
193 }
194 
195 static void
196 display(pos, chr)
197 	const struct body *pos;
198 	char chr;
199 {
200 	wmove(tv, pos->y, pos->x);
201 	waddch(tv, chr);
202 }
203 
204 static void
205 leave(dummy)
206 	int dummy;
207 {
208 	endwin();
209 
210 	if (dummy == 0){	/* called via crash() */
211 		printf("\nWell, you ran into something and the game is over.\n");
212 		printf("Your final score was %d\n\n", score);
213 	}
214 	exit(0);
215 }
216 
217 static void
218 wake(dummy)
219 	int dummy __unused;
220 {
221 	signal(SIGALRM, wake);
222 	fflush(stdout);
223 	process(lastch);
224 }
225 
226 static int
227 rnd(range)
228 	int range;
229 {
230 	return abs((rand()>>5)+(rand()>>5)) % range;
231 }
232 
233 static void
234 newpos(bp)
235 	struct body * bp;
236 {
237 	if (visible_len == (LINES-3) * (COLS-3) - 1) {
238 		endwin();
239 
240 		printf("\nYou won!\n");
241 		printf("Your final score was %d\n\n", score);
242 		exit(0);
243 	}
244 	do {
245 		bp->y = rnd(LINES-3)+ 1;
246 		bp->x = rnd(COLS-3) + 1;
247 		wmove(tv, bp->y, bp->x);
248 	} while(winch(tv) != ' ');
249 }
250 
251 static void
252 prize()
253 {
254 	int value;
255 
256 	value = rnd(9) + 1;
257 	newpos(&goody);
258 	waddch(tv, value+'0');
259 	wrefresh(tv);
260 }
261 
262 static void
263 process(ch)
264 	int ch;
265 {
266 	int x,y;
267 	struct body *nh;
268 
269 	alarm(0);
270 	x = head->x;
271 	y = head->y;
272 	switch(ch)
273 	{
274 #ifdef KEY_LEFT
275 		case KEY_LEFT:
276 #endif
277 		case 'h':
278 			x--; break;
279 
280 #ifdef KEY_DOWN
281 		case KEY_DOWN:
282 #endif
283 		case 'j':
284 			y++; break;
285 
286 #ifdef KEY_UP
287 		case KEY_UP:
288 #endif
289 		case 'k':
290 			y--; break;
291 
292 #ifdef KEY_RIGHT
293 		case KEY_RIGHT:
294 #endif
295 		case 'l':
296 			x++; break;
297 
298 		case 'H': x--; running = RUNLEN; ch = tolower(ch); break;
299 		case 'J': y++; running = RUNLEN/2; ch = tolower(ch); break;
300 		case 'K': y--; running = RUNLEN/2; ch = tolower(ch); break;
301 		case 'L': x++; running = RUNLEN; ch = tolower(ch); break;
302 		case '\f': setup(); return;
303 
304 		case ERR:
305 		case CNTRL('C'):
306 		case CNTRL('D'):
307 			crash();
308 			return;
309 
310 		default: if (! running) alarm(1);
311 			   return;
312 	}
313 	lastch = ch;
314 	if (growing == 0)
315 	{
316 		display(tail, ' ');
317 		tail->next->prev = NULL;
318 		nh = tail->next;
319 		free(tail);
320 		tail = nh;
321 		visible_len--;
322 	}
323 	else growing--;
324 	display(head, BODY);
325 	wmove(tv, y, x);
326 	if (isdigit(ch = winch(tv)))
327 	{
328 		growing += ch-'0';
329 		prize();
330 		score += growing;
331 		running = 0;
332 		wmove(stw, 0, COLS - 12);
333 		wprintw(stw, "Score: %3d", score);
334 		wrefresh(stw);
335 	}
336 	else if(ch != ' ') crash();
337 	nh = newlink();
338 	if (nh == NULL)
339 		err(1, NULL);
340 	nh->next = NULL;
341 	nh->prev = head;
342 	head->next = nh;
343 	nh->y = y;
344 	nh->x = x;
345 	display(nh, HEAD);
346 	head = nh;
347 	visible_len++;
348 	if (!(slow && running))
349 	{
350 		wmove(tv, head->y, head->x);
351 		wrefresh(tv);
352 	}
353 	if (!running)
354 		alarm(1);
355 }
356 
357 static void
358 crash()
359 {
360 	leave(0);
361 }
362 
363 static void
364 setup()
365 {
366 	clear();
367 	refresh();
368 	touchwin(stw);
369 	wrefresh(stw);
370 	touchwin(tv);
371 	wrefresh(tv);
372 	alarm(1);
373 }
374