1 /*-
2 * Copyright (c) 1983-2003, Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University of California, San Francisco nor
15 * the names of its contributors may be used to endorse or promote
16 * products derived from this software without specific prior written
17 * permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * $OpenBSD: otto.c,v 1.9 2006/03/27 00:10:15 tedu Exp $
32 * $NetBSD: otto.c,v 1.2 1997/10/10 16:32:39 lukem Exp $
33 */
34
35 /*
36 * otto - a hunt otto-matic player
37 *
38 * This guy is buggy, unfair, stupid, and not extensible.
39 * Future versions of hunt will have a subroutine library for
40 * automatic players to link to. If you write your own "otto"
41 * please let us know what subroutines you would expect in the
42 * subroutine library.
43 */
44
45 #include <sys/time.h>
46 #include <ctype.h>
47 #include <signal.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50 #include <string.h>
51 #include "hunt.h"
52 #include "client.h"
53 #include "display.h"
54
55 #include <stdio.h>
56 #define panic(m) _panic(__FILE__,__LINE__,m)
57
58 useconds_t Otto_pause = 55000;
59
60 int Otto_mode;
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 # define SCREEN(y, x) display_atyx(y, x)
73
74 # define OPPONENT "{}i!"
75 # define PROPONENT "^v<>"
76 # define WALL "+\\/#*-|"
77 # define PUSHOVER " bg;*#&"
78 # define SHOTS "$@Oo:"
79
80 /* number of "directions" */
81 # define NUMDIRECTIONS 4
82 # define direction(abs,rel) (((abs) + (rel)) % NUMDIRECTIONS)
83
84 /* absolute directions (facings) - counterclockwise */
85 # define NORTH 0
86 # define WEST 1
87 # define SOUTH 2
88 # define EAST 3
89 # define ALLDIRS 0xf
90
91 /* relative directions - counterclockwise */
92 # define FRONT 0
93 # define LEFT 1
94 # define BACK 2
95 # define RIGHT 3
96
97 # define ABSCHARS "NWSE"
98 # define RELCHARS "FLBR"
99 # define DIRKEYS "khjl"
100
101 static char command[1024]; /* XXX */
102 static int comlen;
103
104 # define DEADEND 0x1
105 # define ON_LEFT 0x2
106 # define ON_RIGHT 0x4
107 # define ON_SIDE (ON_LEFT|ON_RIGHT)
108 # define BEEN 0x8
109 # define BEEN_SAME 0x10
110
111 struct item {
112 char what;
113 int distance;
114 int flags;
115 };
116
117 static struct item flbr[NUMDIRECTIONS];
118
119 # define fitem flbr[FRONT]
120 # define litem flbr[LEFT]
121 # define bitem flbr[BACK]
122 # define ritem flbr[RIGHT]
123
124 static int facing;
125 static int row, col;
126 static int num_turns; /* for wandering */
127 static char been_there[HEIGHT][WIDTH2];
128
129 static void attack(int, struct item *);
130 static void duck(int);
131 static void face_and_move_direction(int, int);
132 static int go_for_ammo(char);
133 static void ottolook(int, struct item *);
134 static void look_around(void);
135 static int stop_look(struct item *, char, int, int);
136 static void wander(void);
137 static void _panic(const char *, int, const char *);
138
139 int
otto(int y,int x,char face,char * buf,size_t buflen)140 otto(int y, int x, char face, char *buf, size_t buflen)
141 {
142 int i;
143
144 if (usleep(Otto_pause) < 0)
145 panic("usleep");
146
147 /* save away parameters so other functions may use/update info */
148 switch (face) {
149 case '^': facing = NORTH; break;
150 case '<': facing = WEST; break;
151 case 'v': facing = SOUTH; break;
152 case '>': facing = EAST; break;
153 default: panic("unknown face");
154 }
155 row = y; col = x;
156 been_there[row][col] |= 1 << facing;
157
158 /* initially no commands to be sent */
159 comlen = 0;
160
161 /* find something to do */
162 look_around();
163 for (i = 0; i < NUMDIRECTIONS; i++) {
164 if (strchr(OPPONENT, flbr[i].what) != NULL) {
165 attack(i, &flbr[i]);
166 memset(been_there, 0, sizeof been_there);
167 goto done;
168 }
169 }
170
171 if (strchr(SHOTS, bitem.what) != NULL && !(bitem.what & ON_SIDE)) {
172 duck(BACK);
173 memset(been_there, 0, sizeof been_there);
174 } else if (go_for_ammo(BOOT_PAIR)) {
175 memset(been_there, 0, sizeof been_there);
176 } else if (go_for_ammo(BOOT)) {
177 memset(been_there, 0, sizeof been_there);
178 } else if (go_for_ammo(GMINE))
179 memset(been_there, 0, sizeof been_there);
180 else if (go_for_ammo(MINE))
181 memset(been_there, 0, sizeof been_there);
182 else
183 wander();
184
185 done:
186 if (comlen) {
187 if (comlen > (int)buflen)
188 panic("not enough buffer space");
189 memcpy(buf, command, comlen);
190 }
191 return comlen;
192 }
193
194 static int
stop_look(struct item * itemp,char c,int dist,int side)195 stop_look(struct item *itemp, char c, int dist, int side)
196 {
197 switch (c) {
198
199 case SPACE:
200 if (side)
201 itemp->flags &= ~DEADEND;
202 return 0;
203
204 case MINE:
205 case GMINE:
206 case BOOT:
207 case BOOT_PAIR:
208 if (itemp->distance == -1) {
209 itemp->distance = dist;
210 itemp->what = c;
211 if (side < 0)
212 itemp->flags |= ON_LEFT;
213 else if (side > 0)
214 itemp->flags |= ON_RIGHT;
215 }
216 return 0;
217
218 case SHOT:
219 case GRENADE:
220 case SATCHEL:
221 case BOMB:
222 case SLIME:
223 if (itemp->distance == -1 || (!side
224 && (itemp->flags & ON_SIDE
225 || itemp->what == GMINE || itemp->what == MINE))) {
226 itemp->distance = dist;
227 itemp->what = c;
228 itemp->flags &= ~ON_SIDE;
229 if (side < 0)
230 itemp->flags |= ON_LEFT;
231 else if (side > 0)
232 itemp->flags |= ON_RIGHT;
233 }
234 return 0;
235
236 case '{':
237 case '}':
238 case 'i':
239 case '!':
240 itemp->distance = dist;
241 itemp->what = c;
242 itemp->flags &= ~(ON_SIDE|DEADEND);
243 if (side < 0)
244 itemp->flags |= ON_LEFT;
245 else if (side > 0)
246 itemp->flags |= ON_RIGHT;
247 return 1;
248
249 default:
250 /* a wall or unknown object */
251 if (side)
252 return 0;
253 if (itemp->distance == -1) {
254 itemp->distance = dist;
255 itemp->what = c;
256 }
257 return 1;
258 }
259 }
260
261 static void
ottolook(int rel_dir,struct item * itemp)262 ottolook(int rel_dir, struct item *itemp)
263 {
264 int r, c;
265 char ch;
266
267 r = 0;
268 itemp->what = 0;
269 itemp->distance = -1;
270 itemp->flags = DEADEND|BEEN; /* true until proven false */
271
272 switch (direction(facing, rel_dir)) {
273
274 case NORTH:
275 if (been_there[row - 1][col] & NORTH)
276 itemp->flags |= BEEN_SAME;
277 for (r = row - 1; r >= 0; r--)
278 for (c = col - 1; c < col + 2; c++) {
279 ch = SCREEN(r, c);
280 if (stop_look(itemp, ch, row - r, c - col))
281 goto cont_north;
282 if (c == col && !been_there[r][c])
283 itemp->flags &= ~BEEN;
284 }
285 cont_north:
286 if (itemp->flags & DEADEND) {
287 itemp->flags |= BEEN;
288 if (r >= 0)
289 been_there[r][col] |= NORTH;
290 for (r = row - 1; r > row - itemp->distance; r--)
291 been_there[r][col] = ALLDIRS;
292 }
293 break;
294
295 case SOUTH:
296 if (been_there[row + 1][col] & SOUTH)
297 itemp->flags |= BEEN_SAME;
298 for (r = row + 1; r < HEIGHT; r++)
299 for (c = col - 1; c < col + 2; c++) {
300 ch = SCREEN(r, c);
301 if (stop_look(itemp, ch, r - row, col - c))
302 goto cont_south;
303 if (c == col && !been_there[r][c])
304 itemp->flags &= ~BEEN;
305 }
306 cont_south:
307 if (itemp->flags & DEADEND) {
308 itemp->flags |= BEEN;
309 if (r < HEIGHT)
310 been_there[r][col] |= SOUTH;
311 for (r = row + 1; r < row + itemp->distance; r++)
312 been_there[r][col] = ALLDIRS;
313 }
314 break;
315
316 case WEST:
317 if (been_there[row][col - 1] & WEST)
318 itemp->flags |= BEEN_SAME;
319 for (c = col - 1; c >= 0; c--)
320 for (r = row - 1; r < row + 2; r++) {
321 ch = SCREEN(r, c);
322 if (stop_look(itemp, ch, col - c, row - r))
323 goto cont_west;
324 if (r == row && !been_there[r][c])
325 itemp->flags &= ~BEEN;
326 }
327 cont_west:
328 if (itemp->flags & DEADEND) {
329 itemp->flags |= BEEN;
330 been_there[r][col] |= WEST;
331 for (c = col - 1; c > col - itemp->distance; c--)
332 been_there[row][c] = ALLDIRS;
333 }
334 break;
335
336 case EAST:
337 if (been_there[row][col + 1] & EAST)
338 itemp->flags |= BEEN_SAME;
339 for (c = col + 1; c < WIDTH; c++)
340 for (r = row - 1; r < row + 2; r++) {
341 ch = SCREEN(r, c);
342 if (stop_look(itemp, ch, c - col, r - row))
343 goto cont_east;
344 if (r == row && !been_there[r][c])
345 itemp->flags &= ~BEEN;
346 }
347 cont_east:
348 if (itemp->flags & DEADEND) {
349 itemp->flags |= BEEN;
350 been_there[r][col] |= EAST;
351 for (c = col + 1; c < col + itemp->distance; c++)
352 been_there[row][c] = ALLDIRS;
353 }
354 break;
355
356 default:
357 panic("unknown look");
358 }
359 }
360
361 static void
look_around(void)362 look_around(void)
363 {
364 int i;
365
366 for (i = 0; i < NUMDIRECTIONS; i++) {
367 ottolook(i, &flbr[i]);
368 }
369 }
370
371 /*
372 * as a side effect modifies facing and location (row, col)
373 */
374
375 static void
face_and_move_direction(int rel_dir,int distance)376 face_and_move_direction(int rel_dir, int distance)
377 {
378 int old_facing;
379 char cmd;
380
381 old_facing = facing;
382 cmd = DIRKEYS[facing = direction(facing, rel_dir)];
383
384 if (rel_dir != FRONT) {
385 int i;
386 struct item items[NUMDIRECTIONS];
387
388 command[comlen++] = toupper(cmd);
389 if (distance == 0) {
390 /* rotate ottolook's to be in right position */
391 for (i = 0; i < NUMDIRECTIONS; i++)
392 items[i] =
393 flbr[(i + old_facing) % NUMDIRECTIONS];
394 memcpy(flbr, items, sizeof flbr);
395 }
396 }
397 while (distance--) {
398 command[comlen++] = cmd;
399 switch (facing) {
400
401 case NORTH: row--; break;
402 case WEST: col--; break;
403 case SOUTH: row++; break;
404 case EAST: col++; break;
405 }
406 if (distance == 0)
407 look_around();
408 }
409 }
410
411 static void
attack(int rel_dir,struct item * itemp)412 attack(int rel_dir, struct item *itemp)
413 {
414 if (!(itemp->flags & ON_SIDE)) {
415 face_and_move_direction(rel_dir, 0);
416 command[comlen++] = 'o';
417 command[comlen++] = 'o';
418 duck(FRONT);
419 command[comlen++] = ' ';
420 } else if (itemp->distance > 1) {
421 face_and_move_direction(rel_dir, 2);
422 duck(FRONT);
423 } else {
424 face_and_move_direction(rel_dir, 1);
425 if (itemp->flags & ON_LEFT)
426 rel_dir = LEFT;
427 else
428 rel_dir = RIGHT;
429 face_and_move_direction(rel_dir, 0);
430 command[comlen++] = 'f';
431 command[comlen++] = 'f';
432 duck(FRONT);
433 command[comlen++] = ' ';
434 }
435 }
436
437 static void
duck(int rel_dir)438 duck(int rel_dir)
439 {
440 int dir;
441
442 switch (dir = direction(facing, rel_dir)) {
443
444 case NORTH:
445 case SOUTH:
446 if (strchr(PUSHOVER, SCREEN(row, col - 1)) != NULL)
447 command[comlen++] = 'h';
448 else if (strchr(PUSHOVER, SCREEN(row, col + 1)) != NULL)
449 command[comlen++] = 'l';
450 else if (dir == NORTH
451 && strchr(PUSHOVER, SCREEN(row + 1, col)) != NULL)
452 command[comlen++] = 'j';
453 else if (dir == SOUTH
454 && strchr(PUSHOVER, SCREEN(row - 1, col)) != NULL)
455 command[comlen++] = 'k';
456 else if (dir == NORTH)
457 command[comlen++] = 'k';
458 else
459 command[comlen++] = 'j';
460 break;
461
462 case WEST:
463 case EAST:
464 if (strchr(PUSHOVER, SCREEN(row - 1, col)) != NULL)
465 command[comlen++] = 'k';
466 else if (strchr(PUSHOVER, SCREEN(row + 1, col)) != NULL)
467 command[comlen++] = 'j';
468 else if (dir == WEST
469 && strchr(PUSHOVER, SCREEN(row, col + 1)) != NULL)
470 command[comlen++] = 'l';
471 else if (dir == EAST
472 && strchr(PUSHOVER, SCREEN(row, col - 1)) != NULL)
473 command[comlen++] = 'h';
474 else if (dir == WEST)
475 command[comlen++] = 'h';
476 else
477 command[comlen++] = 'l';
478 break;
479 }
480 }
481
482 /*
483 * go for the closest mine if possible
484 */
485
486 static int
go_for_ammo(char mine)487 go_for_ammo(char mine)
488 {
489 int i, rel_dir, dist;
490
491 rel_dir = -1;
492 dist = WIDTH;
493 for (i = 0; i < NUMDIRECTIONS; i++) {
494 if (flbr[i].what == mine && flbr[i].distance < dist) {
495 rel_dir = i;
496 dist = flbr[i].distance;
497 }
498 }
499 if (rel_dir == -1)
500 return FALSE;
501
502 if (!(flbr[rel_dir].flags & ON_SIDE)
503 || flbr[rel_dir].distance > 1) {
504 if (dist > 4)
505 dist = 4;
506 face_and_move_direction(rel_dir, dist);
507 } else
508 return FALSE; /* until it's done right */
509 return TRUE;
510 }
511
512 static void
wander(void)513 wander(void)
514 {
515 int i, j, rel_dir, dir_mask, dir_count;
516
517 for (i = 0; i < NUMDIRECTIONS; i++)
518 if (!(flbr[i].flags & BEEN) || flbr[i].distance <= 1)
519 break;
520 if (i == NUMDIRECTIONS)
521 memset(been_there, 0, sizeof been_there);
522 dir_mask = dir_count = 0;
523 for (i = 0; i < NUMDIRECTIONS; i++) {
524 j = (RIGHT + i) % NUMDIRECTIONS;
525 if (flbr[j].distance <= 1 || flbr[j].flags & DEADEND)
526 continue;
527 if (!(flbr[j].flags & BEEN_SAME)) {
528 dir_mask = 1 << j;
529 dir_count = 1;
530 break;
531 }
532 if (j == FRONT
533 && num_turns > 4 + (random() %
534 ((flbr[FRONT].flags & BEEN) ? 7 : HEIGHT)))
535 continue;
536 dir_mask |= 1 << j;
537 dir_count = 1;
538 break;
539 }
540 if (dir_count == 0) {
541 duck(random() % NUMDIRECTIONS);
542 num_turns = 0;
543 return;
544 } else {
545 rel_dir = ffs(dir_mask) - 1;
546 }
547 if (rel_dir == FRONT)
548 num_turns++;
549 else
550 num_turns = 0;
551
552 face_and_move_direction(rel_dir, 1);
553 }
554
555 /* Otto always re-enters the game, cloaked. */
556 int
otto_quit(int old_status __unused)557 otto_quit(int old_status __unused)
558 {
559 return Q_CLOAK;
560 }
561
562 static void
_panic(const char * file,int line,const char * msg)563 _panic(const char *file, int line, const char *msg)
564 {
565
566 fprintf(stderr, "%s:%d: panic! %s\n", file, line, msg);
567 abort();
568 }
569