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