games: Remove (void) casts.
[dragonfly.git] / games / hunt / hunt / otto.c
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
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
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
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
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
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
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
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
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
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
557 otto_quit(int old_status __unused)
558 {
559         return Q_CLOAK;
560 }
561
562 static void
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 }