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