1 /* $NetBSD: move.c,v 1.19 2019/02/03 03:19:25 mrg Exp $ */ 2 3 /* 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. 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 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)move.c 8.1 (Berkeley) 5/31/93"; 36 #else 37 __RCSID("$NetBSD: move.c,v 1.19 2019/02/03 03:19:25 mrg Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <termios.h> 42 43 #ifdef DEBUG 44 #include <sys/param.h> 45 #endif 46 47 #include "mille.h" 48 #ifndef unctrl 49 #include "unctrl.h" 50 #endif 51 52 /* 53 * @(#)move.c 1.2 (Berkeley) 3/28/83 54 */ 55 56 #undef CTRL 57 #define CTRL(c) (c - 'A' + 1) 58 59 static void check_go(void); 60 static int playcard(PLAY *); 61 static void getmove(void); 62 static int haspicked(const PLAY *); 63 64 void 65 domove(void) 66 { 67 PLAY *pp; 68 int i, j; 69 bool goodplay; 70 71 pp = &Player[Play]; 72 for (i = 0, j = 0; i < HAND_SZ; i++) 73 if (pp->hand[i] != -1) 74 j++; 75 if (!j) { 76 nextplay(); 77 return; 78 } 79 if (Play == PLAYER) 80 getmove(); 81 else 82 calcmove(); 83 Next = FALSE; 84 goodplay = TRUE; 85 switch (Movetype) { 86 case M_DISCARD: 87 if (haspicked(pp)) { 88 if (pp->hand[Card_no] == C_INIT) 89 if (Card_no == 6) 90 Finished = TRUE; 91 else 92 error("no card there"); 93 else { 94 if (is_safety(pp->hand[Card_no])) { 95 error("discard a safety?"); 96 goodplay = FALSE; 97 break; 98 } 99 Discard = pp->hand[Card_no]; 100 pp->hand[Card_no] = C_INIT; 101 Next = TRUE; 102 if (Play == PLAYER) 103 account(Discard); 104 } 105 } 106 else 107 error("must pick first"); 108 break; 109 case M_PLAY: 110 goodplay = playcard(pp); 111 break; 112 case M_DRAW: 113 Card_no = 0; 114 if (Topcard <= Deck) 115 error("no more cards"); 116 else if (haspicked(pp)) 117 error("already picked"); 118 else { 119 pp->hand[0] = *--Topcard; 120 #ifdef DEBUG 121 if (Debug) 122 fprintf(outf, "DOMOVE: Draw %s\n", C_name[*Topcard]); 123 #endif 124 acc: 125 if (Play == COMP) { 126 account(*Topcard); 127 if (is_safety(*Topcard)) 128 pp->safety[*Topcard-S_CONV] = S_IN_HAND; 129 } 130 if (pp->hand[1] == C_INIT && Topcard > Deck) { 131 Card_no = 1; 132 pp->hand[1] = *--Topcard; 133 #ifdef DEBUG 134 if (Debug) 135 fprintf(outf, "DOMOVE: Draw %s\n", C_name[*Topcard]); 136 #endif 137 goto acc; 138 } 139 pp->new_battle = FALSE; 140 pp->new_speed = FALSE; 141 } 142 break; 143 144 case M_ORDER: 145 break; 146 } 147 /* 148 * move blank card to top by one of two methods. If the 149 * computer's hand was sorted, the randomness for picking 150 * between equally valued cards would be lost 151 */ 152 if (Order && Movetype != M_DRAW && goodplay && pp == &Player[PLAYER]) 153 sort(pp->hand); 154 else 155 for (i = 1; i < HAND_SZ; i++) 156 if (pp->hand[i] == C_INIT) { 157 for (j = 0; pp->hand[j] == C_INIT; j++) 158 if (j >= HAND_SZ) { 159 j = 0; 160 break; 161 } 162 pp->hand[i] = pp->hand[j]; 163 pp->hand[j] = C_INIT; 164 } 165 if (Topcard <= Deck) 166 check_go(); 167 if (Next) 168 nextplay(); 169 } 170 171 /* 172 * Check and see if either side can go. If they cannot, 173 * the game is over 174 */ 175 static void 176 check_go(void) 177 { 178 CARD card; 179 PLAY *pp, *op; 180 int i; 181 182 for (pp = Player; pp < &Player[2]; pp++) { 183 op = (pp == &Player[COMP] ? &Player[PLAYER] : &Player[COMP]); 184 for (i = 0; i < HAND_SZ; i++) { 185 card = pp->hand[i]; 186 if (is_safety(card) || canplay(pp, op, card)) { 187 #ifdef DEBUG 188 if (Debug) { 189 fprintf(outf, "CHECK_GO: can play %s (%d), ", C_name[card], card); 190 fprintf(outf, "is_safety(card) = %d, ", is_safety(card)); 191 fprintf(outf, "canplay(pp, op, card) = %d\n", canplay(pp, op, card)); 192 } 193 #endif 194 return; 195 } 196 #ifdef DEBUG 197 else if (Debug) 198 fprintf(outf, "CHECK_GO: cannot play %s\n", 199 C_name[card]); 200 #endif 201 } 202 } 203 Finished = TRUE; 204 } 205 206 static int 207 playcard(PLAY *pp) 208 { 209 int v; 210 CARD card; 211 212 /* 213 * check and see if player has picked 214 */ 215 switch (pp->hand[Card_no]) { 216 default: 217 if (!haspicked(pp)) 218 mustpick: 219 return error("must pick first"); 220 case C_GAS_SAFE: case C_SPARE_SAFE: 221 case C_DRIVE_SAFE: case C_RIGHT_WAY: 222 break; 223 } 224 225 card = pp->hand[Card_no]; 226 #ifdef DEBUG 227 if (Debug) 228 fprintf(outf, "PLAYCARD: Card = %s\n", C_name[card]); 229 #endif 230 Next = FALSE; 231 switch (card) { 232 case C_200: 233 if (pp->nummiles[C_200] == 2) 234 return error("only two 200's per hand"); 235 /* FALLTHROUGH */ 236 case C_100: case C_75: 237 if (pp->speed == C_LIMIT) 238 return error("limit of 50"); 239 /* FALLTHROUGH */ 240 case C_50: 241 if (pp->mileage + Value[card] > End) 242 return error("puts you over %d", End); 243 /* FALLTHROUGH */ 244 case C_25: 245 if (!pp->can_go) 246 return error("cannot move now"); 247 pp->nummiles[card]++; 248 v = Value[card]; 249 pp->total += v; 250 pp->hand_tot += v; 251 if ((pp->mileage += v) == End) 252 check_ext(FALSE); 253 break; 254 255 case C_GAS: case C_SPARE: case C_REPAIRS: 256 if (pp->battle != opposite(card)) 257 return error("can't play \"%s\"", C_name[card]); 258 pp->battle = card; 259 if (pp->safety[S_RIGHT_WAY] == S_PLAYED) 260 pp->can_go = TRUE; 261 break; 262 263 case C_GO: 264 if (pp->battle != C_INIT && pp->battle != C_STOP 265 && !is_repair(pp->battle)) 266 return error("cannot play \"Go\" on a \"%s\"", 267 C_name[pp->battle]); 268 pp->battle = C_GO; 269 pp->can_go = TRUE; 270 break; 271 272 case C_END_LIMIT: 273 if (pp->speed != C_LIMIT) 274 return error("not limited"); 275 pp->speed = C_END_LIMIT; 276 break; 277 278 case C_EMPTY: case C_FLAT: case C_CRASH: 279 case C_STOP: 280 pp = &Player[other(Play)]; 281 if (!pp->can_go) 282 return error("opponent cannot go"); 283 else if (pp->safety[safety(card) - S_CONV] == S_PLAYED) 284 protected: 285 return error("opponent is protected"); 286 pp->battle = card; 287 pp->new_battle = TRUE; 288 pp->can_go = FALSE; 289 pp = &Player[Play]; 290 break; 291 292 case C_LIMIT: 293 pp = &Player[other(Play)]; 294 if (pp->speed == C_LIMIT) 295 return error("opponent has limit"); 296 if (pp->safety[S_RIGHT_WAY] == S_PLAYED) 297 goto protected; 298 pp->speed = C_LIMIT; 299 pp->new_speed = TRUE; 300 pp = &Player[Play]; 301 break; 302 303 case C_GAS_SAFE: case C_SPARE_SAFE: 304 case C_DRIVE_SAFE: case C_RIGHT_WAY: 305 if (pp->battle == opposite(card) 306 || (card == C_RIGHT_WAY && pp->speed == C_LIMIT)) { 307 if (!(card == C_RIGHT_WAY && !is_repair(pp->battle))) { 308 pp->battle = C_GO; 309 pp->can_go = TRUE; 310 } 311 if (card == C_RIGHT_WAY && pp->speed == C_LIMIT) 312 pp->speed = C_INIT; 313 if (pp->new_battle 314 || (pp->new_speed && card == C_RIGHT_WAY)) { 315 pp->coups[card - S_CONV] = TRUE; 316 pp->total += SC_COUP; 317 pp->hand_tot += SC_COUP; 318 pp->coupscore += SC_COUP; 319 } 320 } 321 /* 322 * if not coup, must pick first 323 */ 324 else if (pp->hand[0] == C_INIT && Topcard > Deck) 325 goto mustpick; 326 pp->safety[card - S_CONV] = S_PLAYED; 327 pp->total += SC_SAFETY; 328 pp->hand_tot += SC_SAFETY; 329 if ((pp->safescore += SC_SAFETY) == NUM_SAFE * SC_SAFETY) { 330 pp->total += SC_ALL_SAFE; 331 pp->hand_tot += SC_ALL_SAFE; 332 } 333 if (card == C_RIGHT_WAY) { 334 if (pp->speed == C_LIMIT) 335 pp->speed = C_INIT; 336 if (pp->battle == C_STOP || pp->battle == C_INIT) { 337 pp->can_go = TRUE; 338 pp->battle = C_INIT; 339 } 340 if (!pp->can_go && is_repair(pp->battle)) 341 pp->can_go = TRUE; 342 } 343 Next = -1; 344 break; 345 346 case C_INIT: 347 error("no card there"); 348 Next = -1; 349 break; 350 } 351 if (pp == &Player[PLAYER]) 352 account(card); 353 pp->hand[Card_no] = C_INIT; 354 Next = (Next == (bool)-1 ? FALSE : TRUE); 355 return TRUE; 356 } 357 358 static void 359 getmove(void) 360 { 361 char c; 362 #ifdef EXTRAP 363 static bool last_ex = FALSE; /* set if last command was E */ 364 365 if (last_ex) { 366 undoex(); 367 prboard(); 368 last_ex = FALSE; 369 } 370 #endif 371 for (;;) { 372 prompt(MOVEPROMPT); 373 leaveok(Board, FALSE); 374 refresh(); 375 while ((c = readch()) == killchar() || c == erasechar()) 376 continue; 377 if (islower((unsigned char)c)) 378 c = toupper((unsigned char)c); 379 if (isprint((unsigned char)c) && !isspace((unsigned char)c)) { 380 addch(c); 381 refresh(); 382 } 383 switch (c) { 384 case 'P': /* Pick */ 385 Movetype = M_DRAW; 386 goto ret; 387 case 'U': /* Use Card */ 388 case 'D': /* Discard Card */ 389 if ((Card_no = getcard()) < 0) 390 break; 391 Movetype = (c == 'U' ? M_PLAY : M_DISCARD); 392 goto ret; 393 case 'O': /* Order */ 394 Order = !Order; 395 if (Window == W_SMALL) { 396 if (!Order) 397 mvwaddstr(Score, 12, 21, 398 "o: order hand"); 399 else 400 mvwaddstr(Score, 12, 21, 401 "o: stop ordering"); 402 wclrtoeol(Score); 403 } 404 Movetype = M_ORDER; 405 goto ret; 406 case 'Q': /* Quit */ 407 rub(0); /* Same as a rubout */ 408 break; 409 case 'W': /* Window toggle */ 410 Window = nextwin(Window); 411 newscore(); 412 prscore(TRUE); 413 wrefresh(Score); 414 break; 415 case 'R': /* Redraw screen */ 416 case CTRL('L'): 417 wrefresh(curscr); 418 break; 419 case 'S': /* Save game */ 420 On_exit = FALSE; 421 save(); 422 break; 423 case 'E': /* Extrapolate */ 424 #ifdef EXTRAP 425 if (last_ex) 426 break; 427 Finished = TRUE; 428 if (Window != W_FULL) 429 newscore(); 430 prscore(FALSE); 431 wrefresh(Score); 432 last_ex = TRUE; 433 Finished = FALSE; 434 #else 435 error("%c: command not implemented", c); 436 #endif 437 break; 438 case '\r': /* Ignore RETURNs and */ 439 case '\n': /* Line Feeds */ 440 case ' ': /* Spaces */ 441 case '\0': /* and nulls */ 442 break; 443 #ifdef DEBUG 444 case 'Z': /* Debug code */ 445 if (!Debug && outf == NULL) { 446 char buf[MAXPATHLEN]; 447 char *sp; 448 449 prompt(FILEPROMPT); 450 leaveok(Board, FALSE); 451 refresh(); 452 over: 453 sp = buf; 454 while ((*sp = readch()) != '\n') { 455 if (*sp == killchar()) 456 goto over; 457 else if (*sp == erasechar()) { 458 if (--sp < buf) 459 sp = buf; 460 else { 461 addch('\b'); 462 if (*sp < ' ') 463 addch('\b'); 464 clrtoeol(); 465 } 466 } 467 else 468 addstr(unctrl(*sp++)); 469 refresh(); 470 } 471 *sp = '\0'; 472 leaveok(Board, TRUE); 473 if ((outf = fopen(buf, "w")) == NULL) 474 warn("%s", buf); 475 setbuf(outf, NULL); 476 } 477 Debug = !Debug; 478 break; 479 #endif 480 default: 481 error("unknown command: %s", unctrl(c)); 482 break; 483 } 484 } 485 ret: 486 leaveok(Board, TRUE); 487 } 488 489 /* 490 * return whether or not the player has picked 491 */ 492 static int 493 haspicked(const PLAY *pp) 494 { 495 int card; 496 497 if (Topcard <= Deck) 498 return TRUE; 499 switch (pp->hand[Card_no]) { 500 case C_GAS_SAFE: case C_SPARE_SAFE: 501 case C_DRIVE_SAFE: case C_RIGHT_WAY: 502 card = 1; 503 break; 504 default: 505 card = 0; 506 break; 507 } 508 return (pp->hand[card] != C_INIT); 509 } 510 511 void 512 account(CARD card) 513 { 514 CARD oppos; 515 516 if (card == C_INIT) 517 return; 518 ++Numseen[card]; 519 if (Play == COMP) 520 switch (card) { 521 case C_GAS_SAFE: 522 case C_SPARE_SAFE: 523 case C_DRIVE_SAFE: 524 oppos = opposite(card); 525 Numgos += Numcards[oppos] - Numseen[oppos]; 526 break; 527 case C_CRASH: 528 case C_FLAT: 529 case C_EMPTY: 530 case C_STOP: 531 Numgos++; 532 break; 533 } 534 } 535 536 void 537 prompt(int promptno) 538 { 539 static const char *const names[] = { 540 ">>:Move:", 541 "Really?", 542 "Another hand?", 543 "Another game?", 544 "Save game?", 545 "Same file?", 546 "file:", 547 "Extension?", 548 "Overwrite file?", 549 }; 550 static int last_prompt = -1; 551 552 if (promptno == last_prompt) 553 move(MOVE_Y, MOVE_X + strlen(names[promptno]) + 1); 554 else { 555 move(MOVE_Y, MOVE_X); 556 if (promptno == MOVEPROMPT) 557 standout(); 558 addstr(names[promptno]); 559 if (promptno == MOVEPROMPT) 560 standend(); 561 addch(' '); 562 last_prompt = promptno; 563 } 564 clrtoeol(); 565 } 566 567 void 568 sort(CARD *hand) 569 { 570 CARD *cp, *tp; 571 CARD temp; 572 573 cp = hand; 574 hand += HAND_SZ; 575 for ( ; cp < &hand[-1]; cp++) 576 for (tp = cp + 1; tp < hand; tp++) 577 if (*cp > *tp) { 578 temp = *cp; 579 *cp = *tp; 580 *tp = temp; 581 } 582 } 583