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