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