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