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