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