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