xref: /netbsd/games/hunt/hunt/otto.c (revision 6550d01e)
1 /*	$NetBSD: otto.c,v 1.15 2009/07/04 07:10:23 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.15 2009/07/04 07:10:23 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 <unistd.h>
57 #include "hunt.h"
58 
59 #undef WALL
60 #undef NORTH
61 #undef SOUTH
62 #undef WEST
63 #undef EAST
64 #undef FRONT
65 #undef LEFT
66 #undef BACK
67 #undef RIGHT
68 
69 #ifdef HPUX
70 #define random rand
71 #endif
72 
73 #define SCREEN(y, x)	mvinch(y, x)
74 
75 #ifndef DEBUG
76 #define STATIC		static
77 #else
78 #define STATIC
79 #endif
80 
81 #define OPPONENT	"{}i!"
82 #define PROPONENT	"^v<>"
83 #define WALL		"+\\/#*-|"
84 #define PUSHOVER	" bg;*#&"
85 #define SHOTS		"$@Oo:"
86 
87 /* number of "directions" */
88 #define NUMDIRECTIONS	4
89 
90 /* absolute directions (facings) - counterclockwise */
91 #define NORTH		0
92 #define WEST		1
93 #define SOUTH		2
94 #define EAST		3
95 #define ALLDIRS		0xf
96 
97 /* relative directions - counterclockwise */
98 #define FRONT		0
99 #define LEFT		1
100 #define BACK		2
101 #define RIGHT		3
102 
103 #define ABSCHARS	"NWSE"
104 #define RELCHARS	"FLBR"
105 #define DIRKEYS		"khjl"
106 
107 STATIC char command[BUFSIZ];
108 STATIC int comlen;
109 
110 #ifdef DEBUG
111 STATIC FILE *debug = NULL;
112 #endif
113 
114 #define DEADEND		0x1
115 #define ON_LEFT		0x2
116 #define ON_RIGHT	0x4
117 #define ON_SIDE		(ON_LEFT|ON_RIGHT)
118 #define BEEN		0x8
119 #define BEEN_SAME	0x10
120 
121 struct item {
122 	char what;
123 	int distance;
124 	int flags;
125 };
126 
127 STATIC struct item flbr[NUMDIRECTIONS];
128 
129 #define fitem flbr[FRONT]
130 #define litem flbr[LEFT]
131 #define bitem flbr[BACK]
132 #define ritem flbr[RIGHT]
133 
134 STATIC int facing;
135 STATIC int row, col;
136 STATIC int num_turns;			/* for wandering */
137 STATIC char been_there[HEIGHT][WIDTH2];
138 STATIC struct itimerval	pause_time = { { 0, 0 }, { 0, 55000 }};
139 
140 STATIC void attack(int, struct item *);
141 STATIC void duck(int);
142 STATIC void face_and_move_direction(int, int);
143 STATIC int go_for_ammo(char);
144 STATIC void ottolook(int, struct item *);
145 STATIC void look_around(void);
146 STATIC void nothing(int);
147 STATIC int stop_look(struct item *, char, int, int);
148 STATIC void wander(void);
149 
150 extern int Otto_count;
151 
152 STATIC void
153 nothing(int dummy __unused)
154 {
155 }
156 
157 void
158 otto(int y, int x, char face)
159 {
160 	int i;
161 	int old_mask;
162 
163 #ifdef DEBUG
164 	if (debug == NULL) {
165 		debug = fopen("bug", "w");
166 		setbuf(debug, NULL);
167 	}
168 	fprintf(debug, "\n%c(%d,%d)", face, y, x);
169 #endif
170 	(void) signal(SIGALRM, nothing);
171 	old_mask = sigblock(sigmask(SIGALRM));
172 	setitimer(ITIMER_REAL, &pause_time, NULL);
173 	sigpause(old_mask);
174 	sigsetmask(old_mask);
175 
176 	/* save away parameters so other functions may use/update info */
177 	switch (face) {
178 	case '^': facing = NORTH; break;
179 	case '<': facing = WEST; break;
180 	case 'v': facing = SOUTH; break;
181 	case '>': facing = EAST; break;
182 	default: abort();
183 	}
184 	row = y; col = x;
185 	been_there[row][col] |= 1 << facing;
186 
187 	/* initially no commands to be sent */
188 	comlen = 0;
189 
190 	/* find something to do */
191 	look_around();
192 	for (i = 0; i < NUMDIRECTIONS; i++) {
193 		if (strchr(OPPONENT, flbr[i].what) != NULL) {
194 			attack(i, &flbr[i]);
195 			memset(been_there, 0, sizeof been_there);
196 			goto done;
197 		}
198 	}
199 
200 	if (strchr(SHOTS, bitem.what) != NULL && !(bitem.what & ON_SIDE)) {
201 		duck(BACK);
202 		memset(been_there, 0, sizeof been_there);
203 #ifdef BOOTS
204 	} else if (go_for_ammo(BOOT_PAIR)) {
205 		memset(been_there, 0, sizeof been_there);
206 	} else if (go_for_ammo(BOOT)) {
207 		memset(been_there, 0, sizeof been_there);
208 #endif
209 	} else if (go_for_ammo(GMINE))
210 		memset(been_there, 0, sizeof been_there);
211 	else if (go_for_ammo(MINE))
212 		memset(been_there, 0, sizeof been_there);
213 	else
214 		wander();
215 
216 done:
217 	(void) write(Socket, command, comlen);
218 	Otto_count += comlen;
219 #ifdef	DEBUG
220 	(void) fwrite(command, 1, comlen, debug);
221 #endif
222 }
223 
224 #define direction(abs,rel)	(((abs) + (rel)) % NUMDIRECTIONS)
225 
226 STATIC int
227 stop_look(struct item *itemp, char c, int dist, int side)
228 {
229 	switch (c) {
230 
231 	case SPACE:
232 		if (side)
233 			itemp->flags &= ~DEADEND;
234 		return 0;
235 
236 	case MINE:
237 	case GMINE:
238 #ifdef BOOTS
239 	case BOOT:
240 	case BOOT_PAIR:
241 #endif
242 		if (itemp->distance == -1) {
243 			itemp->distance = dist;
244 			itemp->what = c;
245 			if (side < 0)
246 				itemp->flags |= ON_LEFT;
247 			else if (side > 0)
248 				itemp->flags |= ON_RIGHT;
249 		}
250 		return 0;
251 
252 	case SHOT:
253 	case GRENADE:
254 	case SATCHEL:
255 	case BOMB:
256 #ifdef OOZE
257 	case SLIME:
258 #endif
259 		if (itemp->distance == -1 || (!side
260 		    && (itemp->flags & ON_SIDE
261 		    || itemp->what == GMINE || itemp->what == MINE))) {
262 			itemp->distance = dist;
263 			itemp->what = c;
264 			itemp->flags &= ~ON_SIDE;
265 			if (side < 0)
266 				itemp->flags |= ON_LEFT;
267 			else if (side > 0)
268 				itemp->flags |= ON_RIGHT;
269 		}
270 		return 0;
271 
272 	case '{':
273 	case '}':
274 	case 'i':
275 	case '!':
276 		itemp->distance = dist;
277 		itemp->what = c;
278 		itemp->flags &= ~(ON_SIDE|DEADEND);
279 		if (side < 0)
280 			itemp->flags |= ON_LEFT;
281 		else if (side > 0)
282 			itemp->flags |= ON_RIGHT;
283 		return 1;
284 
285 	default:
286 		/* a wall or unknown object */
287 		if (side)
288 			return 0;
289 		if (itemp->distance == -1) {
290 			itemp->distance = dist;
291 			itemp->what = c;
292 		}
293 		return 1;
294 	}
295 }
296 
297 STATIC void
298 ottolook(int rel_dir, struct item *itemp)
299 {
300 	int r, c;
301 	char ch;
302 
303 	r = 0;
304 	itemp->what = 0;
305 	itemp->distance = -1;
306 	itemp->flags = DEADEND|BEEN;		/* true until proven false */
307 
308 	switch (direction(facing, rel_dir)) {
309 
310 	case NORTH:
311 		if (been_there[row - 1][col] & NORTH)
312 			itemp->flags |= BEEN_SAME;
313 		for (r = row - 1; r >= 0; r--)
314 			for (c = col - 1; c < col + 2; c++) {
315 				ch = SCREEN(r, c);
316 				if (stop_look(itemp, ch, row - r, c - col))
317 					goto cont_north;
318 				if (c == col && !been_there[r][c])
319 					itemp->flags &= ~BEEN;
320 			}
321 	cont_north:
322 		if (itemp->flags & DEADEND) {
323 			itemp->flags |= BEEN;
324 			if (r >= 0)
325 				been_there[r][col] |= NORTH;
326 			for (r = row - 1; r > row - itemp->distance; r--)
327 				been_there[r][col] = ALLDIRS;
328 		}
329 		break;
330 
331 	case SOUTH:
332 		if (been_there[row + 1][col] & SOUTH)
333 			itemp->flags |= BEEN_SAME;
334 		for (r = row + 1; r < HEIGHT; r++)
335 			for (c = col - 1; c < col + 2; c++) {
336 				ch = SCREEN(r, c);
337 				if (stop_look(itemp, ch, r - row, col - c))
338 					goto cont_south;
339 				if (c == col && !been_there[r][c])
340 					itemp->flags &= ~BEEN;
341 			}
342 	cont_south:
343 		if (itemp->flags & DEADEND) {
344 			itemp->flags |= BEEN;
345 			if (r < HEIGHT)
346 				been_there[r][col] |= SOUTH;
347 			for (r = row + 1; r < row + itemp->distance; r++)
348 				been_there[r][col] = ALLDIRS;
349 		}
350 		break;
351 
352 	case WEST:
353 		if (been_there[row][col - 1] & WEST)
354 			itemp->flags |= BEEN_SAME;
355 		for (c = col - 1; c >= 0; c--)
356 			for (r = row - 1; r < row + 2; r++) {
357 				ch = SCREEN(r, c);
358 				if (stop_look(itemp, ch, col - c, row - r))
359 					goto cont_west;
360 				if (r == row && !been_there[r][c])
361 					itemp->flags &= ~BEEN;
362 			}
363 	cont_west:
364 		if (itemp->flags & DEADEND) {
365 			itemp->flags |= BEEN;
366 			been_there[r][col] |= WEST;
367 			for (c = col - 1; c > col - itemp->distance; c--)
368 				been_there[row][c] = ALLDIRS;
369 		}
370 		break;
371 
372 	case EAST:
373 		if (been_there[row][col + 1] & EAST)
374 			itemp->flags |= BEEN_SAME;
375 		for (c = col + 1; c < WIDTH; c++)
376 			for (r = row - 1; r < row + 2; r++) {
377 				ch = SCREEN(r, c);
378 				if (stop_look(itemp, ch, c - col, r - row))
379 					goto cont_east;
380 				if (r == row && !been_there[r][c])
381 					itemp->flags &= ~BEEN;
382 			}
383 	cont_east:
384 		if (itemp->flags & DEADEND) {
385 			itemp->flags |= BEEN;
386 			been_there[r][col] |= EAST;
387 			for (c = col + 1; c < col + itemp->distance; c++)
388 				been_there[row][c] = ALLDIRS;
389 		}
390 		break;
391 
392 	default:
393 		abort();
394 	}
395 }
396 
397 STATIC void
398 look_around(void)
399 {
400 	int i;
401 
402 	for (i = 0; i < NUMDIRECTIONS; i++) {
403 		ottolook(i, &flbr[i]);
404 #ifdef DEBUG
405 		fprintf(debug, " ottolook(%c)=%c(%d)(0x%x)",
406 			RELCHARS[i], flbr[i].what, flbr[i].distance, flbr[i].flags);
407 #endif
408 	}
409 }
410 
411 /*
412  * as a side effect modifies facing and location (row, col)
413  */
414 
415 STATIC void
416 face_and_move_direction(int rel_dir, int distance)
417 {
418 	int old_facing;
419 	char cmd;
420 
421 	old_facing = facing;
422 	cmd = DIRKEYS[facing = direction(facing, rel_dir)];
423 
424 	if (rel_dir != FRONT) {
425 		int i;
426 		struct item items[NUMDIRECTIONS];
427 
428 		command[comlen++] = toupper((unsigned char)cmd);
429 		if (distance == 0) {
430 			/* rotate ottolook's to be in right position */
431 			for (i = 0; i < NUMDIRECTIONS; i++)
432 				items[i] =
433 					flbr[(i + old_facing) % NUMDIRECTIONS];
434 			memcpy(flbr, items, sizeof flbr);
435 		}
436 	}
437 	while (distance--) {
438 		command[comlen++] = cmd;
439 		switch (facing) {
440 
441 		case NORTH: row--; break;
442 		case WEST:  col--; break;
443 		case SOUTH: row++; break;
444 		case EAST:  col++; break;
445 		}
446 		if (distance == 0)
447 			look_around();
448 	}
449 }
450 
451 STATIC void
452 attack(int rel_dir, struct item *itemp)
453 {
454 	if (!(itemp->flags & ON_SIDE)) {
455 		face_and_move_direction(rel_dir, 0);
456 		command[comlen++] = 'o';
457 		command[comlen++] = 'o';
458 		duck(FRONT);
459 		command[comlen++] = ' ';
460 	} else if (itemp->distance > 1) {
461 		face_and_move_direction(rel_dir, 2);
462 		duck(FRONT);
463 	} else {
464 		face_and_move_direction(rel_dir, 1);
465 		if (itemp->flags & ON_LEFT)
466 			rel_dir = LEFT;
467 		else
468 			rel_dir = RIGHT;
469 		(void) face_and_move_direction(rel_dir, 0);
470 		command[comlen++] = 'f';
471 		command[comlen++] = 'f';
472 		duck(FRONT);
473 		command[comlen++] = ' ';
474 	}
475 }
476 
477 STATIC void
478 duck(int rel_dir)
479 {
480 	int dir;
481 
482 	switch (dir = direction(facing, rel_dir)) {
483 
484 	case NORTH:
485 	case SOUTH:
486 		if (strchr(PUSHOVER, SCREEN(row, col - 1)) != NULL)
487 			command[comlen++] = 'h';
488 		else if (strchr(PUSHOVER, SCREEN(row, col + 1)) != NULL)
489 			command[comlen++] = 'l';
490 		else if (dir == NORTH
491 			&& strchr(PUSHOVER, SCREEN(row + 1, col)) != NULL)
492 				command[comlen++] = 'j';
493 		else if (dir == SOUTH
494 			&& strchr(PUSHOVER, SCREEN(row - 1, col)) != NULL)
495 				command[comlen++] = 'k';
496 		else if (dir == NORTH)
497 			command[comlen++] = 'k';
498 		else
499 			command[comlen++] = 'j';
500 		break;
501 
502 	case WEST:
503 	case EAST:
504 		if (strchr(PUSHOVER, SCREEN(row - 1, col)) != NULL)
505 			command[comlen++] = 'k';
506 		else if (strchr(PUSHOVER, SCREEN(row + 1, col)) != NULL)
507 			command[comlen++] = 'j';
508 		else if (dir == WEST
509 			&& strchr(PUSHOVER, SCREEN(row, col + 1)) != NULL)
510 				command[comlen++] = 'l';
511 		else if (dir == EAST
512 			&& strchr(PUSHOVER, SCREEN(row, col - 1)) != NULL)
513 				command[comlen++] = 'h';
514 		else if (dir == WEST)
515 			command[comlen++] = 'h';
516 		else
517 			command[comlen++] = 'l';
518 		break;
519 	}
520 }
521 
522 /*
523  * go for the closest mine if possible
524  */
525 
526 STATIC int
527 go_for_ammo(char mine)
528 {
529 	int i, rel_dir, dist;
530 
531 	rel_dir = -1;
532 	dist = WIDTH;
533 	for (i = 0; i < NUMDIRECTIONS; i++) {
534 		if (flbr[i].what == mine && flbr[i].distance < dist) {
535 			rel_dir = i;
536 			dist = flbr[i].distance;
537 		}
538 	}
539 	if (rel_dir == -1)
540 		return FALSE;
541 
542 	if (!(flbr[rel_dir].flags & ON_SIDE)
543 	|| flbr[rel_dir].distance > 1) {
544 		if (dist > 4)
545 			dist = 4;
546 		face_and_move_direction(rel_dir, dist);
547 	} else
548 		return FALSE;		/* until it's done right */
549 	return TRUE;
550 }
551 
552 STATIC void
553 wander(void)
554 {
555 	int i, j, rel_dir, dir_mask, dir_count;
556 
557 	for (i = 0; i < NUMDIRECTIONS; i++)
558 		if (!(flbr[i].flags & BEEN) || flbr[i].distance <= 1)
559 			break;
560 	if (i == NUMDIRECTIONS)
561 		memset(been_there, 0, sizeof been_there);
562 	dir_mask = dir_count = 0;
563 	for (i = 0; i < NUMDIRECTIONS; i++) {
564 		j = (RIGHT + i) % NUMDIRECTIONS;
565 		if (flbr[j].distance <= 1 || flbr[j].flags & DEADEND)
566 			continue;
567 		if (!(flbr[j].flags & BEEN_SAME)) {
568 			dir_mask = 1 << j;
569 			dir_count = 1;
570 			break;
571 		}
572 		if (j == FRONT
573 		&& num_turns > 4 + (random() %
574 				((flbr[FRONT].flags & BEEN) ? 7 : HEIGHT)))
575 			continue;
576 		dir_mask |= 1 << j;
577 #ifdef notdef
578 		dir_count++;
579 #else
580 		dir_count = 1;
581 		break;
582 #endif
583 	}
584 #ifdef notdef
585 	if (dir_count == 0) {
586 		duck(random() % NUMDIRECTIONS);
587 		num_turns = 0;
588 		return;
589 	} else if (dir_count == 1)
590 #endif
591 		rel_dir = ffs(dir_mask) - 1;
592 #ifdef notdef
593 	else {
594 		rel_dir = ffs(dir_mask) - 1;
595 		dir_mask &= ~(1 << rel_dir);
596 		while (dir_mask != 0) {
597 			i = ffs(dir_mask) - 1;
598 			if (random() % 5 == 0)
599 				rel_dir = i;
600 			dir_mask &= ~(1 << i);
601 		}
602 	}
603 #endif
604 	if (rel_dir == FRONT)
605 		num_turns++;
606 	else
607 		num_turns = 0;
608 
609 #ifdef DEBUG
610 	fprintf(debug, " w(%c)", RELCHARS[rel_dir]);
611 #endif
612 	face_and_move_direction(rel_dir, 1);
613 }
614 
615 #endif /* OTTO */
616