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