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