Home | History | Annotate | Line # | Download | only in hunt
      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
    156 nothing(int dummy __unused)
    157 {
    158 }
    159 
    160 void
    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
    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
    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
    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
    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
    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
    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
    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
    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