/* * Copyright (c) 1983-2003, Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * + Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * + Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * + Neither the name of the University of California, San Francisco nor * the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $OpenBSD: otto.c,v 1.9 2006/03/27 00:10:15 tedu Exp $ * $NetBSD: otto.c,v 1.2 1997/10/10 16:32:39 lukem Exp $ * $DragonFly: src/games/hunt/hunt/otto.c,v 1.2 2008/09/04 16:12:51 swildner Exp $ */ /* * otto - a hunt otto-matic player * * This guy is buggy, unfair, stupid, and not extensible. * Future versions of hunt will have a subroutine library for * automatic players to link to. If you write your own "otto" * please let us know what subroutines you would expect in the * subroutine library. */ #include #include #include #include #include #include #include "hunt.h" #include "client.h" #include "display.h" #include #define panic(m) _panic(__FILE__,__LINE__,m) useconds_t Otto_pause = 55000; int Otto_mode; # undef WALL # undef NORTH # undef SOUTH # undef WEST # undef EAST # undef FRONT # undef LEFT # undef BACK # undef RIGHT # define SCREEN(y, x) display_atyx(y, x) # define OPPONENT "{}i!" # define PROPONENT "^v<>" # define WALL "+\\/#*-|" # define PUSHOVER " bg;*#&" # define SHOTS "$@Oo:" /* number of "directions" */ # define NUMDIRECTIONS 4 # define direction(abs,rel) (((abs) + (rel)) % NUMDIRECTIONS) /* absolute directions (facings) - counterclockwise */ # define NORTH 0 # define WEST 1 # define SOUTH 2 # define EAST 3 # define ALLDIRS 0xf /* relative directions - counterclockwise */ # define FRONT 0 # define LEFT 1 # define BACK 2 # define RIGHT 3 # define ABSCHARS "NWSE" # define RELCHARS "FLBR" # define DIRKEYS "khjl" static char command[1024]; /* XXX */ static int comlen; # define DEADEND 0x1 # define ON_LEFT 0x2 # define ON_RIGHT 0x4 # define ON_SIDE (ON_LEFT|ON_RIGHT) # define BEEN 0x8 # define BEEN_SAME 0x10 struct item { char what; int distance; int flags; }; static struct item flbr[NUMDIRECTIONS]; # define fitem flbr[FRONT] # define litem flbr[LEFT] # define bitem flbr[BACK] # define ritem flbr[RIGHT] static int facing; static int row, col; static int num_turns; /* for wandering */ static char been_there[HEIGHT][WIDTH2]; static void attack(int, struct item *); static void duck(int); static void face_and_move_direction(int, int); static int go_for_ammo(char); static void ottolook(int, struct item *); static void look_around(void); static int stop_look(struct item *, char, int, int); static void wander(void); static void _panic(const char *, int, const char *); int otto(int y, int x, char face, char *buf, size_t buflen) { int i; if (usleep(Otto_pause) < 0) panic("usleep"); /* save away parameters so other functions may use/update info */ switch (face) { case '^': facing = NORTH; break; case '<': facing = WEST; break; case 'v': facing = SOUTH; break; case '>': facing = EAST; break; default: panic("unknown face"); } row = y; col = x; been_there[row][col] |= 1 << facing; /* initially no commands to be sent */ comlen = 0; /* find something to do */ look_around(); for (i = 0; i < NUMDIRECTIONS; i++) { if (strchr(OPPONENT, flbr[i].what) != NULL) { attack(i, &flbr[i]); memset(been_there, 0, sizeof been_there); goto done; } } if (strchr(SHOTS, bitem.what) != NULL && !(bitem.what & ON_SIDE)) { duck(BACK); memset(been_there, 0, sizeof been_there); } else if (go_for_ammo(BOOT_PAIR)) { memset(been_there, 0, sizeof been_there); } else if (go_for_ammo(BOOT)) { memset(been_there, 0, sizeof been_there); } else if (go_for_ammo(GMINE)) memset(been_there, 0, sizeof been_there); else if (go_for_ammo(MINE)) memset(been_there, 0, sizeof been_there); else wander(); done: if (comlen) { if (comlen > (int)buflen) panic("not enough buffer space"); memcpy(buf, command, comlen); } return comlen; } static int stop_look(struct item *itemp, char c, int dist, int side) { switch (c) { case SPACE: if (side) itemp->flags &= ~DEADEND; return 0; case MINE: case GMINE: case BOOT: case BOOT_PAIR: if (itemp->distance == -1) { itemp->distance = dist; itemp->what = c; if (side < 0) itemp->flags |= ON_LEFT; else if (side > 0) itemp->flags |= ON_RIGHT; } return 0; case SHOT: case GRENADE: case SATCHEL: case BOMB: case SLIME: if (itemp->distance == -1 || (!side && (itemp->flags & ON_SIDE || itemp->what == GMINE || itemp->what == MINE))) { itemp->distance = dist; itemp->what = c; itemp->flags &= ~ON_SIDE; if (side < 0) itemp->flags |= ON_LEFT; else if (side > 0) itemp->flags |= ON_RIGHT; } return 0; case '{': case '}': case 'i': case '!': itemp->distance = dist; itemp->what = c; itemp->flags &= ~(ON_SIDE|DEADEND); if (side < 0) itemp->flags |= ON_LEFT; else if (side > 0) itemp->flags |= ON_RIGHT; return 1; default: /* a wall or unknown object */ if (side) return 0; if (itemp->distance == -1) { itemp->distance = dist; itemp->what = c; } return 1; } } static void ottolook(int rel_dir, struct item *itemp) { int r, c; char ch; r = 0; itemp->what = 0; itemp->distance = -1; itemp->flags = DEADEND|BEEN; /* true until proven false */ switch (direction(facing, rel_dir)) { case NORTH: if (been_there[row - 1][col] & NORTH) itemp->flags |= BEEN_SAME; for (r = row - 1; r >= 0; r--) for (c = col - 1; c < col + 2; c++) { ch = SCREEN(r, c); if (stop_look(itemp, ch, row - r, c - col)) goto cont_north; if (c == col && !been_there[r][c]) itemp->flags &= ~BEEN; } cont_north: if (itemp->flags & DEADEND) { itemp->flags |= BEEN; if (r >= 0) been_there[r][col] |= NORTH; for (r = row - 1; r > row - itemp->distance; r--) been_there[r][col] = ALLDIRS; } break; case SOUTH: if (been_there[row + 1][col] & SOUTH) itemp->flags |= BEEN_SAME; for (r = row + 1; r < HEIGHT; r++) for (c = col - 1; c < col + 2; c++) { ch = SCREEN(r, c); if (stop_look(itemp, ch, r - row, col - c)) goto cont_south; if (c == col && !been_there[r][c]) itemp->flags &= ~BEEN; } cont_south: if (itemp->flags & DEADEND) { itemp->flags |= BEEN; if (r < HEIGHT) been_there[r][col] |= SOUTH; for (r = row + 1; r < row + itemp->distance; r++) been_there[r][col] = ALLDIRS; } break; case WEST: if (been_there[row][col - 1] & WEST) itemp->flags |= BEEN_SAME; for (c = col - 1; c >= 0; c--) for (r = row - 1; r < row + 2; r++) { ch = SCREEN(r, c); if (stop_look(itemp, ch, col - c, row - r)) goto cont_west; if (r == row && !been_there[r][c]) itemp->flags &= ~BEEN; } cont_west: if (itemp->flags & DEADEND) { itemp->flags |= BEEN; been_there[r][col] |= WEST; for (c = col - 1; c > col - itemp->distance; c--) been_there[row][c] = ALLDIRS; } break; case EAST: if (been_there[row][col + 1] & EAST) itemp->flags |= BEEN_SAME; for (c = col + 1; c < WIDTH; c++) for (r = row - 1; r < row + 2; r++) { ch = SCREEN(r, c); if (stop_look(itemp, ch, c - col, r - row)) goto cont_east; if (r == row && !been_there[r][c]) itemp->flags &= ~BEEN; } cont_east: if (itemp->flags & DEADEND) { itemp->flags |= BEEN; been_there[r][col] |= EAST; for (c = col + 1; c < col + itemp->distance; c++) been_there[row][c] = ALLDIRS; } break; default: panic("unknown look"); } } static void look_around(void) { int i; for (i = 0; i < NUMDIRECTIONS; i++) { ottolook(i, &flbr[i]); } } /* * as a side effect modifies facing and location (row, col) */ static void face_and_move_direction(int rel_dir, int distance) { int old_facing; char cmd; old_facing = facing; cmd = DIRKEYS[facing = direction(facing, rel_dir)]; if (rel_dir != FRONT) { int i; struct item items[NUMDIRECTIONS]; command[comlen++] = toupper(cmd); if (distance == 0) { /* rotate ottolook's to be in right position */ for (i = 0; i < NUMDIRECTIONS; i++) items[i] = flbr[(i + old_facing) % NUMDIRECTIONS]; memcpy(flbr, items, sizeof flbr); } } while (distance--) { command[comlen++] = cmd; switch (facing) { case NORTH: row--; break; case WEST: col--; break; case SOUTH: row++; break; case EAST: col++; break; } if (distance == 0) look_around(); } } static void attack(int rel_dir, struct item *itemp) { if (!(itemp->flags & ON_SIDE)) { face_and_move_direction(rel_dir, 0); command[comlen++] = 'o'; command[comlen++] = 'o'; duck(FRONT); command[comlen++] = ' '; } else if (itemp->distance > 1) { face_and_move_direction(rel_dir, 2); duck(FRONT); } else { face_and_move_direction(rel_dir, 1); if (itemp->flags & ON_LEFT) rel_dir = LEFT; else rel_dir = RIGHT; (void) face_and_move_direction(rel_dir, 0); command[comlen++] = 'f'; command[comlen++] = 'f'; duck(FRONT); command[comlen++] = ' '; } } static void duck(int rel_dir) { int dir; switch (dir = direction(facing, rel_dir)) { case NORTH: case SOUTH: if (strchr(PUSHOVER, SCREEN(row, col - 1)) != NULL) command[comlen++] = 'h'; else if (strchr(PUSHOVER, SCREEN(row, col + 1)) != NULL) command[comlen++] = 'l'; else if (dir == NORTH && strchr(PUSHOVER, SCREEN(row + 1, col)) != NULL) command[comlen++] = 'j'; else if (dir == SOUTH && strchr(PUSHOVER, SCREEN(row - 1, col)) != NULL) command[comlen++] = 'k'; else if (dir == NORTH) command[comlen++] = 'k'; else command[comlen++] = 'j'; break; case WEST: case EAST: if (strchr(PUSHOVER, SCREEN(row - 1, col)) != NULL) command[comlen++] = 'k'; else if (strchr(PUSHOVER, SCREEN(row + 1, col)) != NULL) command[comlen++] = 'j'; else if (dir == WEST && strchr(PUSHOVER, SCREEN(row, col + 1)) != NULL) command[comlen++] = 'l'; else if (dir == EAST && strchr(PUSHOVER, SCREEN(row, col - 1)) != NULL) command[comlen++] = 'h'; else if (dir == WEST) command[comlen++] = 'h'; else command[comlen++] = 'l'; break; } } /* * go for the closest mine if possible */ static int go_for_ammo(char mine) { int i, rel_dir, dist; rel_dir = -1; dist = WIDTH; for (i = 0; i < NUMDIRECTIONS; i++) { if (flbr[i].what == mine && flbr[i].distance < dist) { rel_dir = i; dist = flbr[i].distance; } } if (rel_dir == -1) return FALSE; if (!(flbr[rel_dir].flags & ON_SIDE) || flbr[rel_dir].distance > 1) { if (dist > 4) dist = 4; face_and_move_direction(rel_dir, dist); } else return FALSE; /* until it's done right */ return TRUE; } static void wander(void) { int i, j, rel_dir, dir_mask, dir_count; for (i = 0; i < NUMDIRECTIONS; i++) if (!(flbr[i].flags & BEEN) || flbr[i].distance <= 1) break; if (i == NUMDIRECTIONS) memset(been_there, 0, sizeof been_there); dir_mask = dir_count = 0; for (i = 0; i < NUMDIRECTIONS; i++) { j = (RIGHT + i) % NUMDIRECTIONS; if (flbr[j].distance <= 1 || flbr[j].flags & DEADEND) continue; if (!(flbr[j].flags & BEEN_SAME)) { dir_mask = 1 << j; dir_count = 1; break; } if (j == FRONT && num_turns > 4 + (random() % ((flbr[FRONT].flags & BEEN) ? 7 : HEIGHT))) continue; dir_mask |= 1 << j; dir_count = 1; break; } if (dir_count == 0) { duck(random() % NUMDIRECTIONS); num_turns = 0; return; } else { rel_dir = ffs(dir_mask) - 1; } if (rel_dir == FRONT) num_turns++; else num_turns = 0; face_and_move_direction(rel_dir, 1); } /* Otto always re-enters the game, cloaked. */ int otto_quit(int old_status __unused) { return Q_CLOAK; } static void _panic(const char *file, int line, const char *msg) { fprintf(stderr, "%s:%d: panic! %s\n", file, line, msg); abort(); }