1 /* $OpenBSD: otto.c,v 1.13 2016/01/07 21:37:53 mestre Exp $ */
2 /* $NetBSD: otto.c,v 1.2 1997/10/10 16:32:39 lukem Exp $ */
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
44 #include <ctype.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #include "display.h"
51 #include "hunt.h"
52
53 #define panic(m) _panic(__FILE__,__LINE__,m)
54
55 useconds_t Otto_pause = 55000;
56
57 int Otto_mode;
58
59 # undef WALL
60 # undef NORTH
61 # undef SOUTH
62 # undef WEST
63 # undef EAST
64 # undef FRONT
65 # undef LEFT
66 # undef BACK
67 # undef RIGHT
68
69 # define SCREEN(y, x) display_atyx(y, x)
70
71 # define OPPONENT "{}i!"
72 # define PROPONENT "^v<>"
73 # define WALL "+\\/#*-|"
74 # define PUSHOVER " bg;*#&"
75 # define SHOTS "$@Oo:"
76
77 /* number of "directions" */
78 # define NUMDIRECTIONS 4
79 # define direction(abs,rel) (((abs) + (rel)) % NUMDIRECTIONS)
80
81 /* absolute directions (facings) - counterclockwise */
82 # define NORTH 0
83 # define WEST 1
84 # define SOUTH 2
85 # define EAST 3
86 # define ALLDIRS 0xf
87
88 /* relative directions - counterclockwise */
89 # define FRONT 0
90 # define LEFT 1
91 # define BACK 2
92 # define RIGHT 3
93
94 # define ABSCHARS "NWSE"
95 # define RELCHARS "FLBR"
96 # define DIRKEYS "khjl"
97
98 static char command[1024]; /* XXX */
99 static int comlen;
100
101 # define DEADEND 0x1
102 # define ON_LEFT 0x2
103 # define ON_RIGHT 0x4
104 # define ON_SIDE (ON_LEFT|ON_RIGHT)
105 # define BEEN 0x8
106 # define BEEN_SAME 0x10
107
108 struct item {
109 char what;
110 int distance;
111 int flags;
112 };
113
114 static struct item flbr[NUMDIRECTIONS];
115
116 # define fitem flbr[FRONT]
117 # define litem flbr[LEFT]
118 # define bitem flbr[BACK]
119 # define ritem flbr[RIGHT]
120
121 static int facing;
122 static int row, col;
123 static int num_turns; /* for wandering */
124 static char been_there[HEIGHT][WIDTH2];
125
126 static void attack(int, struct item *);
127 static void duck(int);
128 static void face_and_move_direction(int, int);
129 static int go_for_ammo(char);
130 static void ottolook(int, struct item *);
131 static void look_around(void);
132 static int stop_look(struct item *, char, int, int);
133 static void wander(void);
134 static void _panic(const char *, int, const char *);
135
136 int
otto(int y,int x,char face,char * buf,size_t buflen)137 otto(int y, int x, char face, char *buf, size_t buflen)
138 {
139 int i;
140
141 if (usleep(Otto_pause) < 0)
142 panic("usleep");
143
144 /* save away parameters so other functions may use/update info */
145 switch (face) {
146 case '^': facing = NORTH; break;
147 case '<': facing = WEST; break;
148 case 'v': facing = SOUTH; break;
149 case '>': facing = EAST; break;
150 default: panic("unknown face");
151 }
152 row = y; col = x;
153 been_there[row][col] |= 1 << facing;
154
155 /* initially no commands to be sent */
156 comlen = 0;
157
158 /* find something to do */
159 look_around();
160 for (i = 0; i < NUMDIRECTIONS; i++) {
161 if (strchr(OPPONENT, flbr[i].what) != NULL) {
162 attack(i, &flbr[i]);
163 memset(been_there, 0, sizeof been_there);
164 goto done;
165 }
166 }
167
168 if (strchr(SHOTS, bitem.what) != NULL && !(bitem.what & ON_SIDE)) {
169 duck(BACK);
170 memset(been_there, 0, sizeof been_there);
171 } else if (go_for_ammo(BOOT_PAIR)) {
172 memset(been_there, 0, sizeof been_there);
173 } else if (go_for_ammo(BOOT)) {
174 memset(been_there, 0, sizeof been_there);
175 } else if (go_for_ammo(GMINE))
176 memset(been_there, 0, sizeof been_there);
177 else if (go_for_ammo(MINE))
178 memset(been_there, 0, sizeof been_there);
179 else
180 wander();
181
182 done:
183 if (comlen) {
184 if (comlen > buflen)
185 panic("not enough buffer space");
186 memcpy(buf, command, comlen);
187 }
188 return comlen;
189 }
190
191 static int
stop_look(struct item * itemp,char c,int dist,int side)192 stop_look(struct item *itemp, char c, int dist, int side)
193 {
194 switch (c) {
195
196 case SPACE:
197 if (side)
198 itemp->flags &= ~DEADEND;
199 return 0;
200
201 case MINE:
202 case GMINE:
203 case BOOT:
204 case BOOT_PAIR:
205 if (itemp->distance == -1) {
206 itemp->distance = dist;
207 itemp->what = c;
208 if (side < 0)
209 itemp->flags |= ON_LEFT;
210 else if (side > 0)
211 itemp->flags |= ON_RIGHT;
212 }
213 return 0;
214
215 case SHOT:
216 case GRENADE:
217 case SATCHEL:
218 case BOMB:
219 case SLIME:
220 if (itemp->distance == -1 || (!side
221 && (itemp->flags & ON_SIDE
222 || itemp->what == GMINE || itemp->what == MINE))) {
223 itemp->distance = dist;
224 itemp->what = c;
225 itemp->flags &= ~ON_SIDE;
226 if (side < 0)
227 itemp->flags |= ON_LEFT;
228 else if (side > 0)
229 itemp->flags |= ON_RIGHT;
230 }
231 return 0;
232
233 case '{':
234 case '}':
235 case 'i':
236 case '!':
237 itemp->distance = dist;
238 itemp->what = c;
239 itemp->flags &= ~(ON_SIDE|DEADEND);
240 if (side < 0)
241 itemp->flags |= ON_LEFT;
242 else if (side > 0)
243 itemp->flags |= ON_RIGHT;
244 return 1;
245
246 default:
247 /* a wall or unknown object */
248 if (side)
249 return 0;
250 if (itemp->distance == -1) {
251 itemp->distance = dist;
252 itemp->what = c;
253 }
254 return 1;
255 }
256 }
257
258 static void
ottolook(int rel_dir,struct item * itemp)259 ottolook(int rel_dir, struct item *itemp)
260 {
261 int r, c;
262 char ch;
263
264 r = 0;
265 itemp->what = 0;
266 itemp->distance = -1;
267 itemp->flags = DEADEND|BEEN; /* true until proven false */
268
269 switch (direction(facing, rel_dir)) {
270
271 case NORTH:
272 if (been_there[row - 1][col] & NORTH)
273 itemp->flags |= BEEN_SAME;
274 for (r = row - 1; r >= 0; r--)
275 for (c = col - 1; c < col + 2; c++) {
276 ch = SCREEN(r, c);
277 if (stop_look(itemp, ch, row - r, c - col))
278 goto cont_north;
279 if (c == col && !been_there[r][c])
280 itemp->flags &= ~BEEN;
281 }
282 cont_north:
283 if (itemp->flags & DEADEND) {
284 itemp->flags |= BEEN;
285 if (r >= 0)
286 been_there[r][col] |= NORTH;
287 for (r = row - 1; r > row - itemp->distance; r--)
288 been_there[r][col] = ALLDIRS;
289 }
290 break;
291
292 case SOUTH:
293 if (been_there[row + 1][col] & SOUTH)
294 itemp->flags |= BEEN_SAME;
295 for (r = row + 1; r < HEIGHT; r++)
296 for (c = col - 1; c < col + 2; c++) {
297 ch = SCREEN(r, c);
298 if (stop_look(itemp, ch, r - row, col - c))
299 goto cont_south;
300 if (c == col && !been_there[r][c])
301 itemp->flags &= ~BEEN;
302 }
303 cont_south:
304 if (itemp->flags & DEADEND) {
305 itemp->flags |= BEEN;
306 if (r < HEIGHT)
307 been_there[r][col] |= SOUTH;
308 for (r = row + 1; r < row + itemp->distance; r++)
309 been_there[r][col] = ALLDIRS;
310 }
311 break;
312
313 case WEST:
314 if (been_there[row][col - 1] & WEST)
315 itemp->flags |= BEEN_SAME;
316 for (c = col - 1; c >= 0; c--)
317 for (r = row - 1; r < row + 2; r++) {
318 ch = SCREEN(r, c);
319 if (stop_look(itemp, ch, col - c, row - r))
320 goto cont_west;
321 if (r == row && !been_there[r][c])
322 itemp->flags &= ~BEEN;
323 }
324 cont_west:
325 if (itemp->flags & DEADEND) {
326 itemp->flags |= BEEN;
327 been_there[r][col] |= WEST;
328 for (c = col - 1; c > col - itemp->distance; c--)
329 been_there[row][c] = ALLDIRS;
330 }
331 break;
332
333 case EAST:
334 if (been_there[row][col + 1] & EAST)
335 itemp->flags |= BEEN_SAME;
336 for (c = col + 1; c < WIDTH; c++)
337 for (r = row - 1; r < row + 2; r++) {
338 ch = SCREEN(r, c);
339 if (stop_look(itemp, ch, c - col, r - row))
340 goto cont_east;
341 if (r == row && !been_there[r][c])
342 itemp->flags &= ~BEEN;
343 }
344 cont_east:
345 if (itemp->flags & DEADEND) {
346 itemp->flags |= BEEN;
347 been_there[r][col] |= EAST;
348 for (c = col + 1; c < col + itemp->distance; c++)
349 been_there[row][c] = ALLDIRS;
350 }
351 break;
352
353 default:
354 panic("unknown look");
355 }
356 }
357
358 static void
look_around(void)359 look_around(void)
360 {
361 int i;
362
363 for (i = 0; i < NUMDIRECTIONS; i++) {
364 ottolook(i, &flbr[i]);
365 }
366 }
367
368 /*
369 * as a side effect modifies facing and location (row, col)
370 */
371
372 static void
face_and_move_direction(int rel_dir,int distance)373 face_and_move_direction(int rel_dir, int distance)
374 {
375 int old_facing;
376 char cmd;
377
378 old_facing = facing;
379 cmd = DIRKEYS[facing = direction(facing, rel_dir)];
380
381 if (rel_dir != FRONT) {
382 int i;
383 struct item items[NUMDIRECTIONS];
384
385 command[comlen++] = toupper((unsigned char)cmd);
386 if (distance == 0) {
387 /* rotate ottolook's to be in right position */
388 for (i = 0; i < NUMDIRECTIONS; i++)
389 items[i] =
390 flbr[(i + old_facing) % NUMDIRECTIONS];
391 memcpy(flbr, items, sizeof flbr);
392 }
393 }
394 while (distance--) {
395 command[comlen++] = cmd;
396 switch (facing) {
397
398 case NORTH: row--; break;
399 case WEST: col--; break;
400 case SOUTH: row++; break;
401 case EAST: col++; break;
402 }
403 if (distance == 0)
404 look_around();
405 }
406 }
407
408 static void
attack(int rel_dir,struct item * itemp)409 attack(int rel_dir, struct item *itemp)
410 {
411 if (!(itemp->flags & ON_SIDE)) {
412 face_and_move_direction(rel_dir, 0);
413 command[comlen++] = 'o';
414 command[comlen++] = 'o';
415 duck(FRONT);
416 command[comlen++] = ' ';
417 } else if (itemp->distance > 1) {
418 face_and_move_direction(rel_dir, 2);
419 duck(FRONT);
420 } else {
421 face_and_move_direction(rel_dir, 1);
422 if (itemp->flags & ON_LEFT)
423 rel_dir = LEFT;
424 else
425 rel_dir = RIGHT;
426 (void) face_and_move_direction(rel_dir, 0);
427 command[comlen++] = 'f';
428 command[comlen++] = 'f';
429 duck(FRONT);
430 command[comlen++] = ' ';
431 }
432 }
433
434 static void
duck(int rel_dir)435 duck(int rel_dir)
436 {
437 int dir;
438
439 switch (dir = direction(facing, rel_dir)) {
440
441 case NORTH:
442 case SOUTH:
443 if (strchr(PUSHOVER, SCREEN(row, col - 1)) != NULL)
444 command[comlen++] = 'h';
445 else if (strchr(PUSHOVER, SCREEN(row, col + 1)) != NULL)
446 command[comlen++] = 'l';
447 else if (dir == NORTH
448 && strchr(PUSHOVER, SCREEN(row + 1, col)) != NULL)
449 command[comlen++] = 'j';
450 else if (dir == SOUTH
451 && strchr(PUSHOVER, SCREEN(row - 1, col)) != NULL)
452 command[comlen++] = 'k';
453 else if (dir == NORTH)
454 command[comlen++] = 'k';
455 else
456 command[comlen++] = 'j';
457 break;
458
459 case WEST:
460 case EAST:
461 if (strchr(PUSHOVER, SCREEN(row - 1, col)) != NULL)
462 command[comlen++] = 'k';
463 else if (strchr(PUSHOVER, SCREEN(row + 1, col)) != NULL)
464 command[comlen++] = 'j';
465 else if (dir == WEST
466 && strchr(PUSHOVER, SCREEN(row, col + 1)) != NULL)
467 command[comlen++] = 'l';
468 else if (dir == EAST
469 && strchr(PUSHOVER, SCREEN(row, col - 1)) != NULL)
470 command[comlen++] = 'h';
471 else if (dir == WEST)
472 command[comlen++] = 'h';
473 else
474 command[comlen++] = 'l';
475 break;
476 }
477 }
478
479 /*
480 * go for the closest mine if possible
481 */
482
483 static int
go_for_ammo(char mine)484 go_for_ammo(char mine)
485 {
486 int i, rel_dir, dist;
487
488 rel_dir = -1;
489 dist = WIDTH;
490 for (i = 0; i < NUMDIRECTIONS; i++) {
491 if (flbr[i].what == mine && flbr[i].distance < dist) {
492 rel_dir = i;
493 dist = flbr[i].distance;
494 }
495 }
496 if (rel_dir == -1)
497 return FALSE;
498
499 if (!(flbr[rel_dir].flags & ON_SIDE)
500 || flbr[rel_dir].distance > 1) {
501 if (dist > 4)
502 dist = 4;
503 face_and_move_direction(rel_dir, dist);
504 } else
505 return FALSE; /* until it's done right */
506 return TRUE;
507 }
508
509 static void
wander(void)510 wander(void)
511 {
512 int i, j, rel_dir, dir_mask, dir_count;
513
514 for (i = 0; i < NUMDIRECTIONS; i++)
515 if (!(flbr[i].flags & BEEN) || flbr[i].distance <= 1)
516 break;
517 if (i == NUMDIRECTIONS)
518 memset(been_there, 0, sizeof been_there);
519 dir_mask = dir_count = 0;
520 for (i = 0; i < NUMDIRECTIONS; i++) {
521 j = (RIGHT + i) % NUMDIRECTIONS;
522 if (flbr[j].distance <= 1 || flbr[j].flags & DEADEND)
523 continue;
524 if (!(flbr[j].flags & BEEN_SAME)) {
525 dir_mask = 1 << j;
526 dir_count = 1;
527 break;
528 }
529 if (j == FRONT
530 && num_turns > 4 + (arc4random_uniform(
531 ((flbr[FRONT].flags & BEEN) ? 7 : HEIGHT))))
532 continue;
533 dir_mask |= 1 << j;
534 dir_count = 1;
535 break;
536 }
537 if (dir_count == 0) {
538 duck(arc4random_uniform(NUMDIRECTIONS));
539 num_turns = 0;
540 return;
541 } else {
542 rel_dir = ffs(dir_mask) - 1;
543 }
544 if (rel_dir == FRONT)
545 num_turns++;
546 else
547 num_turns = 0;
548
549 face_and_move_direction(rel_dir, 1);
550 }
551
552 /* Otto always re-enters the game, cloaked. */
553 int
otto_quit(int old_status)554 otto_quit(int old_status)
555 {
556 return Q_CLOAK;
557 }
558
559 static void
_panic(const char * file,int line,const char * msg)560 _panic(const char *file, int line, const char *msg)
561 {
562
563 fprintf(stderr, "%s:%d: panic! %s\n", file, line, msg);
564 abort();
565 }
566