1 /* $NetBSD: otto.c,v 1.15 2009/07/04 07:10:23 dholland Exp $ */ 2 #ifdef OTTO 3 /* 4 * Copyright (c) 1983-2003, Regents of the University of California. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are 9 * met: 10 * 11 * + Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * + Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * + Neither the name of the University of California, San Francisco nor 17 * the names of its contributors may be used to endorse or promote 18 * products derived from this software without specific prior written 19 * permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 22 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * otto - a hunt otto-matic player 36 * 37 * This guy is buggy, unfair, stupid, and not extensible. 38 * Future versions of hunt will have a subroutine library for 39 * automatic players to link to. If you write your own "otto" 40 * please let us know what subroutines you would expect in the 41 * subroutine library. 42 * 43 * Id: otto.c,v 1.14 2003/04/16 06:11:54 gregc Exp 44 */ 45 46 #include <sys/cdefs.h> 47 #ifndef lint 48 __RCSID("$NetBSD: otto.c,v 1.15 2009/07/04 07:10:23 dholland Exp $"); 49 #endif /* not lint */ 50 51 #include <sys/time.h> 52 #include <curses.h> 53 #include <ctype.h> 54 #include <signal.h> 55 #include <stdlib.h> 56 #include <unistd.h> 57 #include "hunt.h" 58 59 #undef WALL 60 #undef NORTH 61 #undef SOUTH 62 #undef WEST 63 #undef EAST 64 #undef FRONT 65 #undef LEFT 66 #undef BACK 67 #undef RIGHT 68 69 #ifdef HPUX 70 #define random rand 71 #endif 72 73 #define SCREEN(y, x) mvinch(y, x) 74 75 #ifndef DEBUG 76 #define STATIC static 77 #else 78 #define STATIC 79 #endif 80 81 #define OPPONENT "{}i!" 82 #define PROPONENT "^v<>" 83 #define WALL "+\\/#*-|" 84 #define PUSHOVER " bg;*#&" 85 #define SHOTS "$@Oo:" 86 87 /* number of "directions" */ 88 #define NUMDIRECTIONS 4 89 90 /* absolute directions (facings) - counterclockwise */ 91 #define NORTH 0 92 #define WEST 1 93 #define SOUTH 2 94 #define EAST 3 95 #define ALLDIRS 0xf 96 97 /* relative directions - counterclockwise */ 98 #define FRONT 0 99 #define LEFT 1 100 #define BACK 2 101 #define RIGHT 3 102 103 #define ABSCHARS "NWSE" 104 #define RELCHARS "FLBR" 105 #define DIRKEYS "khjl" 106 107 STATIC char command[BUFSIZ]; 108 STATIC int comlen; 109 110 #ifdef DEBUG 111 STATIC FILE *debug = NULL; 112 #endif 113 114 #define DEADEND 0x1 115 #define ON_LEFT 0x2 116 #define ON_RIGHT 0x4 117 #define ON_SIDE (ON_LEFT|ON_RIGHT) 118 #define BEEN 0x8 119 #define BEEN_SAME 0x10 120 121 struct item { 122 char what; 123 int distance; 124 int flags; 125 }; 126 127 STATIC struct item flbr[NUMDIRECTIONS]; 128 129 #define fitem flbr[FRONT] 130 #define litem flbr[LEFT] 131 #define bitem flbr[BACK] 132 #define ritem flbr[RIGHT] 133 134 STATIC int facing; 135 STATIC int row, col; 136 STATIC int num_turns; /* for wandering */ 137 STATIC char been_there[HEIGHT][WIDTH2]; 138 STATIC struct itimerval pause_time = { { 0, 0 }, { 0, 55000 }}; 139 140 STATIC void attack(int, struct item *); 141 STATIC void duck(int); 142 STATIC void face_and_move_direction(int, int); 143 STATIC int go_for_ammo(char); 144 STATIC void ottolook(int, struct item *); 145 STATIC void look_around(void); 146 STATIC void nothing(int); 147 STATIC int stop_look(struct item *, char, int, int); 148 STATIC void wander(void); 149 150 extern int Otto_count; 151 152 STATIC void 153 nothing(int dummy __unused) 154 { 155 } 156 157 void 158 otto(int y, int x, char face) 159 { 160 int i; 161 int old_mask; 162 163 #ifdef DEBUG 164 if (debug == NULL) { 165 debug = fopen("bug", "w"); 166 setbuf(debug, NULL); 167 } 168 fprintf(debug, "\n%c(%d,%d)", face, y, x); 169 #endif 170 (void) signal(SIGALRM, nothing); 171 old_mask = sigblock(sigmask(SIGALRM)); 172 setitimer(ITIMER_REAL, &pause_time, NULL); 173 sigpause(old_mask); 174 sigsetmask(old_mask); 175 176 /* save away parameters so other functions may use/update info */ 177 switch (face) { 178 case '^': facing = NORTH; break; 179 case '<': facing = WEST; break; 180 case 'v': facing = SOUTH; break; 181 case '>': facing = EAST; break; 182 default: abort(); 183 } 184 row = y; col = x; 185 been_there[row][col] |= 1 << facing; 186 187 /* initially no commands to be sent */ 188 comlen = 0; 189 190 /* find something to do */ 191 look_around(); 192 for (i = 0; i < NUMDIRECTIONS; i++) { 193 if (strchr(OPPONENT, flbr[i].what) != NULL) { 194 attack(i, &flbr[i]); 195 memset(been_there, 0, sizeof been_there); 196 goto done; 197 } 198 } 199 200 if (strchr(SHOTS, bitem.what) != NULL && !(bitem.what & ON_SIDE)) { 201 duck(BACK); 202 memset(been_there, 0, sizeof been_there); 203 #ifdef BOOTS 204 } else if (go_for_ammo(BOOT_PAIR)) { 205 memset(been_there, 0, sizeof been_there); 206 } else if (go_for_ammo(BOOT)) { 207 memset(been_there, 0, sizeof been_there); 208 #endif 209 } else if (go_for_ammo(GMINE)) 210 memset(been_there, 0, sizeof been_there); 211 else if (go_for_ammo(MINE)) 212 memset(been_there, 0, sizeof been_there); 213 else 214 wander(); 215 216 done: 217 (void) write(Socket, command, comlen); 218 Otto_count += comlen; 219 #ifdef DEBUG 220 (void) fwrite(command, 1, comlen, debug); 221 #endif 222 } 223 224 #define direction(abs,rel) (((abs) + (rel)) % NUMDIRECTIONS) 225 226 STATIC int 227 stop_look(struct item *itemp, char c, int dist, int side) 228 { 229 switch (c) { 230 231 case SPACE: 232 if (side) 233 itemp->flags &= ~DEADEND; 234 return 0; 235 236 case MINE: 237 case GMINE: 238 #ifdef BOOTS 239 case BOOT: 240 case BOOT_PAIR: 241 #endif 242 if (itemp->distance == -1) { 243 itemp->distance = dist; 244 itemp->what = c; 245 if (side < 0) 246 itemp->flags |= ON_LEFT; 247 else if (side > 0) 248 itemp->flags |= ON_RIGHT; 249 } 250 return 0; 251 252 case SHOT: 253 case GRENADE: 254 case SATCHEL: 255 case BOMB: 256 #ifdef OOZE 257 case SLIME: 258 #endif 259 if (itemp->distance == -1 || (!side 260 && (itemp->flags & ON_SIDE 261 || itemp->what == GMINE || itemp->what == MINE))) { 262 itemp->distance = dist; 263 itemp->what = c; 264 itemp->flags &= ~ON_SIDE; 265 if (side < 0) 266 itemp->flags |= ON_LEFT; 267 else if (side > 0) 268 itemp->flags |= ON_RIGHT; 269 } 270 return 0; 271 272 case '{': 273 case '}': 274 case 'i': 275 case '!': 276 itemp->distance = dist; 277 itemp->what = c; 278 itemp->flags &= ~(ON_SIDE|DEADEND); 279 if (side < 0) 280 itemp->flags |= ON_LEFT; 281 else if (side > 0) 282 itemp->flags |= ON_RIGHT; 283 return 1; 284 285 default: 286 /* a wall or unknown object */ 287 if (side) 288 return 0; 289 if (itemp->distance == -1) { 290 itemp->distance = dist; 291 itemp->what = c; 292 } 293 return 1; 294 } 295 } 296 297 STATIC void 298 ottolook(int rel_dir, struct item *itemp) 299 { 300 int r, c; 301 char ch; 302 303 r = 0; 304 itemp->what = 0; 305 itemp->distance = -1; 306 itemp->flags = DEADEND|BEEN; /* true until proven false */ 307 308 switch (direction(facing, rel_dir)) { 309 310 case NORTH: 311 if (been_there[row - 1][col] & NORTH) 312 itemp->flags |= BEEN_SAME; 313 for (r = row - 1; r >= 0; r--) 314 for (c = col - 1; c < col + 2; c++) { 315 ch = SCREEN(r, c); 316 if (stop_look(itemp, ch, row - r, c - col)) 317 goto cont_north; 318 if (c == col && !been_there[r][c]) 319 itemp->flags &= ~BEEN; 320 } 321 cont_north: 322 if (itemp->flags & DEADEND) { 323 itemp->flags |= BEEN; 324 if (r >= 0) 325 been_there[r][col] |= NORTH; 326 for (r = row - 1; r > row - itemp->distance; r--) 327 been_there[r][col] = ALLDIRS; 328 } 329 break; 330 331 case SOUTH: 332 if (been_there[row + 1][col] & SOUTH) 333 itemp->flags |= BEEN_SAME; 334 for (r = row + 1; r < HEIGHT; r++) 335 for (c = col - 1; c < col + 2; c++) { 336 ch = SCREEN(r, c); 337 if (stop_look(itemp, ch, r - row, col - c)) 338 goto cont_south; 339 if (c == col && !been_there[r][c]) 340 itemp->flags &= ~BEEN; 341 } 342 cont_south: 343 if (itemp->flags & DEADEND) { 344 itemp->flags |= BEEN; 345 if (r < HEIGHT) 346 been_there[r][col] |= SOUTH; 347 for (r = row + 1; r < row + itemp->distance; r++) 348 been_there[r][col] = ALLDIRS; 349 } 350 break; 351 352 case WEST: 353 if (been_there[row][col - 1] & WEST) 354 itemp->flags |= BEEN_SAME; 355 for (c = col - 1; c >= 0; c--) 356 for (r = row - 1; r < row + 2; r++) { 357 ch = SCREEN(r, c); 358 if (stop_look(itemp, ch, col - c, row - r)) 359 goto cont_west; 360 if (r == row && !been_there[r][c]) 361 itemp->flags &= ~BEEN; 362 } 363 cont_west: 364 if (itemp->flags & DEADEND) { 365 itemp->flags |= BEEN; 366 been_there[r][col] |= WEST; 367 for (c = col - 1; c > col - itemp->distance; c--) 368 been_there[row][c] = ALLDIRS; 369 } 370 break; 371 372 case EAST: 373 if (been_there[row][col + 1] & EAST) 374 itemp->flags |= BEEN_SAME; 375 for (c = col + 1; c < WIDTH; c++) 376 for (r = row - 1; r < row + 2; r++) { 377 ch = SCREEN(r, c); 378 if (stop_look(itemp, ch, c - col, r - row)) 379 goto cont_east; 380 if (r == row && !been_there[r][c]) 381 itemp->flags &= ~BEEN; 382 } 383 cont_east: 384 if (itemp->flags & DEADEND) { 385 itemp->flags |= BEEN; 386 been_there[r][col] |= EAST; 387 for (c = col + 1; c < col + itemp->distance; c++) 388 been_there[row][c] = ALLDIRS; 389 } 390 break; 391 392 default: 393 abort(); 394 } 395 } 396 397 STATIC void 398 look_around(void) 399 { 400 int i; 401 402 for (i = 0; i < NUMDIRECTIONS; i++) { 403 ottolook(i, &flbr[i]); 404 #ifdef DEBUG 405 fprintf(debug, " ottolook(%c)=%c(%d)(0x%x)", 406 RELCHARS[i], flbr[i].what, flbr[i].distance, flbr[i].flags); 407 #endif 408 } 409 } 410 411 /* 412 * as a side effect modifies facing and location (row, col) 413 */ 414 415 STATIC void 416 face_and_move_direction(int rel_dir, int distance) 417 { 418 int old_facing; 419 char cmd; 420 421 old_facing = facing; 422 cmd = DIRKEYS[facing = direction(facing, rel_dir)]; 423 424 if (rel_dir != FRONT) { 425 int i; 426 struct item items[NUMDIRECTIONS]; 427 428 command[comlen++] = toupper((unsigned char)cmd); 429 if (distance == 0) { 430 /* rotate ottolook's to be in right position */ 431 for (i = 0; i < NUMDIRECTIONS; i++) 432 items[i] = 433 flbr[(i + old_facing) % NUMDIRECTIONS]; 434 memcpy(flbr, items, sizeof flbr); 435 } 436 } 437 while (distance--) { 438 command[comlen++] = cmd; 439 switch (facing) { 440 441 case NORTH: row--; break; 442 case WEST: col--; break; 443 case SOUTH: row++; break; 444 case EAST: col++; break; 445 } 446 if (distance == 0) 447 look_around(); 448 } 449 } 450 451 STATIC void 452 attack(int rel_dir, struct item *itemp) 453 { 454 if (!(itemp->flags & ON_SIDE)) { 455 face_and_move_direction(rel_dir, 0); 456 command[comlen++] = 'o'; 457 command[comlen++] = 'o'; 458 duck(FRONT); 459 command[comlen++] = ' '; 460 } else if (itemp->distance > 1) { 461 face_and_move_direction(rel_dir, 2); 462 duck(FRONT); 463 } else { 464 face_and_move_direction(rel_dir, 1); 465 if (itemp->flags & ON_LEFT) 466 rel_dir = LEFT; 467 else 468 rel_dir = RIGHT; 469 (void) face_and_move_direction(rel_dir, 0); 470 command[comlen++] = 'f'; 471 command[comlen++] = 'f'; 472 duck(FRONT); 473 command[comlen++] = ' '; 474 } 475 } 476 477 STATIC void 478 duck(int rel_dir) 479 { 480 int dir; 481 482 switch (dir = direction(facing, rel_dir)) { 483 484 case NORTH: 485 case SOUTH: 486 if (strchr(PUSHOVER, SCREEN(row, col - 1)) != NULL) 487 command[comlen++] = 'h'; 488 else if (strchr(PUSHOVER, SCREEN(row, col + 1)) != NULL) 489 command[comlen++] = 'l'; 490 else if (dir == NORTH 491 && strchr(PUSHOVER, SCREEN(row + 1, col)) != NULL) 492 command[comlen++] = 'j'; 493 else if (dir == SOUTH 494 && strchr(PUSHOVER, SCREEN(row - 1, col)) != NULL) 495 command[comlen++] = 'k'; 496 else if (dir == NORTH) 497 command[comlen++] = 'k'; 498 else 499 command[comlen++] = 'j'; 500 break; 501 502 case WEST: 503 case EAST: 504 if (strchr(PUSHOVER, SCREEN(row - 1, col)) != NULL) 505 command[comlen++] = 'k'; 506 else if (strchr(PUSHOVER, SCREEN(row + 1, col)) != NULL) 507 command[comlen++] = 'j'; 508 else if (dir == WEST 509 && strchr(PUSHOVER, SCREEN(row, col + 1)) != NULL) 510 command[comlen++] = 'l'; 511 else if (dir == EAST 512 && strchr(PUSHOVER, SCREEN(row, col - 1)) != NULL) 513 command[comlen++] = 'h'; 514 else if (dir == WEST) 515 command[comlen++] = 'h'; 516 else 517 command[comlen++] = 'l'; 518 break; 519 } 520 } 521 522 /* 523 * go for the closest mine if possible 524 */ 525 526 STATIC int 527 go_for_ammo(char mine) 528 { 529 int i, rel_dir, dist; 530 531 rel_dir = -1; 532 dist = WIDTH; 533 for (i = 0; i < NUMDIRECTIONS; i++) { 534 if (flbr[i].what == mine && flbr[i].distance < dist) { 535 rel_dir = i; 536 dist = flbr[i].distance; 537 } 538 } 539 if (rel_dir == -1) 540 return FALSE; 541 542 if (!(flbr[rel_dir].flags & ON_SIDE) 543 || flbr[rel_dir].distance > 1) { 544 if (dist > 4) 545 dist = 4; 546 face_and_move_direction(rel_dir, dist); 547 } else 548 return FALSE; /* until it's done right */ 549 return TRUE; 550 } 551 552 STATIC void 553 wander(void) 554 { 555 int i, j, rel_dir, dir_mask, dir_count; 556 557 for (i = 0; i < NUMDIRECTIONS; i++) 558 if (!(flbr[i].flags & BEEN) || flbr[i].distance <= 1) 559 break; 560 if (i == NUMDIRECTIONS) 561 memset(been_there, 0, sizeof been_there); 562 dir_mask = dir_count = 0; 563 for (i = 0; i < NUMDIRECTIONS; i++) { 564 j = (RIGHT + i) % NUMDIRECTIONS; 565 if (flbr[j].distance <= 1 || flbr[j].flags & DEADEND) 566 continue; 567 if (!(flbr[j].flags & BEEN_SAME)) { 568 dir_mask = 1 << j; 569 dir_count = 1; 570 break; 571 } 572 if (j == FRONT 573 && num_turns > 4 + (random() % 574 ((flbr[FRONT].flags & BEEN) ? 7 : HEIGHT))) 575 continue; 576 dir_mask |= 1 << j; 577 #ifdef notdef 578 dir_count++; 579 #else 580 dir_count = 1; 581 break; 582 #endif 583 } 584 #ifdef notdef 585 if (dir_count == 0) { 586 duck(random() % NUMDIRECTIONS); 587 num_turns = 0; 588 return; 589 } else if (dir_count == 1) 590 #endif 591 rel_dir = ffs(dir_mask) - 1; 592 #ifdef notdef 593 else { 594 rel_dir = ffs(dir_mask) - 1; 595 dir_mask &= ~(1 << rel_dir); 596 while (dir_mask != 0) { 597 i = ffs(dir_mask) - 1; 598 if (random() % 5 == 0) 599 rel_dir = i; 600 dir_mask &= ~(1 << i); 601 } 602 } 603 #endif 604 if (rel_dir == FRONT) 605 num_turns++; 606 else 607 num_turns = 0; 608 609 #ifdef DEBUG 610 fprintf(debug, " w(%c)", RELCHARS[rel_dir]); 611 #endif 612 face_and_move_direction(rel_dir, 1); 613 } 614 615 #endif /* OTTO */ 616