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