xref: /netbsd/games/hunt/hunt/otto.c (revision ca5d861d)
1 /*	$NetBSD: otto.c,v 1.18 2014/03/30 05:14:47 dholland Exp $	*/
2 #ifdef OTTO
3 /*
4  * Copyright (c) 1983-2003, Regents of the University of California.
5  * 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 are
9  * met:
10  *
11  * + Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * + Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in the
15  *   documentation and/or other materials provided with the distribution.
16  * + Neither the name of the University of California, San Francisco nor
17  *   the names of its contributors may be used to endorse or promote
18  *   products derived from this software without specific prior written
19  *   permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  *	otto	- a hunt otto-matic player
36  *
37  *		This guy is buggy, unfair, stupid, and not extensible.
38  *	Future versions of hunt will have a subroutine library for
39  *	automatic players to link to.  If you write your own "otto"
40  *	please let us know what subroutines you would expect in the
41  *	subroutine library.
42  *
43  *	Id: otto.c,v 1.14 2003/04/16 06:11:54 gregc Exp
44  */
45 
46 #include <sys/cdefs.h>
47 #ifndef lint
48 __RCSID("$NetBSD: otto.c,v 1.18 2014/03/30 05:14:47 dholland Exp $");
49 #endif /* not lint */
50 
51 #include <sys/time.h>
52 #include <curses.h>
53 #include <ctype.h>
54 #include <signal.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58 
59 #include "hunt_common.h"
60 #include "hunt_private.h"
61 
62 #undef WALL
63 #undef NORTH
64 #undef SOUTH
65 #undef WEST
66 #undef EAST
67 #undef FRONT
68 #undef LEFT
69 #undef BACK
70 #undef RIGHT
71 
72 #ifdef HPUX
73 #define random rand
74 #endif
75 
76 #define SCREEN(y, x)	mvinch(y, x)
77 
78 #ifndef DEBUG
79 #define STATIC		static
80 #else
81 #define STATIC
82 #endif
83 
84 #define OPPONENT	"{}i!"
85 #define PROPONENT	"^v<>"
86 #define WALL		"+\\/#*-|"
87 #define PUSHOVER	" bg;*#&"
88 #define SHOTS		"$@Oo:"
89 
90 /* number of "directions" */
91 #define NUMDIRECTIONS	4
92 
93 /* absolute directions (facings) - counterclockwise */
94 #define NORTH		0
95 #define WEST		1
96 #define SOUTH		2
97 #define EAST		3
98 #define ALLDIRS		0xf
99 
100 /* relative directions - counterclockwise */
101 #define FRONT		0
102 #define LEFT		1
103 #define BACK		2
104 #define RIGHT		3
105 
106 #define ABSCHARS	"NWSE"
107 #define RELCHARS	"FLBR"
108 #define DIRKEYS		"khjl"
109 
110 STATIC char command[BUFSIZ];
111 STATIC int comlen;
112 
113 #ifdef DEBUG
114 STATIC FILE *debug = NULL;
115 #endif
116 
117 #define DEADEND		0x1
118 #define ON_LEFT		0x2
119 #define ON_RIGHT	0x4
120 #define ON_SIDE		(ON_LEFT|ON_RIGHT)
121 #define BEEN		0x8
122 #define BEEN_SAME	0x10
123 
124 struct item {
125 	char what;
126 	int distance;
127 	int flags;
128 };
129 
130 STATIC struct item flbr[NUMDIRECTIONS];
131 
132 #define fitem flbr[FRONT]
133 #define litem flbr[LEFT]
134 #define bitem flbr[BACK]
135 #define ritem flbr[RIGHT]
136 
137 STATIC int facing;
138 STATIC int row, col;
139 STATIC int num_turns;			/* for wandering */
140 STATIC char been_there[HEIGHT][WIDTH2];
141 STATIC struct itimerval	pause_time = { { 0, 0 }, { 0, 55000 }};
142 
143 STATIC void attack(int, struct item *);
144 STATIC void duck(int);
145 STATIC void face_and_move_direction(int, int);
146 STATIC bool go_for_ammo(char);
147 STATIC void ottolook(int, struct item *);
148 STATIC void look_around(void);
149 STATIC void nothing(int);
150 STATIC int stop_look(struct item *, char, int, int);
151 STATIC void wander(void);
152 
153 extern int Otto_count;
154 
155 STATIC void
nothing(int dummy __unused)156 nothing(int dummy __unused)
157 {
158 }
159 
160 void
otto(int y,int x,char face)161 otto(int y, int x, char face)
162 {
163 	int i;
164 	int old_mask;
165 
166 #ifdef DEBUG
167 	if (debug == NULL) {
168 		debug = fopen("bug", "w");
169 		setbuf(debug, NULL);
170 	}
171 	fprintf(debug, "\n%c(%d,%d)", face, y, x);
172 #endif
173 	(void) signal(SIGALRM, nothing);
174 	old_mask = sigblock(sigmask(SIGALRM));
175 	setitimer(ITIMER_REAL, &pause_time, NULL);
176 	sigpause(old_mask);
177 	sigsetmask(old_mask);
178 
179 	/* save away parameters so other functions may use/update info */
180 	switch (face) {
181 	case '^': facing = NORTH; break;
182 	case '<': facing = WEST; break;
183 	case 'v': facing = SOUTH; break;
184 	case '>': facing = EAST; break;
185 	default: abort();
186 	}
187 	row = y; col = x;
188 	been_there[row][col] |= 1 << facing;
189 
190 	/* initially no commands to be sent */
191 	comlen = 0;
192 
193 	/* find something to do */
194 	look_around();
195 	for (i = 0; i < NUMDIRECTIONS; i++) {
196 		if (strchr(OPPONENT, flbr[i].what) != NULL) {
197 			attack(i, &flbr[i]);
198 			memset(been_there, 0, sizeof been_there);
199 			goto done;
200 		}
201 	}
202 
203 	if (strchr(SHOTS, bitem.what) != NULL && !(bitem.what & ON_SIDE)) {
204 		duck(BACK);
205 		memset(been_there, 0, sizeof been_there);
206 #ifdef BOOTS
207 	} else if (go_for_ammo(BOOT_PAIR)) {
208 		memset(been_there, 0, sizeof been_there);
209 	} else if (go_for_ammo(BOOT)) {
210 		memset(been_there, 0, sizeof been_there);
211 #endif
212 	} else if (go_for_ammo(GMINE))
213 		memset(been_there, 0, sizeof been_there);
214 	else if (go_for_ammo(MINE))
215 		memset(been_there, 0, sizeof been_there);
216 	else
217 		wander();
218 
219 done:
220 	(void) write(huntsocket, command, comlen);
221 	Otto_count += comlen;
222 #ifdef	DEBUG
223 	(void) fwrite(command, 1, comlen, debug);
224 #endif
225 }
226 
227 #define direction(abs,rel)	(((abs) + (rel)) % NUMDIRECTIONS)
228 
229 STATIC int
stop_look(struct item * itemp,char c,int dist,int side)230 stop_look(struct item *itemp, char c, int dist, int side)
231 {
232 	switch (c) {
233 
234 	case SPACE:
235 		if (side)
236 			itemp->flags &= ~DEADEND;
237 		return 0;
238 
239 	case MINE:
240 	case GMINE:
241 #ifdef BOOTS
242 	case BOOT:
243 	case BOOT_PAIR:
244 #endif
245 		if (itemp->distance == -1) {
246 			itemp->distance = dist;
247 			itemp->what = c;
248 			if (side < 0)
249 				itemp->flags |= ON_LEFT;
250 			else if (side > 0)
251 				itemp->flags |= ON_RIGHT;
252 		}
253 		return 0;
254 
255 	case SHOT:
256 	case GRENADE:
257 	case SATCHEL:
258 	case BOMB:
259 #ifdef OOZE
260 	case SLIME:
261 #endif
262 		if (itemp->distance == -1 || (!side
263 		    && (itemp->flags & ON_SIDE
264 		    || itemp->what == GMINE || itemp->what == MINE))) {
265 			itemp->distance = dist;
266 			itemp->what = c;
267 			itemp->flags &= ~ON_SIDE;
268 			if (side < 0)
269 				itemp->flags |= ON_LEFT;
270 			else if (side > 0)
271 				itemp->flags |= ON_RIGHT;
272 		}
273 		return 0;
274 
275 	case '{':
276 	case '}':
277 	case 'i':
278 	case '!':
279 		itemp->distance = dist;
280 		itemp->what = c;
281 		itemp->flags &= ~(ON_SIDE|DEADEND);
282 		if (side < 0)
283 			itemp->flags |= ON_LEFT;
284 		else if (side > 0)
285 			itemp->flags |= ON_RIGHT;
286 		return 1;
287 
288 	default:
289 		/* a wall or unknown object */
290 		if (side)
291 			return 0;
292 		if (itemp->distance == -1) {
293 			itemp->distance = dist;
294 			itemp->what = c;
295 		}
296 		return 1;
297 	}
298 }
299 
300 STATIC void
ottolook(int rel_dir,struct item * itemp)301 ottolook(int rel_dir, struct item *itemp)
302 {
303 	int r, c;
304 	char ch;
305 
306 	r = 0;
307 	itemp->what = 0;
308 	itemp->distance = -1;
309 	itemp->flags = DEADEND|BEEN;		/* true until proven false */
310 
311 	switch (direction(facing, rel_dir)) {
312 
313 	case NORTH:
314 		if (been_there[row - 1][col] & NORTH)
315 			itemp->flags |= BEEN_SAME;
316 		for (r = row - 1; r >= 0; r--)
317 			for (c = col - 1; c < col + 2; c++) {
318 				ch = SCREEN(r, c);
319 				if (stop_look(itemp, ch, row - r, c - col))
320 					goto cont_north;
321 				if (c == col && !been_there[r][c])
322 					itemp->flags &= ~BEEN;
323 			}
324 	cont_north:
325 		if (itemp->flags & DEADEND) {
326 			itemp->flags |= BEEN;
327 			if (r >= 0)
328 				been_there[r][col] |= NORTH;
329 			for (r = row - 1; r > row - itemp->distance; r--)
330 				been_there[r][col] = ALLDIRS;
331 		}
332 		break;
333 
334 	case SOUTH:
335 		if (been_there[row + 1][col] & SOUTH)
336 			itemp->flags |= BEEN_SAME;
337 		for (r = row + 1; r < HEIGHT; r++)
338 			for (c = col - 1; c < col + 2; c++) {
339 				ch = SCREEN(r, c);
340 				if (stop_look(itemp, ch, r - row, col - c))
341 					goto cont_south;
342 				if (c == col && !been_there[r][c])
343 					itemp->flags &= ~BEEN;
344 			}
345 	cont_south:
346 		if (itemp->flags & DEADEND) {
347 			itemp->flags |= BEEN;
348 			if (r < HEIGHT)
349 				been_there[r][col] |= SOUTH;
350 			for (r = row + 1; r < row + itemp->distance; r++)
351 				been_there[r][col] = ALLDIRS;
352 		}
353 		break;
354 
355 	case WEST:
356 		if (been_there[row][col - 1] & WEST)
357 			itemp->flags |= BEEN_SAME;
358 		for (c = col - 1; c >= 0; c--)
359 			for (r = row - 1; r < row + 2; r++) {
360 				ch = SCREEN(r, c);
361 				if (stop_look(itemp, ch, col - c, row - r))
362 					goto cont_west;
363 				if (r == row && !been_there[r][c])
364 					itemp->flags &= ~BEEN;
365 			}
366 	cont_west:
367 		if (itemp->flags & DEADEND) {
368 			itemp->flags |= BEEN;
369 			been_there[r][col] |= WEST;
370 			for (c = col - 1; c > col - itemp->distance; c--)
371 				been_there[row][c] = ALLDIRS;
372 		}
373 		break;
374 
375 	case EAST:
376 		if (been_there[row][col + 1] & EAST)
377 			itemp->flags |= BEEN_SAME;
378 		for (c = col + 1; c < WIDTH; c++)
379 			for (r = row - 1; r < row + 2; r++) {
380 				ch = SCREEN(r, c);
381 				if (stop_look(itemp, ch, c - col, r - row))
382 					goto cont_east;
383 				if (r == row && !been_there[r][c])
384 					itemp->flags &= ~BEEN;
385 			}
386 	cont_east:
387 		if (itemp->flags & DEADEND) {
388 			itemp->flags |= BEEN;
389 			been_there[r][col] |= EAST;
390 			for (c = col + 1; c < col + itemp->distance; c++)
391 				been_there[row][c] = ALLDIRS;
392 		}
393 		break;
394 
395 	default:
396 		abort();
397 	}
398 }
399 
400 STATIC void
look_around(void)401 look_around(void)
402 {
403 	int i;
404 
405 	for (i = 0; i < NUMDIRECTIONS; i++) {
406 		ottolook(i, &flbr[i]);
407 #ifdef DEBUG
408 		fprintf(debug, " ottolook(%c)=%c(%d)(0x%x)",
409 			RELCHARS[i], flbr[i].what, flbr[i].distance, flbr[i].flags);
410 #endif
411 	}
412 }
413 
414 /*
415  * as a side effect modifies facing and location (row, col)
416  */
417 
418 STATIC void
face_and_move_direction(int rel_dir,int distance)419 face_and_move_direction(int rel_dir, int distance)
420 {
421 	int old_facing;
422 	char cmd;
423 
424 	old_facing = facing;
425 	cmd = DIRKEYS[facing = direction(facing, rel_dir)];
426 
427 	if (rel_dir != FRONT) {
428 		int i;
429 		struct item items[NUMDIRECTIONS];
430 
431 		command[comlen++] = toupper((unsigned char)cmd);
432 		if (distance == 0) {
433 			/* rotate ottolook's to be in right position */
434 			for (i = 0; i < NUMDIRECTIONS; i++)
435 				items[i] =
436 					flbr[(i + old_facing) % NUMDIRECTIONS];
437 			memcpy(flbr, items, sizeof flbr);
438 		}
439 	}
440 	while (distance--) {
441 		command[comlen++] = cmd;
442 		switch (facing) {
443 
444 		case NORTH: row--; break;
445 		case WEST:  col--; break;
446 		case SOUTH: row++; break;
447 		case EAST:  col++; break;
448 		}
449 		if (distance == 0)
450 			look_around();
451 	}
452 }
453 
454 STATIC void
attack(int rel_dir,struct item * itemp)455 attack(int rel_dir, struct item *itemp)
456 {
457 	if (!(itemp->flags & ON_SIDE)) {
458 		face_and_move_direction(rel_dir, 0);
459 		command[comlen++] = 'o';
460 		command[comlen++] = 'o';
461 		duck(FRONT);
462 		command[comlen++] = ' ';
463 	} else if (itemp->distance > 1) {
464 		face_and_move_direction(rel_dir, 2);
465 		duck(FRONT);
466 	} else {
467 		face_and_move_direction(rel_dir, 1);
468 		if (itemp->flags & ON_LEFT)
469 			rel_dir = LEFT;
470 		else
471 			rel_dir = RIGHT;
472 		(void) face_and_move_direction(rel_dir, 0);
473 		command[comlen++] = 'f';
474 		command[comlen++] = 'f';
475 		duck(FRONT);
476 		command[comlen++] = ' ';
477 	}
478 }
479 
480 STATIC void
duck(int rel_dir)481 duck(int rel_dir)
482 {
483 	int dir;
484 
485 	switch (dir = direction(facing, rel_dir)) {
486 
487 	case NORTH:
488 	case SOUTH:
489 		if (strchr(PUSHOVER, SCREEN(row, col - 1)) != NULL)
490 			command[comlen++] = 'h';
491 		else if (strchr(PUSHOVER, SCREEN(row, col + 1)) != NULL)
492 			command[comlen++] = 'l';
493 		else if (dir == NORTH
494 			&& strchr(PUSHOVER, SCREEN(row + 1, col)) != NULL)
495 				command[comlen++] = 'j';
496 		else if (dir == SOUTH
497 			&& strchr(PUSHOVER, SCREEN(row - 1, col)) != NULL)
498 				command[comlen++] = 'k';
499 		else if (dir == NORTH)
500 			command[comlen++] = 'k';
501 		else
502 			command[comlen++] = 'j';
503 		break;
504 
505 	case WEST:
506 	case EAST:
507 		if (strchr(PUSHOVER, SCREEN(row - 1, col)) != NULL)
508 			command[comlen++] = 'k';
509 		else if (strchr(PUSHOVER, SCREEN(row + 1, col)) != NULL)
510 			command[comlen++] = 'j';
511 		else if (dir == WEST
512 			&& strchr(PUSHOVER, SCREEN(row, col + 1)) != NULL)
513 				command[comlen++] = 'l';
514 		else if (dir == EAST
515 			&& strchr(PUSHOVER, SCREEN(row, col - 1)) != NULL)
516 				command[comlen++] = 'h';
517 		else if (dir == WEST)
518 			command[comlen++] = 'h';
519 		else
520 			command[comlen++] = 'l';
521 		break;
522 	}
523 }
524 
525 /*
526  * go for the closest mine if possible
527  */
528 
529 STATIC bool
go_for_ammo(char mine)530 go_for_ammo(char mine)
531 {
532 	int i, rel_dir, dist;
533 
534 	rel_dir = -1;
535 	dist = WIDTH;
536 	for (i = 0; i < NUMDIRECTIONS; i++) {
537 		if (flbr[i].what == mine && flbr[i].distance < dist) {
538 			rel_dir = i;
539 			dist = flbr[i].distance;
540 		}
541 	}
542 	if (rel_dir == -1)
543 		return false;
544 
545 	if (!(flbr[rel_dir].flags & ON_SIDE)
546 	|| flbr[rel_dir].distance > 1) {
547 		if (dist > 4)
548 			dist = 4;
549 		face_and_move_direction(rel_dir, dist);
550 	} else
551 		return false;		/* until it's done right */
552 	return true;
553 }
554 
555 STATIC void
wander(void)556 wander(void)
557 {
558 	int i, j, rel_dir, dir_mask, dir_count;
559 
560 	for (i = 0; i < NUMDIRECTIONS; i++)
561 		if (!(flbr[i].flags & BEEN) || flbr[i].distance <= 1)
562 			break;
563 	if (i == NUMDIRECTIONS)
564 		memset(been_there, 0, sizeof been_there);
565 	dir_mask = dir_count = 0;
566 	for (i = 0; i < NUMDIRECTIONS; i++) {
567 		j = (RIGHT + i) % NUMDIRECTIONS;
568 		if (flbr[j].distance <= 1 || flbr[j].flags & DEADEND)
569 			continue;
570 		if (!(flbr[j].flags & BEEN_SAME)) {
571 			dir_mask = 1 << j;
572 			dir_count = 1;
573 			break;
574 		}
575 		if (j == FRONT
576 		&& num_turns > 4 + (random() %
577 				((flbr[FRONT].flags & BEEN) ? 7 : HEIGHT)))
578 			continue;
579 		dir_mask |= 1 << j;
580 #ifdef notdef
581 		dir_count++;
582 #else
583 		dir_count = 1;
584 		break;
585 #endif
586 	}
587 #ifdef notdef
588 	if (dir_count == 0) {
589 		duck(random() % NUMDIRECTIONS);
590 		num_turns = 0;
591 		return;
592 	} else if (dir_count == 1)
593 #endif
594 		rel_dir = ffs(dir_mask) - 1;
595 #ifdef notdef
596 	else {
597 		rel_dir = ffs(dir_mask) - 1;
598 		dir_mask &= ~(1 << rel_dir);
599 		while (dir_mask != 0) {
600 			i = ffs(dir_mask) - 1;
601 			if (random() % 5 == 0)
602 				rel_dir = i;
603 			dir_mask &= ~(1 << i);
604 		}
605 	}
606 #endif
607 	if (rel_dir == FRONT)
608 		num_turns++;
609 	else
610 		num_turns = 0;
611 
612 #ifdef DEBUG
613 	fprintf(debug, " w(%c)", RELCHARS[rel_dir]);
614 #endif
615 	face_and_move_direction(rel_dir, 1);
616 }
617 
618 #endif /* OTTO */
619