games: Massive style(9) cleanup commit. Reduces differences to NetBSD.
[dragonfly.git] / games / hunt / hunt / otto.c
blob0dc1f6b497485e6136a5c0bb072837fc95a8bda1
1 /*-
2 * Copyright (c) 1983-2003, Regents of the University of California.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
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.
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.
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 $
37 * otto - a hunt otto-matic player
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.
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"
56 #include <stdio.h>
57 #define panic(m) _panic(__FILE__,__LINE__,m)
59 useconds_t Otto_pause = 55000;
61 int Otto_mode;
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
73 # define SCREEN(y, x) display_atyx(y, x)
75 # define OPPONENT "{}i!"
76 # define PROPONENT "^v<>"
77 # define WALL "+\\/#*-|"
78 # define PUSHOVER " bg;*#&"
79 # define SHOTS "$@Oo:"
81 /* number of "directions" */
82 # define NUMDIRECTIONS 4
83 # define direction(abs,rel) (((abs) + (rel)) % NUMDIRECTIONS)
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
92 /* relative directions - counterclockwise */
93 # define FRONT 0
94 # define LEFT 1
95 # define BACK 2
96 # define RIGHT 3
98 # define ABSCHARS "NWSE"
99 # define RELCHARS "FLBR"
100 # define DIRKEYS "khjl"
102 static char command[1024]; /* XXX */
103 static int comlen;
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
112 struct item {
113 char what;
114 int distance;
115 int flags;
118 static struct item flbr[NUMDIRECTIONS];
120 # define fitem flbr[FRONT]
121 # define litem flbr[LEFT]
122 # define bitem flbr[BACK]
123 # define ritem flbr[RIGHT]
125 static int facing;
126 static int row, col;
127 static int num_turns; /* for wandering */
128 static char been_there[HEIGHT][WIDTH2];
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 *);
141 otto(int y, int x, char face, char *buf, size_t buflen)
143 int i;
145 if (usleep(Otto_pause) < 0)
146 panic("usleep");
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");
156 row = y; col = x;
157 been_there[row][col] |= 1 << facing;
159 /* initially no commands to be sent */
160 comlen = 0;
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;
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();
186 done:
187 if (comlen) {
188 if (comlen > (int)buflen)
189 panic("not enough buffer space");
190 memcpy(buf, command, comlen);
192 return comlen;
195 static int
196 stop_look(struct item *itemp, char c, int dist, int side)
198 switch (c) {
200 case SPACE:
201 if (side)
202 itemp->flags &= ~DEADEND;
203 return 0;
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;
217 return 0;
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;
235 return 0;
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;
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;
258 return 1;
262 static void
263 ottolook(int rel_dir, struct item *itemp)
265 int r, c;
266 char ch;
268 r = 0;
269 itemp->what = 0;
270 itemp->distance = -1;
271 itemp->flags = DEADEND|BEEN; /* true until proven false */
273 switch (direction(facing, rel_dir)) {
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;
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;
294 break;
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;
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;
315 break;
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;
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;
335 break;
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;
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;
355 break;
357 default:
358 panic("unknown look");
362 static void
363 look_around(void)
365 int i;
367 for (i = 0; i < NUMDIRECTIONS; i++) {
368 ottolook(i, &flbr[i]);
373 * as a side effect modifies facing and location (row, col)
376 static void
377 face_and_move_direction(int rel_dir, int distance)
379 int old_facing;
380 char cmd;
382 old_facing = facing;
383 cmd = DIRKEYS[facing = direction(facing, rel_dir)];
385 if (rel_dir != FRONT) {
386 int i;
387 struct item items[NUMDIRECTIONS];
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);
398 while (distance--) {
399 command[comlen++] = cmd;
400 switch (facing) {
402 case NORTH: row--; break;
403 case WEST: col--; break;
404 case SOUTH: row++; break;
405 case EAST: col++; break;
407 if (distance == 0)
408 look_around();
412 static void
413 attack(int rel_dir, struct item *itemp)
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++] = ' ';
438 static void
439 duck(int rel_dir)
441 int dir;
443 switch (dir = direction(facing, rel_dir)) {
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;
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;
484 * go for the closest mine if possible
487 static int
488 go_for_ammo(char mine)
490 int i, rel_dir, dist;
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;
500 if (rel_dir == -1)
501 return FALSE;
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;
513 static void
514 wander(void)
516 int i, j, rel_dir, dir_mask, dir_count;
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;
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;
541 if (dir_count == 0) {
542 duck(random() % NUMDIRECTIONS);
543 num_turns = 0;
544 return;
545 } else {
546 rel_dir = ffs(dir_mask) - 1;
548 if (rel_dir == FRONT)
549 num_turns++;
550 else
551 num_turns = 0;
553 face_and_move_direction(rel_dir, 1);
556 /* Otto always re-enters the game, cloaked. */
558 otto_quit(int old_status __unused)
560 return Q_CLOAK;
563 static void
564 _panic(const char *file, int line, const char *msg)
567 fprintf(stderr, "%s:%d: panic! %s\n", file, line, msg);
568 abort();