1/*	$NetBSD: otto.c,v 1.17 2014/03/29 21:24:26 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.17 2014/03/29 21:24:26 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
110STATIC char command[BUFSIZ];
111STATIC int comlen;
112
113#ifdef DEBUG
114STATIC 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
124struct item {
125	char what;
126	int distance;
127	int flags;
128};
129
130STATIC 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
137STATIC int facing;
138STATIC int row, col;
139STATIC int num_turns;			/* for wandering */
140STATIC char been_there[HEIGHT][WIDTH2];
141STATIC struct itimerval	pause_time = { { 0, 0 }, { 0, 55000 }};
142
143STATIC void attack(int, struct item *);
144STATIC void duck(int);
145STATIC void face_and_move_direction(int, int);
146STATIC bool go_for_ammo(char);
147STATIC void ottolook(int, struct item *);
148STATIC void look_around(void);
149STATIC void nothing(int);
150STATIC int stop_look(struct item *, char, int, int);
151STATIC void wander(void);
152
153extern int Otto_count;
154
155STATIC void
156nothing(int dummy __unused)
157{
158}
159
160void
161otto(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
219done:
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
229STATIC int
230stop_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
300STATIC void
301ottolook(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
400STATIC void
401look_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
418STATIC void
419face_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
454STATIC void
455attack(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
480STATIC void
481duck(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
529STATIC bool
530go_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
555STATIC void
556wander(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