xref: /openbsd/games/worms/worms.c (revision 17df1aa7)
1 /*	$OpenBSD: worms.c,v 1.19 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  *
34  *	 @@@	    @@@	   @@@@@@@@@@	  @@@@@@@@@@@	 @@@@@@@@@@@@
35  *	 @@@	    @@@	  @@@@@@@@@@@@	  @@@@@@@@@@@@	 @@@@@@@@@@@@@
36  *	 @@@	    @@@	 @@@@	   @@@@	  @@@@		 @@@@ @@@  @@@@
37  *	 @@@   @@   @@@	 @@@	    @@@	  @@@		 @@@  @@@   @@@
38  *	 @@@  @@@@  @@@	 @@@	    @@@	  @@@		 @@@  @@@   @@@
39  *	 @@@@ @@@@ @@@@	 @@@	    @@@	  @@@		 @@@  @@@   @@@
40  *	  @@@@@@@@@@@@	 @@@@	   @@@@	  @@@		 @@@  @@@   @@@
41  *	   @@@@	 @@@@	  @@@@@@@@@@@@	  @@@		 @@@  @@@   @@@
42  *	    @@	  @@	   @@@@@@@@@@	  @@@		 @@@  @@@   @@@
43  *
44  *				 Eric P. Scott
45  *			  Caltech High Energy Physics
46  *				 October, 1980
47  *
48  */
49 #include <sys/types.h>
50 
51 #include <curses.h>
52 #include <err.h>
53 #include <signal.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <termios.h>
57 #include <unistd.h>
58 
59 static const struct options {
60 	int nopts;
61 	int opts[3];
62 }
63 	normal[8] = {
64 	{ 3, { 7, 0, 1 } },
65 	{ 3, { 0, 1, 2 } },
66 	{ 3, { 1, 2, 3 } },
67 	{ 3, { 2, 3, 4 } },
68 	{ 3, { 3, 4, 5 } },
69 	{ 3, { 4, 5, 6 } },
70 	{ 3, { 5, 6, 7 } },
71 	{ 3, { 6, 7, 0 } }
72 },	upper[8] = {
73 	{ 1, { 1, 0, 0 } },
74 	{ 2, { 1, 2, 0 } },
75 	{ 0, { 0, 0, 0 } },
76 	{ 0, { 0, 0, 0 } },
77 	{ 0, { 0, 0, 0 } },
78 	{ 2, { 4, 5, 0 } },
79 	{ 1, { 5, 0, 0 } },
80 	{ 2, { 1, 5, 0 } }
81 },
82 	left[8] = {
83 	{ 0, { 0, 0, 0 } },
84 	{ 0, { 0, 0, 0 } },
85 	{ 0, { 0, 0, 0 } },
86 	{ 2, { 2, 3, 0 } },
87 	{ 1, { 3, 0, 0 } },
88 	{ 2, { 3, 7, 0 } },
89 	{ 1, { 7, 0, 0 } },
90 	{ 2, { 7, 0, 0 } }
91 },
92 	right[8] = {
93 	{ 1, { 7, 0, 0 } },
94 	{ 2, { 3, 7, 0 } },
95 	{ 1, { 3, 0, 0 } },
96 	{ 2, { 3, 4, 0 } },
97 	{ 0, { 0, 0, 0 } },
98 	{ 0, { 0, 0, 0 } },
99 	{ 0, { 0, 0, 0 } },
100 	{ 2, { 6, 7, 0 } }
101 },
102 	lower[8] = {
103 	{ 0, { 0, 0, 0 } },
104 	{ 2, { 0, 1, 0 } },
105 	{ 1, { 1, 0, 0 } },
106 	{ 2, { 1, 5, 0 } },
107 	{ 1, { 5, 0, 0 } },
108 	{ 2, { 5, 6, 0 } },
109 	{ 0, { 0, 0, 0 } },
110 	{ 0, { 0, 0, 0 } }
111 },
112 	upleft[8] = {
113 	{ 0, { 0, 0, 0 } },
114 	{ 0, { 0, 0, 0 } },
115 	{ 0, { 0, 0, 0 } },
116 	{ 0, { 0, 0, 0 } },
117 	{ 0, { 0, 0, 0 } },
118 	{ 1, { 3, 0, 0 } },
119 	{ 2, { 1, 3, 0 } },
120 	{ 1, { 1, 0, 0 } }
121 },
122 	upright[8] = {
123 	{ 2, { 3, 5, 0 } },
124 	{ 1, { 3, 0, 0 } },
125 	{ 0, { 0, 0, 0 } },
126 	{ 0, { 0, 0, 0 } },
127 	{ 0, { 0, 0, 0 } },
128 	{ 0, { 0, 0, 0 } },
129 	{ 0, { 0, 0, 0 } },
130 	{ 1, { 5, 0, 0 } }
131 },
132 	lowleft[8] = {
133 	{ 3, { 7, 0, 1 } },
134 	{ 0, { 0, 0, 0 } },
135 	{ 0, { 0, 0, 0 } },
136 	{ 1, { 1, 0, 0 } },
137 	{ 2, { 1, 7, 0 } },
138 	{ 1, { 7, 0, 0 } },
139 	{ 0, { 0, 0, 0 } },
140 	{ 0, { 0, 0, 0 } }
141 },
142 	lowright[8] = {
143 	{ 0, { 0, 0, 0 } },
144 	{ 1, { 7, 0, 0 } },
145 	{ 2, { 5, 7, 0 } },
146 	{ 1, { 5, 0, 0 } },
147 	{ 0, { 0, 0, 0 } },
148 	{ 0, { 0, 0, 0 } },
149 	{ 0, { 0, 0, 0 } },
150 	{ 0, { 0, 0, 0 } }
151 };
152 
153 static const char	flavor[] = {
154 	'O', '*', '#', '$', '%', '0', '@', '~'
155 };
156 static const short	xinc[] = {
157 	1,  1,  1,  0, -1, -1, -1,  0
158 }, yinc[] = {
159 	-1,  0,  1,  1,  1,  0, -1, -1
160 };
161 static struct	worm {
162 	int orientation, head;
163 	short *xpos, *ypos;
164 } *worm;
165 
166 volatile sig_atomic_t sig_caught = 0;
167 
168 void	 nomem(void);
169 void	 onsig(int);
170 
171 int
172 main(int argc, char *argv[])
173 {
174 	int x, y, h, n;
175 	struct worm *w;
176 	const struct options *op;
177 	short *ip;
178 	int CO, LI, last, bottom, ch, length, number, trail;
179 	short **ref;
180 	const char *field;
181 	struct termios term;
182 	speed_t speed;
183 	u_int delay = 0;
184 
185 	/* set default delay based on terminal baud rate */
186 	if (tcgetattr(STDOUT_FILENO, &term) == 0 &&
187 	    (speed = cfgetospeed(&term)) > B9600)
188 		delay = (speed / B9600) - 1;
189 
190 	length = 16;
191 	number = 3;
192 	trail = ' ';
193 	field = NULL;
194 	while ((ch = getopt(argc, argv, "d:fhl:n:t")) != -1)
195 		switch(ch) {
196 		case 'd':
197 			if ((delay = (u_int)strtoul(optarg, (char **)NULL, 10)) < 1
198 			    || delay > 1000)
199 				errx(1, "invalid delay (1-1000)");
200 			delay *= 1000;  /* ms -> us */
201 			break;
202 		case 'f':
203 			field = "WORM";
204 			break;
205 		case 'l':
206 			if ((length = atoi(optarg)) < 2 || length > 1024)
207 				errx(1, "invalid length (%d - %d).", 2, 1024);
208 			break;
209 		case 'n':
210 			if ((number = atoi(optarg)) < 1 || number > 100)
211 				errx(1, "invalid number of worms (%d - %d).",
212 				    1, 100);
213 			break;
214 		case 't':
215 			trail = '.';
216 			break;
217 		case '?': case 'h':
218 		default:
219 			(void)fprintf(stderr,
220 			    "usage: worms [-ft] [-d delay] [-l length] [-n number]\n");
221 			exit(1);
222 		}
223 
224 	srandomdev();
225 	if (!(worm = calloc((size_t)number, sizeof(struct worm))))
226 		nomem();
227 	initscr();
228 	curs_set(0);
229 	CO = COLS;
230 	LI = LINES;
231 	last = CO - 1;
232 	bottom = LI - 1;
233 	if (!(ip = malloc((size_t)(LI * CO * sizeof(short)))) ||
234 	    !(ref = calloc((size_t)LI, sizeof(short *)))) {
235 		endwin();
236 		nomem();
237 	}
238 	for (n = 0; n < LI; ++n) {
239 		ref[n] = ip;
240 		ip += CO;
241 	}
242 	for (ip = ref[0], n = LI * CO; --n >= 0;)
243 		*ip++ = 0;
244 	for (n = number, w = &worm[0]; --n >= 0; w++) {
245 		w->orientation = w->head = 0;
246 		if (!(ip = calloc((size_t)length, sizeof(short)))) {
247 			endwin();
248 			nomem();
249 		}
250 		w->xpos = ip;
251 		for (x = length; --x >= 0;)
252 			*ip++ = -1;
253 		if (!(ip = calloc((size_t)length, sizeof(short)))) {
254 			endwin();
255 			nomem();
256 		}
257 		w->ypos = ip;
258 		for (y = length; --y >= 0;)
259 			*ip++ = -1;
260 	}
261 
262 	(void)signal(SIGHUP, onsig);
263 	(void)signal(SIGINT, onsig);
264 	(void)signal(SIGQUIT, onsig);
265 	(void)signal(SIGSTOP, onsig);
266 	(void)signal(SIGTSTP, onsig);
267 	(void)signal(SIGTERM, onsig);
268 
269 	if (field) {
270 		const char *p = field;
271 
272 		for (y = LI; --y >= 0;) {
273 			for (x = CO; --x >= 0;) {
274 				addch(*p++);
275 				if (!*p)
276 					p = field;
277 			}
278 			refresh();
279 		}
280 	}
281 	for (;;) {
282 		refresh();
283 		if (sig_caught) {
284 			endwin();
285 			exit(0);
286 		}
287 		if (delay) usleep(delay);
288 		for (n = 0, w = &worm[0]; n < number; n++, w++) {
289 			if ((x = w->xpos[h = w->head]) < 0) {
290 				mvaddch(y = w->ypos[h] = bottom,
291 				    x = w->xpos[h] = 0,
292 				    flavor[n % sizeof(flavor)]);
293 				ref[y][x]++;
294 			}
295 			else
296 				y = w->ypos[h];
297 			if (++h == length)
298 				h = 0;
299 			if (w->xpos[w->head = h] >= 0) {
300 				int x1, y1;
301 
302 				x1 = w->xpos[h];
303 				y1 = w->ypos[h];
304 				if (--ref[y1][x1] == 0)
305 					mvaddch(y1, x1, trail);
306 			}
307 			op = &(!x ? (!y ? upleft : (y == bottom ? lowleft : left)) : (x == last ? (!y ? upright : (y == bottom ? lowright : right)) : (!y ? upper : (y == bottom ? lower : normal))))[w->orientation];
308 			switch (op->nopts) {
309 			case 0:
310 				endwin();
311 				return(1);
312 			case 1:
313 				w->orientation = op->opts[0];
314 				break;
315 			default:
316 				w->orientation =
317 				    op->opts[(int)random() % op->nopts];
318 			}
319 			mvaddch(y += yinc[w->orientation],
320 			    x += xinc[w->orientation],
321 			    flavor[n % sizeof(flavor)]);
322 			ref[w->ypos[h] = y][w->xpos[h] = x]++;
323 		}
324 	}
325 }
326 
327 void
328 onsig(int signo)
329 {
330 	sig_caught = 1;
331 }
332 
333 void
334 nomem(void)
335 {
336 	errx(1, "not enough memory.");
337 }
338