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 * $DragonFly: src/games/mille/move.c,v 1.5 2006/08/27 17:17:23 pavalos Exp $ 32 */ 33 34 #include <termios.h> 35 36 #include "mille.h" 37 #include <unctrl.h> 38 #include <term.h> 39 40 /* 41 * @(#)move.c 1.2 (Berkeley) 3/28/83 42 */ 43 44 #undef CTRL 45 #define CTRL(c) (c - 'A' + 1) 46 47 const char *Movenames[] = { 48 "M_DISCARD", "M_DRAW", "M_PLAY", "M_ORDER" 49 }; 50 51 static void check_go(void); 52 static void getmove(void); 53 static int haspicked(PLAY *); 54 static bool playcard(PLAY *); 55 56 void 57 domove(void) 58 { 59 PLAY *pp; 60 int i, j; 61 bool goodplay; 62 63 pp = &Player[Play]; 64 if (Play == PLAYER) 65 getmove(); 66 else 67 calcmove(); 68 Next = FALSE; 69 goodplay = TRUE; 70 switch (Movetype) { 71 case M_DISCARD: 72 if (haspicked(pp)) { 73 if (pp->hand[Card_no] == C_INIT) 74 if (Card_no == 6) 75 Finished = TRUE; 76 else 77 error("no card there"); 78 else { 79 if (issafety(pp->hand[Card_no])) { 80 error("discard a safety?"); 81 goodplay = FALSE; 82 break; 83 } 84 Discard = pp->hand[Card_no]; 85 pp->hand[Card_no] = C_INIT; 86 Next = TRUE; 87 if (Play == PLAYER) 88 account(Discard); 89 } 90 } 91 else 92 error("must pick first"); 93 break; 94 case M_PLAY: 95 goodplay = playcard(pp); 96 break; 97 case M_DRAW: 98 Card_no = 0; 99 if (Topcard <= Deck) 100 error("no more cards"); 101 else if (haspicked(pp)) 102 error("already picked"); 103 else { 104 pp->hand[0] = *--Topcard; 105 #ifdef DEBUG 106 if (Debug) 107 fprintf(outf, "DOMOVE: Draw %s\n", C_name[*Topcard]); 108 #endif 109 acc: 110 if (Play == COMP) { 111 account(*Topcard); 112 if (issafety(*Topcard)) 113 pp->safety[*Topcard-S_CONV] = S_IN_HAND; 114 } 115 if (pp->hand[1] == C_INIT && Topcard > Deck) { 116 Card_no = 1; 117 pp->hand[1] = *--Topcard; 118 #ifdef DEBUG 119 if (Debug) 120 fprintf(outf, "DOMOVE: Draw %s\n", C_name[*Topcard]); 121 #endif 122 goto acc; 123 } 124 pp->new_battle = FALSE; 125 pp->new_speed = FALSE; 126 } 127 break; 128 129 case M_ORDER: 130 break; 131 } 132 /* 133 * move blank card to top by one of two methods. If the 134 * computer's hand was sorted, the randomness for picking 135 * between equally valued cards would be lost 136 */ 137 if (Order && Movetype != M_DRAW && goodplay && pp == &Player[PLAYER]) 138 sort(pp->hand); 139 else 140 for (i = 1; i < HAND_SZ; i++) 141 if (pp->hand[i] == C_INIT) { 142 for (j = 0; pp->hand[j] == C_INIT; j++) 143 if (j == HAND_SZ - 1) { 144 j = 0; 145 break; 146 } 147 pp->hand[i] = pp->hand[j]; 148 pp->hand[j] = C_INIT; 149 } 150 if (Topcard <= Deck) 151 check_go(); 152 if (Next) 153 nextplay(); 154 } 155 156 /* 157 * Check and see if either side can go. If they cannot, 158 * the game is over 159 */ 160 static void 161 check_go(void) 162 { 163 CARD card; 164 PLAY *pp, *op; 165 int i; 166 167 for (pp = Player; pp < &Player[2]; pp++) { 168 op = (pp == &Player[COMP] ? &Player[PLAYER] : &Player[COMP]); 169 for (i = 0; i < HAND_SZ; i++) { 170 card = pp->hand[i]; 171 if (issafety(card) || canplay(pp, op, card)) { 172 #ifdef DEBUG 173 if (Debug) { 174 fprintf(outf, "CHECK_GO: can play %s (%d), ", C_name[card], card); 175 fprintf(outf, "issafety(card) = %d, ", issafety(card)); 176 fprintf(outf, "canplay(pp, op, card) = %d\n", canplay(pp, op, card)); 177 } 178 #endif 179 return; 180 } 181 #ifdef DEBUG 182 else if (Debug) 183 fprintf(outf, "CHECK_GO: cannot play %s\n", 184 C_name[card]); 185 #endif 186 } 187 } 188 Finished = TRUE; 189 } 190 191 static bool 192 playcard(PLAY *pp) 193 { 194 int v; 195 CARD card; 196 bool blockNext; 197 198 /* 199 * check and see if player has picked 200 */ 201 switch (pp->hand[Card_no]) { 202 default: 203 if (!haspicked(pp)) 204 mustpick: 205 return error("must pick first"); 206 case C_GAS_SAFE: case C_SPARE_SAFE: 207 case C_DRIVE_SAFE: case C_RIGHT_WAY: 208 break; 209 } 210 211 card = pp->hand[Card_no]; 212 #ifdef DEBUG 213 if (Debug) 214 fprintf(outf, "PLAYCARD: Card = %s\n", C_name[card]); 215 #endif 216 blockNext = FALSE; 217 switch (card) { 218 case C_200: 219 if (pp->nummiles[C_200] == 2) 220 return error("only two 200's per hand"); 221 case C_100: case C_75: 222 if (pp->speed == C_LIMIT) 223 return error("limit of 50"); 224 case C_50: 225 if (pp->mileage + Value[card] > End) 226 return error("puts you over %d", End); 227 case C_25: 228 if (!pp->can_go) 229 return error("cannot move now"); 230 pp->nummiles[card]++; 231 v = Value[card]; 232 pp->total += v; 233 pp->hand_tot += v; 234 if ((pp->mileage += v) == End) 235 check_ext(FALSE); 236 break; 237 238 case C_GAS: case C_SPARE: case C_REPAIRS: 239 if (pp->battle != opposite(card)) 240 return error("can't play \"%s\"", C_name[card]); 241 pp->battle = card; 242 if (pp->safety[S_RIGHT_WAY] == S_PLAYED) 243 pp->can_go = TRUE; 244 break; 245 246 case C_GO: 247 if (pp->battle != C_INIT && pp->battle != C_STOP 248 && !isrepair(pp->battle)) 249 return error("cannot play \"Go\" on a \"%s\"", 250 C_name[pp->battle]); 251 pp->battle = C_GO; 252 pp->can_go = TRUE; 253 break; 254 255 case C_END_LIMIT: 256 if (pp->speed != C_LIMIT) 257 return error("not limited"); 258 pp->speed = C_END_LIMIT; 259 break; 260 261 case C_EMPTY: case C_FLAT: case C_CRASH: 262 case C_STOP: 263 pp = &Player[other(Play)]; 264 if (!pp->can_go) 265 return error("opponent cannot go"); 266 else if (pp->safety[safety(card) - S_CONV] == S_PLAYED) 267 protected: 268 return error("opponent is protected"); 269 pp->battle = card; 270 pp->new_battle = TRUE; 271 pp->can_go = FALSE; 272 pp = &Player[Play]; 273 break; 274 275 case C_LIMIT: 276 pp = &Player[other(Play)]; 277 if (pp->speed == C_LIMIT) 278 return error("opponent has limit"); 279 if (pp->safety[S_RIGHT_WAY] == S_PLAYED) 280 goto protected; 281 pp->speed = C_LIMIT; 282 pp->new_speed = TRUE; 283 pp = &Player[Play]; 284 break; 285 286 case C_GAS_SAFE: case C_SPARE_SAFE: 287 case C_DRIVE_SAFE: case C_RIGHT_WAY: 288 if (pp->battle == opposite(card) 289 || (card == C_RIGHT_WAY && pp->speed == C_LIMIT)) { 290 if (!(card == C_RIGHT_WAY && !isrepair(pp->battle))) { 291 pp->battle = C_GO; 292 pp->can_go = TRUE; 293 } 294 if (card == C_RIGHT_WAY && pp->speed == C_LIMIT) 295 pp->speed = C_INIT; 296 if (pp->new_battle 297 || (pp->new_speed && card == C_RIGHT_WAY)) { 298 pp->coups[card - S_CONV] = TRUE; 299 pp->total += SC_COUP; 300 pp->hand_tot += SC_COUP; 301 pp->coupscore += SC_COUP; 302 } 303 } 304 /* 305 * if not coup, must pick first 306 */ 307 else if (pp->hand[0] == C_INIT && Topcard > Deck) 308 goto mustpick; 309 pp->safety[card - S_CONV] = S_PLAYED; 310 pp->total += SC_SAFETY; 311 pp->hand_tot += SC_SAFETY; 312 if ((pp->safescore += SC_SAFETY) == NUM_SAFE * SC_SAFETY) { 313 pp->total += SC_ALL_SAFE; 314 pp->hand_tot += SC_ALL_SAFE; 315 } 316 if (card == C_RIGHT_WAY) { 317 if (pp->speed == C_LIMIT) 318 pp->speed = C_INIT; 319 if (pp->battle == C_STOP || pp->battle == C_INIT) { 320 pp->can_go = TRUE; 321 pp->battle = C_INIT; 322 } 323 if (!pp->can_go && isrepair(pp->battle)) 324 pp->can_go = TRUE; 325 } 326 blockNext = TRUE; 327 break; 328 329 case C_INIT: 330 error("no card there"); 331 blockNext = TRUE; 332 break; 333 } 334 if (pp == &Player[PLAYER]) 335 account(card); 336 pp->hand[Card_no] = C_INIT; 337 Next = !blockNext; 338 return TRUE; 339 } 340 341 static void 342 getmove(void) 343 { 344 char c; 345 #ifdef DEBUG 346 char *sp; 347 #endif 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 perror(buf); 459 setbuf(outf, 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 static int 477 haspicked(PLAY *pp) 478 { 479 int card; 480 481 if (Topcard <= Deck) 482 return TRUE; 483 switch (pp->hand[Card_no]) { 484 case C_GAS_SAFE: case C_SPARE_SAFE: 485 case C_DRIVE_SAFE: case C_RIGHT_WAY: 486 card = 1; 487 break; 488 default: 489 card = 0; 490 break; 491 } 492 return (pp->hand[card] != C_INIT); 493 } 494 495 void 496 account(CARD card) 497 { 498 CARD oppos; 499 500 if (card == C_INIT) 501 return; 502 ++Numseen[card]; 503 if (Play == COMP) 504 switch (card) { 505 case C_GAS_SAFE: 506 case C_SPARE_SAFE: 507 case C_DRIVE_SAFE: 508 oppos = opposite(card); 509 Numgos += Numcards[oppos] - Numseen[oppos]; 510 break; 511 case C_CRASH: 512 case C_FLAT: 513 case C_EMPTY: 514 case C_STOP: 515 Numgos++; 516 break; 517 } 518 } 519 520 void 521 prompt(int promptno) 522 { 523 static const char *names[] = { 524 ">>:Move:", 525 "Really?", 526 "Another hand?", 527 "Another game?", 528 "Save game?", 529 "Same file?", 530 "file:", 531 "Extension?", 532 "Overwrite file?", 533 }; 534 static int last_prompt = -1; 535 536 if (promptno == last_prompt) 537 move(MOVE_Y, MOVE_X + strlen(names[promptno]) + 1); 538 else { 539 move(MOVE_Y, MOVE_X); 540 if (promptno == MOVEPROMPT) 541 standout(); 542 addstr(names[promptno]); 543 if (promptno == MOVEPROMPT) 544 standend(); 545 addch(' '); 546 last_prompt = promptno; 547 } 548 clrtoeol(); 549 } 550 551 void 552 sort(CARD *hand) 553 { 554 CARD *cp, *tp; 555 CARD temp; 556 557 cp = hand; 558 hand += HAND_SZ; 559 for ( ; cp < &hand[-1]; cp++) 560 for (tp = cp + 1; tp < hand; tp++) 561 if (*cp > *tp) { 562 temp = *cp; 563 *cp = *tp; 564 *tp = temp; 565 } 566 } 567