1 /* $NetBSD: move.c,v 1.7 1997/10/12 00:54:21 lukem Exp $ */ 2 3 /* 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)move.c 8.1 (Berkeley) 5/31/93"; 40 #else 41 __RCSID("$NetBSD: move.c,v 1.7 1997/10/12 00:54:21 lukem Exp $"); 42 #endif 43 #endif /* not lint */ 44 45 #include <termios.h> 46 47 #include "mille.h" 48 #ifndef unctrl 49 #include "unctrl.h" 50 #endif 51 52 # ifdef attron 53 # include <term.h> 54 # endif attron 55 56 /* 57 * @(#)move.c 1.2 (Berkeley) 3/28/83 58 */ 59 60 #undef CTRL 61 #define CTRL(c) (c - 'A' + 1) 62 63 char *Movenames[] = { 64 "M_DISCARD", "M_DRAW", "M_PLAY", "M_ORDER" 65 }; 66 67 void 68 domove() 69 { 70 PLAY *pp; 71 int i, j; 72 bool goodplay; 73 74 pp = &Player[Play]; 75 if (Play == PLAYER) 76 getmove(); 77 else 78 calcmove(); 79 Next = FALSE; 80 goodplay = TRUE; 81 switch (Movetype) { 82 case M_DISCARD: 83 if (haspicked(pp)) { 84 if (pp->hand[Card_no] == C_INIT) 85 if (Card_no == 6) 86 Finished = TRUE; 87 else 88 error("no card there"); 89 else { 90 if (issafety(pp->hand[Card_no])) { 91 error("discard a safety?"); 92 goodplay = FALSE; 93 break; 94 } 95 Discard = pp->hand[Card_no]; 96 pp->hand[Card_no] = C_INIT; 97 Next = TRUE; 98 if (Play == PLAYER) 99 account(Discard); 100 } 101 } 102 else 103 error("must pick first"); 104 break; 105 case M_PLAY: 106 goodplay = playcard(pp); 107 break; 108 case M_DRAW: 109 Card_no = 0; 110 if (Topcard <= Deck) 111 error("no more cards"); 112 else if (haspicked(pp)) 113 error("already picked"); 114 else { 115 pp->hand[0] = *--Topcard; 116 #ifdef DEBUG 117 if (Debug) 118 fprintf(outf, "DOMOVE: Draw %s\n", C_name[*Topcard]); 119 #endif 120 acc: 121 if (Play == COMP) { 122 account(*Topcard); 123 if (issafety(*Topcard)) 124 pp->safety[*Topcard-S_CONV] = S_IN_HAND; 125 } 126 if (pp->hand[1] == C_INIT && Topcard > Deck) { 127 Card_no = 1; 128 pp->hand[1] = *--Topcard; 129 #ifdef DEBUG 130 if (Debug) 131 fprintf(outf, "DOMOVE: Draw %s\n", C_name[*Topcard]); 132 #endif 133 goto acc; 134 } 135 pp->new_battle = FALSE; 136 pp->new_speed = FALSE; 137 } 138 break; 139 140 case M_ORDER: 141 break; 142 } 143 /* 144 * move blank card to top by one of two methods. If the 145 * computer's hand was sorted, the randomness for picking 146 * between equally valued cards would be lost 147 */ 148 if (Order && Movetype != M_DRAW && goodplay && pp == &Player[PLAYER]) 149 sort(pp->hand); 150 else 151 for (i = 1; i < HAND_SZ; i++) 152 if (pp->hand[i] == C_INIT) { 153 for (j = 0; pp->hand[j] == C_INIT; j++) 154 if (j >= HAND_SZ) { 155 j = 0; 156 break; 157 } 158 pp->hand[i] = pp->hand[j]; 159 pp->hand[j] = C_INIT; 160 } 161 if (Topcard <= Deck) 162 check_go(); 163 if (Next) 164 nextplay(); 165 } 166 167 /* 168 * Check and see if either side can go. If they cannot, 169 * the game is over 170 */ 171 void 172 check_go() 173 { 174 CARD card; 175 PLAY *pp, *op; 176 int i; 177 178 for (pp = Player; pp < &Player[2]; pp++) { 179 op = (pp == &Player[COMP] ? &Player[PLAYER] : &Player[COMP]); 180 for (i = 0; i < HAND_SZ; i++) { 181 card = pp->hand[i]; 182 if (issafety(card) || canplay(pp, op, card)) { 183 #ifdef DEBUG 184 if (Debug) { 185 fprintf(outf, "CHECK_GO: can play %s (%d), ", C_name[card], card); 186 fprintf(outf, "issafety(card) = %d, ", issafety(card)); 187 fprintf(outf, "canplay(pp, op, card) = %d\n", canplay(pp, op, card)); 188 } 189 #endif 190 return; 191 } 192 #ifdef DEBUG 193 else if (Debug) 194 fprintf(outf, "CHECK_GO: cannot play %s\n", 195 C_name[card]); 196 #endif 197 } 198 } 199 Finished = TRUE; 200 } 201 202 int 203 playcard(pp) 204 PLAY *pp; 205 { 206 int v; 207 CARD card; 208 209 /* 210 * check and see if player has picked 211 */ 212 switch (pp->hand[Card_no]) { 213 default: 214 if (!haspicked(pp)) 215 mustpick: 216 return error("must pick first"); 217 case C_GAS_SAFE: case C_SPARE_SAFE: 218 case C_DRIVE_SAFE: case C_RIGHT_WAY: 219 break; 220 } 221 222 card = pp->hand[Card_no]; 223 #ifdef DEBUG 224 if (Debug) 225 fprintf(outf, "PLAYCARD: Card = %s\n", C_name[card]); 226 #endif 227 Next = FALSE; 228 switch (card) { 229 case C_200: 230 if (pp->nummiles[C_200] == 2) 231 return error("only two 200's per hand"); 232 case C_100: case C_75: 233 if (pp->speed == C_LIMIT) 234 return error("limit of 50"); 235 case C_50: 236 if (pp->mileage + Value[card] > End) 237 return error("puts you over %d", End); 238 case C_25: 239 if (!pp->can_go) 240 return error("cannot move now"); 241 pp->nummiles[card]++; 242 v = Value[card]; 243 pp->total += v; 244 pp->hand_tot += v; 245 if ((pp->mileage += v) == End) 246 check_ext(FALSE); 247 break; 248 249 case C_GAS: case C_SPARE: case C_REPAIRS: 250 if (pp->battle != opposite(card)) 251 return error("can't play \"%s\"", C_name[card]); 252 pp->battle = card; 253 if (pp->safety[S_RIGHT_WAY] == S_PLAYED) 254 pp->can_go = TRUE; 255 break; 256 257 case C_GO: 258 if (pp->battle != C_INIT && pp->battle != C_STOP 259 && !isrepair(pp->battle)) 260 return error("cannot play \"Go\" on a \"%s\"", 261 C_name[pp->battle]); 262 pp->battle = C_GO; 263 pp->can_go = TRUE; 264 break; 265 266 case C_END_LIMIT: 267 if (pp->speed != C_LIMIT) 268 return error("not limited"); 269 pp->speed = C_END_LIMIT; 270 break; 271 272 case C_EMPTY: case C_FLAT: case C_CRASH: 273 case C_STOP: 274 pp = &Player[other(Play)]; 275 if (!pp->can_go) 276 return error("opponent cannot go"); 277 else if (pp->safety[safety(card) - S_CONV] == S_PLAYED) 278 protected: 279 return error("opponent is protected"); 280 pp->battle = card; 281 pp->new_battle = TRUE; 282 pp->can_go = FALSE; 283 pp = &Player[Play]; 284 break; 285 286 case C_LIMIT: 287 pp = &Player[other(Play)]; 288 if (pp->speed == C_LIMIT) 289 return error("opponent has limit"); 290 if (pp->safety[S_RIGHT_WAY] == S_PLAYED) 291 goto protected; 292 pp->speed = C_LIMIT; 293 pp->new_speed = TRUE; 294 pp = &Player[Play]; 295 break; 296 297 case C_GAS_SAFE: case C_SPARE_SAFE: 298 case C_DRIVE_SAFE: case C_RIGHT_WAY: 299 if (pp->battle == opposite(card) 300 || (card == C_RIGHT_WAY && pp->speed == C_LIMIT)) { 301 if (!(card == C_RIGHT_WAY && !isrepair(pp->battle))) { 302 pp->battle = C_GO; 303 pp->can_go = TRUE; 304 } 305 if (card == C_RIGHT_WAY && pp->speed == C_LIMIT) 306 pp->speed = C_INIT; 307 if (pp->new_battle 308 || (pp->new_speed && card == C_RIGHT_WAY)) { 309 pp->coups[card - S_CONV] = TRUE; 310 pp->total += SC_COUP; 311 pp->hand_tot += SC_COUP; 312 pp->coupscore += SC_COUP; 313 } 314 } 315 /* 316 * if not coup, must pick first 317 */ 318 else if (pp->hand[0] == C_INIT && Topcard > Deck) 319 goto mustpick; 320 pp->safety[card - S_CONV] = S_PLAYED; 321 pp->total += SC_SAFETY; 322 pp->hand_tot += SC_SAFETY; 323 if ((pp->safescore += SC_SAFETY) == NUM_SAFE * SC_SAFETY) { 324 pp->total += SC_ALL_SAFE; 325 pp->hand_tot += SC_ALL_SAFE; 326 } 327 if (card == C_RIGHT_WAY) { 328 if (pp->speed == C_LIMIT) 329 pp->speed = C_INIT; 330 if (pp->battle == C_STOP || pp->battle == C_INIT) { 331 pp->can_go = TRUE; 332 pp->battle = C_INIT; 333 } 334 if (!pp->can_go && isrepair(pp->battle)) 335 pp->can_go = TRUE; 336 } 337 Next = -1; 338 break; 339 340 case C_INIT: 341 error("no card there"); 342 Next = -1; 343 break; 344 } 345 if (pp == &Player[PLAYER]) 346 account(card); 347 pp->hand[Card_no] = C_INIT; 348 Next = (Next == (bool)-1 ? FALSE : TRUE); 349 return TRUE; 350 } 351 352 void 353 getmove() 354 { 355 char c; 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 warn("%s", buf); 467 setbuf(outf, (char *)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 /* 482 * return whether or not the player has picked 483 */ 484 int 485 haspicked(pp) 486 PLAY *pp; 487 { 488 int card; 489 490 if (Topcard <= Deck) 491 return TRUE; 492 switch (pp->hand[Card_no]) { 493 case C_GAS_SAFE: case C_SPARE_SAFE: 494 case C_DRIVE_SAFE: case C_RIGHT_WAY: 495 card = 1; 496 break; 497 default: 498 card = 0; 499 break; 500 } 501 return (pp->hand[card] != C_INIT); 502 } 503 504 void 505 account(card) 506 CARD card; 507 { 508 CARD oppos; 509 510 if (card == C_INIT) 511 return; 512 ++Numseen[card]; 513 if (Play == COMP) 514 switch (card) { 515 case C_GAS_SAFE: 516 case C_SPARE_SAFE: 517 case C_DRIVE_SAFE: 518 oppos = opposite(card); 519 Numgos += Numcards[oppos] - Numseen[oppos]; 520 break; 521 case C_CRASH: 522 case C_FLAT: 523 case C_EMPTY: 524 case C_STOP: 525 Numgos++; 526 break; 527 } 528 } 529 530 void 531 prompt(promptno) 532 int promptno; 533 { 534 static char *names[] = { 535 ">>:Move:", 536 "Really?", 537 "Another hand?", 538 "Another game?", 539 "Save game?", 540 "Same file?", 541 "file:", 542 "Extension?", 543 "Overwrite file?", 544 }; 545 static int last_prompt = -1; 546 547 if (promptno == last_prompt) 548 move(MOVE_Y, MOVE_X + strlen(names[promptno]) + 1); 549 else { 550 move(MOVE_Y, MOVE_X); 551 if (promptno == MOVEPROMPT) 552 standout(); 553 addstr(names[promptno]); 554 if (promptno == MOVEPROMPT) 555 standend(); 556 addch(' '); 557 last_prompt = promptno; 558 } 559 clrtoeol(); 560 } 561 562 void 563 sort(hand) 564 CARD *hand; 565 { 566 CARD *cp, *tp; 567 CARD temp; 568 569 cp = hand; 570 hand += HAND_SZ; 571 for ( ; cp < &hand[-1]; cp++) 572 for (tp = cp + 1; tp < hand; tp++) 573 if (*cp > *tp) { 574 temp = *cp; 575 *cp = *tp; 576 *tp = temp; 577 } 578 } 579