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