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 * + Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * + 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 * + 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 } 570