xref: /original-bsd/games/worms/worms.c (revision 2bb802fc)
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.5 (Berkeley) 06/27/88";
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 static char	*TE;
151 static 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[40];
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 		onsig();
180 	short **ref;
181 	char *AL, *BC, *CM, *EI, *HO, *IC, *IM, *IP, *SR, *tcp,
182 		*field, tcb[100], *mp, *malloc(), *getenv(), *tgetstr(),
183 		*tgoto();
184 	long random();
185 #ifdef USG
186 	struct termio sg;
187 #else
188 	struct sgttyb sg;
189 #endif
190 
191 	length = 16;
192 	number = 3;
193 	trail = ' ';
194 	field = NULL;
195 	while ((ch = getopt(argc, argv, "fl:n:t")) != EOF)
196 		switch((char)ch) {
197 		case 'f':
198 			field = "WORM";
199 			break;
200 		case 'l':
201 			if ((length = atoi(optarg)) < 2 || length > 1024) {
202 				fprintf(stderr, "%s: invalid length; range %d - %d.\n", *argv, 2, 1024);
203 				exit(1);
204 			}
205 			break;
206 		case 'n':
207 			if ((number = atoi(optarg)) < 1 || number > 40) {
208 				fprintf(stderr, "%s: invalid number of worms; range %d - %d.\n", *argv, 1, 40);
209 				exit(1);
210 			}
211 			break;
212 		case 't':
213 			trail = '.';
214 			break;
215 		case '?':
216 		default:
217 			fprintf(stderr, "usage: %s [-ft] [-length #] [-number #]\n", *argv);
218 			exit(1);
219 		}
220 
221 	if (!(term = getenv("TERM"))) {
222 		fprintf(stderr, "%s: TERM: parameter not set\n", *argv);
223 		exit(1);
224 	}
225 	if (!(mp = malloc((u_int)1024))) {
226 		fprintf(stderr, "%s: out of space.\n", *argv);
227 		exit(1);
228 	}
229 	if (tgetent(mp, term) <= 0) {
230 		fprintf(stderr, "%s: %s: unknown terminal type\n", *argv, term);
231 		exit(1);
232 	}
233 	tcp = tcb;
234 	if (!(CM = tgetstr("cm", &tcp))) {
235 		fprintf(stderr, "%s: terminal not capable of cursor motion\n", *argv);
236 		exit(1);
237 	}
238 	AL = tgetstr("al", &tcp);
239 	BC = tgetflag("bs") ? "\b" : tgetstr("bc", &tcp);
240 	if ((CO = tgetnum("co")) <= 0)
241 		CO = 80;
242 	last = CO - 1;
243 	EI = tgetstr("ei", &tcp);
244 	HO = tgetstr("ho", &tcp);
245 	IC = tgetstr("ic", &tcp);
246 	IM = tgetstr("im", &tcp);
247 	IN = tgetflag("in");
248 	IP = tgetstr("ip", &tcp);
249 	if ((LI = tgetnum("li")) <= 0)
250 		LI = 24;
251 	bottom = LI - 1;
252 	SR = tgetstr("sr", &tcp);
253 	TE = tgetstr("te", &tcp);
254 	UP = tgetstr("up", &tcp);
255 #ifdef USG
256 	ioctl(1, TCGETA, &sg);
257 	ospeed = sg.c_cflag&CBAUD;
258 #else
259 	gtty(1, &sg);
260 	ospeed = sg.sg_ospeed;
261 #endif
262 	Wrap = tgetflag("am");
263 	if (!(ip = (short *)malloc((u_int)(LI * CO * sizeof(short))))) {
264 		fprintf(stderr, "%s: out of memory\n", *argv);
265 		exit(1);
266 	}
267 	if (!(ref = (short **)malloc((u_int)(LI * sizeof(short *))))) {
268 		fprintf(stderr, "%s: out of memory\n", *argv);
269 		exit(1);
270 	}
271 	for (n = 0; n < LI; ++n) {
272 		ref[n] = ip;
273 		ip += CO;
274 	}
275 	for (ip = ref[0], n = LI * CO; --n >= 0;)
276 		*ip++ = 0;
277 	if (Wrap)
278 		ref[bottom][last] = 1;
279 	for (n = number, w = &worm[0]; --n >= 0; w++) {
280 		w->orientation = w->head = 0;
281 		if (!(ip = (short *)malloc((u_int)(length * sizeof(short))))) {
282 			fprintf(stderr, "%s: out of memory\n", *argv);
283 			exit(1);
284 		}
285 		w->xpos = ip;
286 		for (x = length; --x >= 0;)
287 			*ip++ = -1;
288 		if (!(ip = (short *)malloc((u_int)(length * sizeof(short))))) {
289 			fprintf(stderr, "%s: out of memory\n", *argv);
290 			exit(1);
291 		}
292 		w->ypos = ip;
293 		for (y = length; --y >= 0;)
294 			*ip++ = -1;
295 	}
296 
297 	(void)signal(SIGHUP, onsig);
298 	(void)signal(SIGINT, onsig);
299 	(void)signal(SIGQUIT, onsig);
300 	(void)signal(SIGSTOP, onsig);
301 	(void)signal(SIGTSTP, onsig);
302 	(void)signal(SIGTERM, onsig);
303 
304 	tputs(tgetstr("ti", &tcp), 1, fputchar);
305 	tputs(tgetstr("cl", &tcp), 1, fputchar);
306 	if (field) {
307 		register char *p = field;
308 
309 		for (y = bottom; --y >= 0;) {
310 			for (x = CO; --x >= 0;) {
311 				fputchar(*p++);
312 				if (!*p)
313 					p = field;
314 			}
315 			if (!Wrap)
316 				fputchar('\n');
317 			(void)fflush(stdout);
318 		}
319 		if (Wrap) {
320 			if (IM && !IN) {
321 				for (x = last; --x > 0;) {
322 					fputchar(*p++);
323 					if (!*p)
324 						p = field;
325 				}
326 				y = *p++;
327 				if (!*p)
328 					p = field;
329 				fputchar(*p);
330 				if (BC)
331 					tputs(BC, 1, fputchar);
332 				else
333 					cursor(last - 1, bottom);
334 				tputs(IM, 1, fputchar);
335 				if (IC)
336 					tputs(IC, 1, fputchar);
337 				fputchar(y);
338 				if (IP)
339 					tputs(IP, 1, fputchar);
340 				tputs(EI, 1, fputchar);
341 			}
342 			else if (SR || AL) {
343 				if (HO)
344 					tputs(HO, 1, fputchar);
345 				else
346 					cursor(0, 0);
347 				if (SR)
348 					tputs(SR, 1, fputchar);
349 				else
350 					tputs(AL, LI, fputchar);
351 				for (x = CO; --x >= 0;) {
352 					fputchar(*p++);
353 					if (!*p)
354 						p = field;
355 				}
356 			}
357 			else for (x = last; --x >= 0;) {
358 				fputchar(*p++);
359 				if (!*p)
360 					p = field;
361 			}
362 		}
363 		else for (x = CO; --x >= 0;) {
364 			fputchar(*p++);
365 			if (!*p)
366 				p = field;
367 		}
368 	}
369 	for (;;) {
370 		(void)fflush(stdout);
371 		for (n = 0, w = &worm[0]; n < number; n++, w++) {
372 			if ((x = w->xpos[h = w->head]) < 0) {
373 				cursor(x = w->xpos[h] = 0,
374 				     y = w->ypos[h] = bottom);
375 				fputchar(flavor[n % 6]);
376 				ref[y][x]++;
377 			}
378 			else
379 				y = w->ypos[h];
380 			if (++h == length)
381 				h = 0;
382 			if (w->xpos[w->head = h] >= 0) {
383 				register int x1, y1;
384 
385 				x1 = w->xpos[h];
386 				y1 = w->ypos[h];
387 				if (--ref[y1][x1] == 0) {
388 					cursor(x1, y1);
389 					if (trail)
390 						fputchar(trail);
391 				}
392 			}
393 			op = &(!x ? (!y ? upleft : (y == bottom ? lowleft : left)) : (x == last ? (!y ? upright : (y == bottom ? lowright : right)) : (!y ? upper : (y == bottom ? lower : normal))))[w->orientation];
394 			switch (op->nopts) {
395 			case 0:
396 				(void)fflush(stdout);
397 				abort();
398 				return;
399 			case 1:
400 				w->orientation = op->opts[0];
401 				break;
402 			default:
403 				w->orientation = op->opts[(int)random() % op->nopts];
404 			}
405 			cursor(x += xinc[w->orientation], y += yinc[w->orientation]);
406 			if (!Wrap || x != last || y != bottom)
407 				fputchar(flavor[n % 6]);
408 			ref[w->ypos[h] = y][w->xpos[h] = x]++;
409 		}
410 	}
411 }
412 
413 static
414 onsig()
415 {
416 	tputs(TE, 1, fputchar);
417 	exit(0);
418 }
419 
420 static
421 fputchar(c)
422 	char c;
423 {
424 	putchar(c);
425 }
426