xref: /original-bsd/games/worms/worms.c (revision 23c6a147)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1980 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)worms.c	5.7 (Berkeley) 08/02/89";
26 #endif /* not lint */
27 
28 /*
29  *
30  *	 @@@        @@@    @@@@@@@@@@     @@@@@@@@@@@    @@@@@@@@@@@@
31  *	 @@@        @@@   @@@@@@@@@@@@    @@@@@@@@@@@@   @@@@@@@@@@@@@
32  *	 @@@        @@@  @@@@      @@@@   @@@@           @@@@ @@@  @@@@
33  *	 @@@   @@   @@@  @@@        @@@   @@@            @@@  @@@   @@@
34  *	 @@@  @@@@  @@@  @@@        @@@   @@@            @@@  @@@   @@@
35  *	 @@@@ @@@@ @@@@  @@@        @@@   @@@            @@@  @@@   @@@
36  *	  @@@@@@@@@@@@   @@@@      @@@@   @@@            @@@  @@@   @@@
37  *	   @@@@  @@@@     @@@@@@@@@@@@    @@@            @@@  @@@   @@@
38  *	    @@    @@       @@@@@@@@@@     @@@            @@@  @@@   @@@
39  *
40  *				 Eric P. Scott
41  *			  Caltech High Energy Physics
42  *				 October, 1980
43  *
44  */
45 #include <sys/types.h>
46 #include <stdio.h>
47 #ifdef USG
48 #include <termio.h>
49 #else
50 #include <sgtty.h>
51 #endif
52 #include <signal.h>
53 
54 static struct options {
55 	int nopts;
56 	int opts[3];
57 }
58 	normal[8] = {
59 	{ 3, { 7, 0, 1 } },
60 	{ 3, { 0, 1, 2 } },
61 	{ 3, { 1, 2, 3 } },
62 	{ 3, { 2, 3, 4 } },
63 	{ 3, { 3, 4, 5 } },
64 	{ 3, { 4, 5, 6 } },
65 	{ 3, { 5, 6, 7 } },
66 	{ 3, { 6, 7, 0 } }
67 },	upper[8] = {
68 	{ 1, { 1, 0, 0 } },
69 	{ 2, { 1, 2, 0 } },
70 	{ 0, { 0, 0, 0 } },
71 	{ 0, { 0, 0, 0 } },
72 	{ 0, { 0, 0, 0 } },
73 	{ 2, { 4, 5, 0 } },
74 	{ 1, { 5, 0, 0 } },
75 	{ 2, { 1, 5, 0 } }
76 },
77 	left[8] = {
78 	{ 0, { 0, 0, 0 } },
79 	{ 0, { 0, 0, 0 } },
80 	{ 0, { 0, 0, 0 } },
81 	{ 2, { 2, 3, 0 } },
82 	{ 1, { 3, 0, 0 } },
83 	{ 2, { 3, 7, 0 } },
84 	{ 1, { 7, 0, 0 } },
85 	{ 2, { 7, 0, 0 } }
86 },
87 	right[8] = {
88 	{ 1, { 7, 0, 0 } },
89 	{ 2, { 3, 7, 0 } },
90 	{ 1, { 3, 0, 0 } },
91 	{ 2, { 3, 4, 0 } },
92 	{ 0, { 0, 0, 0 } },
93 	{ 0, { 0, 0, 0 } },
94 	{ 0, { 0, 0, 0 } },
95 	{ 2, { 6, 7, 0 } }
96 },
97 	lower[8] = {
98 	{ 0, { 0, 0, 0 } },
99 	{ 2, { 0, 1, 0 } },
100 	{ 1, { 1, 0, 0 } },
101 	{ 2, { 1, 5, 0 } },
102 	{ 1, { 5, 0, 0 } },
103 	{ 2, { 5, 6, 0 } },
104 	{ 0, { 0, 0, 0 } },
105 	{ 0, { 0, 0, 0 } }
106 },
107 	upleft[8] = {
108 	{ 0, { 0, 0, 0 } },
109 	{ 0, { 0, 0, 0 } },
110 	{ 0, { 0, 0, 0 } },
111 	{ 0, { 0, 0, 0 } },
112 	{ 0, { 0, 0, 0 } },
113 	{ 1, { 3, 0, 0 } },
114 	{ 2, { 1, 3, 0 } },
115 	{ 1, { 1, 0, 0 } }
116 },
117 	upright[8] = {
118 	{ 2, { 3, 5, 0 } },
119 	{ 1, { 3, 0, 0 } },
120 	{ 0, { 0, 0, 0 } },
121 	{ 0, { 0, 0, 0 } },
122 	{ 0, { 0, 0, 0 } },
123 	{ 0, { 0, 0, 0 } },
124 	{ 0, { 0, 0, 0 } },
125 	{ 1, { 5, 0, 0 } }
126 },
127 	lowleft[8] = {
128 	{ 3, { 7, 0, 1 } },
129 	{ 0, { 0, 0, 0 } },
130 	{ 0, { 0, 0, 0 } },
131 	{ 1, { 1, 0, 0 } },
132 	{ 2, { 1, 7, 0 } },
133 	{ 1, { 7, 0, 0 } },
134 	{ 0, { 0, 0, 0 } },
135 	{ 0, { 0, 0, 0 } }
136 },
137 	lowright[8] = {
138 	{ 0, { 0, 0, 0 } },
139 	{ 1, { 7, 0, 0 } },
140 	{ 2, { 5, 7, 0 } },
141 	{ 1, { 5, 0, 0 } },
142 	{ 0, { 0, 0, 0 } },
143 	{ 0, { 0, 0, 0 } },
144 	{ 0, { 0, 0, 0 } },
145 	{ 0, { 0, 0, 0 } }
146 };
147 
148 #define	cursor(c, r)	tputs(tgoto(CM, c, r), 1, fputchar)
149 
150 char *tcp;
151 int fputchar();
152 
153 static char	flavor[] = {
154 	'O', '*', '#', '$', '%', '0', '@', '~'
155 };
156 static 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 main(argc, argv)
167 	int argc;
168 	char **argv;
169 {
170 	extern int optind;
171 	extern short ospeed;
172 	extern char *optarg, *UP;
173 	register int x, y, h, n;
174 	register struct worm *w;
175 	register struct options *op;
176 	register short *ip;
177 	register char *term;
178 	int CO, IN, LI, last, bottom, ch, length, number, trail, Wrap;
179 	int onsig();
180 	short **ref;
181 	char *AL, *BC, *CM, *EI, *HO, *IC, *IM, *IP, *SR;
182 	char *field, tcb[100], *mp, *malloc(), *getenv(), *tgetstr(), *tgoto();
183 	long random();
184 #ifdef USG
185 	struct termio sg;
186 #else
187 	struct sgttyb sg;
188 #endif
189 
190 	length = 16;
191 	number = 3;
192 	trail = ' ';
193 	field = NULL;
194 	while ((ch = getopt(argc, argv, "fl:n:t")) != EOF)
195 		switch(ch) {
196 		case 'f':
197 			field = "WORM";
198 			break;
199 		case 'l':
200 			if ((length = atoi(optarg)) < 2 || length > 1024) {
201 				(void)fprintf(stderr,
202 				    "worms: invalid length (%d - %d).\n",
203 				     2, 1024);
204 				exit(1);
205 			}
206 			break;
207 		case 'n':
208 			if ((number = atoi(optarg)) < 1) {
209 				(void)fprintf(stderr,
210 				    "worms: invalid number of worms.\n");
211 				exit(1);
212 			}
213 			break;
214 		case 't':
215 			trail = '.';
216 			break;
217 		case '?':
218 		default:
219 			(void)fprintf(stderr,
220 			    "usage: worms [-ft] [-length #] [-number #]\n");
221 			exit(1);
222 		}
223 
224 	if (!(term = getenv("TERM"))) {
225 		(void)fprintf(stderr, "worms: no TERM environment variable.\n");
226 		exit(1);
227 	}
228 	if (!(worm = (struct worm *)malloc((u_int)number *
229 	    sizeof(struct worm))) || !(mp = malloc((u_int)1024)))
230 		nomem();
231 	if (tgetent(mp, term) <= 0) {
232 		(void)fprintf(stderr, "worms: %s: unknown terminal type.\n",
233 		    term);
234 		exit(1);
235 	}
236 	tcp = tcb;
237 	if (!(CM = tgetstr("cm", &tcp))) {
238 		(void)fprintf(stderr,
239 		    "worms: terminal incapable of cursor motion.\n");
240 		exit(1);
241 	}
242 	AL = tgetstr("al", &tcp);
243 	BC = tgetflag("bs") ? "\b" : tgetstr("bc", &tcp);
244 	if ((CO = tgetnum("co")) <= 0)
245 		CO = 80;
246 	last = CO - 1;
247 	EI = tgetstr("ei", &tcp);
248 	HO = tgetstr("ho", &tcp);
249 	IC = tgetstr("ic", &tcp);
250 	IM = tgetstr("im", &tcp);
251 	IN = tgetflag("in");
252 	IP = tgetstr("ip", &tcp);
253 	if ((LI = tgetnum("li")) <= 0)
254 		LI = 24;
255 	bottom = LI - 1;
256 	SR = tgetstr("sr", &tcp);
257 	UP = tgetstr("up", &tcp);
258 #ifdef USG
259 	ioctl(1, TCGETA, &sg);
260 	ospeed = sg.c_cflag&CBAUD;
261 #else
262 	gtty(1, &sg);
263 	ospeed = sg.sg_ospeed;
264 #endif
265 	Wrap = tgetflag("am");
266 	if (!(ip = (short *)malloc((u_int)(LI * CO * sizeof(short)))))
267 		nomem();
268 	if (!(ref = (short **)malloc((u_int)(LI * sizeof(short *)))))
269 		nomem();
270 	for (n = 0; n < LI; ++n) {
271 		ref[n] = ip;
272 		ip += CO;
273 	}
274 	for (ip = ref[0], n = LI * CO; --n >= 0;)
275 		*ip++ = 0;
276 	if (Wrap)
277 		ref[bottom][last] = 1;
278 	for (n = number, w = &worm[0]; --n >= 0; w++) {
279 		w->orientation = w->head = 0;
280 		if (!(ip = (short *)malloc((u_int)(length * sizeof(short)))))
281 			nomem();
282 		w->xpos = ip;
283 		for (x = length; --x >= 0;)
284 			*ip++ = -1;
285 		if (!(ip = (short *)malloc((u_int)(length * sizeof(short)))))
286 			nomem();
287 		w->ypos = ip;
288 		for (y = length; --y >= 0;)
289 			*ip++ = -1;
290 	}
291 
292 	(void)signal(SIGHUP, onsig);
293 	(void)signal(SIGINT, onsig);
294 	(void)signal(SIGQUIT, onsig);
295 	(void)signal(SIGSTOP, onsig);
296 	(void)signal(SIGTSTP, onsig);
297 	(void)signal(SIGTERM, onsig);
298 
299 	tputs(tgetstr("ti", &tcp), 1, fputchar);
300 	tputs(tgetstr("cl", &tcp), 1, fputchar);
301 	if (field) {
302 		register char *p = field;
303 
304 		for (y = bottom; --y >= 0;) {
305 			for (x = CO; --x >= 0;) {
306 				fputchar(*p++);
307 				if (!*p)
308 					p = field;
309 			}
310 			if (!Wrap)
311 				fputchar('\n');
312 			(void)fflush(stdout);
313 		}
314 		if (Wrap) {
315 			if (IM && !IN) {
316 				for (x = last; --x > 0;) {
317 					fputchar(*p++);
318 					if (!*p)
319 						p = field;
320 				}
321 				y = *p++;
322 				if (!*p)
323 					p = field;
324 				fputchar(*p);
325 				if (BC)
326 					tputs(BC, 1, fputchar);
327 				else
328 					cursor(last - 1, bottom);
329 				tputs(IM, 1, fputchar);
330 				if (IC)
331 					tputs(IC, 1, fputchar);
332 				fputchar(y);
333 				if (IP)
334 					tputs(IP, 1, fputchar);
335 				tputs(EI, 1, fputchar);
336 			}
337 			else if (SR || AL) {
338 				if (HO)
339 					tputs(HO, 1, fputchar);
340 				else
341 					cursor(0, 0);
342 				if (SR)
343 					tputs(SR, 1, fputchar);
344 				else
345 					tputs(AL, LI, fputchar);
346 				for (x = CO; --x >= 0;) {
347 					fputchar(*p++);
348 					if (!*p)
349 						p = field;
350 				}
351 			}
352 			else for (x = last; --x >= 0;) {
353 				fputchar(*p++);
354 				if (!*p)
355 					p = field;
356 			}
357 		}
358 		else for (x = CO; --x >= 0;) {
359 			fputchar(*p++);
360 			if (!*p)
361 				p = field;
362 		}
363 	}
364 	for (;;) {
365 		(void)fflush(stdout);
366 		for (n = 0, w = &worm[0]; n < number; n++, w++) {
367 			if ((x = w->xpos[h = w->head]) < 0) {
368 				cursor(x = w->xpos[h] = 0,
369 				     y = w->ypos[h] = bottom);
370 				fputchar(flavor[n % sizeof(flavor)]);
371 				ref[y][x]++;
372 			}
373 			else
374 				y = w->ypos[h];
375 			if (++h == length)
376 				h = 0;
377 			if (w->xpos[w->head = h] >= 0) {
378 				register int x1, y1;
379 
380 				x1 = w->xpos[h];
381 				y1 = w->ypos[h];
382 				if (--ref[y1][x1] == 0) {
383 					cursor(x1, y1);
384 					if (trail)
385 						fputchar(trail);
386 				}
387 			}
388 			op = &(!x ? (!y ? upleft : (y == bottom ? lowleft : left)) : (x == last ? (!y ? upright : (y == bottom ? lowright : right)) : (!y ? upper : (y == bottom ? lower : normal))))[w->orientation];
389 			switch (op->nopts) {
390 			case 0:
391 				(void)fflush(stdout);
392 				abort();
393 				return;
394 			case 1:
395 				w->orientation = op->opts[0];
396 				break;
397 			default:
398 				w->orientation =
399 				    op->opts[(int)random() % op->nopts];
400 			}
401 			cursor(x += xinc[w->orientation],
402 			    y += yinc[w->orientation]);
403 			if (!Wrap || x != last || y != bottom)
404 				fputchar(flavor[n % sizeof(flavor)]);
405 			ref[w->ypos[h] = y][w->xpos[h] = x]++;
406 		}
407 	}
408 }
409 
410 onsig()
411 {
412 	tputs(tgetstr("cl", &tcp), 1, fputchar);
413 	tputs(tgetstr("te", &tcp), 1, fputchar);
414 	exit(0);
415 }
416 
417 fputchar(c)
418 	char c;
419 {
420 	putchar(c);
421 }
422 
423 nomem()
424 {
425 	(void)fprintf(stderr, "worms: not enough memory.\n");
426 	exit(1);
427 }
428