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