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