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