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