move.c revision 1.4
1/* $OpenBSD: move.c,v 1.4 1998/09/22 04:08:24 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.4 1998/09/22 04:08:24 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# ifdef attron 53# include <term.h> 54# define _tty cur_term->Nttyb 55# endif attron 56 57/* 58 * @(#)move.c 1.2 (Berkeley) 3/28/83 59 */ 60 61#undef CTRL 62#define CTRL(c) (c - 'A' + 1) 63 64char *Movenames[] = { 65 "M_DISCARD", "M_DRAW", "M_PLAY", "M_ORDER" 66 }; 67 68void 69domove() 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 121acc: 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 */ 172void 173check_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 203int 204playcard(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)) 216mustpick: 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) 279protected: 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 == (bool)-1 ? FALSE : TRUE); 350 return TRUE; 351} 352 353void 354getmove() 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]; 445over: 446 prompt(FILEPROMPT); 447 leaveok(Board, FALSE); 448 refresh(); 449 sp = buf; 450 while ((*sp = readch()) != '\n' && *sp != '\r' 451 && (sp - buf < sizeof(buf))) { 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, (char *)NULL); 473 } 474 Debug = !Debug; 475 break; 476#endif 477 default: 478 error("unknown command: %s", unctrl(c)); 479 break; 480 } 481 } 482ret: 483 leaveok(Board, TRUE); 484} 485 486/* 487 * return whether or not the player has picked 488 */ 489int 490haspicked(pp) 491 PLAY *pp; 492{ 493 int card; 494 495 if (Topcard <= Deck) 496 return TRUE; 497 switch (pp->hand[Card_no]) { 498 case C_GAS_SAFE: case C_SPARE_SAFE: 499 case C_DRIVE_SAFE: case C_RIGHT_WAY: 500 card = 1; 501 break; 502 default: 503 card = 0; 504 break; 505 } 506 return (pp->hand[card] != C_INIT); 507} 508 509void 510account(card) 511 CARD card; 512{ 513 CARD oppos; 514 515 if (card == C_INIT) 516 return; 517 ++Numseen[card]; 518 if (Play == COMP) 519 switch (card) { 520 case C_GAS_SAFE: 521 case C_SPARE_SAFE: 522 case C_DRIVE_SAFE: 523 oppos = opposite(card); 524 Numgos += Numcards[oppos] - Numseen[oppos]; 525 break; 526 case C_CRASH: 527 case C_FLAT: 528 case C_EMPTY: 529 case C_STOP: 530 Numgos++; 531 break; 532 } 533} 534 535void 536prompt(promptno) 537 int promptno; 538{ 539 static char *names[] = { 540 ">>:Move:", 541 "Really?", 542 "Another hand?", 543 "Another game?", 544 "Save game?", 545 "Same file?", 546 "file:", 547 "Extension?", 548 "Overwrite file?", 549 }; 550 static int last_prompt = -1; 551 552 if (promptno == last_prompt) 553 move(MOVE_Y, MOVE_X + strlen(names[promptno]) + 1); 554 else { 555 move(MOVE_Y, MOVE_X); 556 if (promptno == MOVEPROMPT) 557 standout(); 558 addstr(names[promptno]); 559 if (promptno == MOVEPROMPT) 560 standend(); 561 addch(' '); 562 last_prompt = promptno; 563 } 564 clrtoeol(); 565} 566 567void 568sort(hand) 569 CARD *hand; 570{ 571 CARD *cp, *tp; 572 CARD temp; 573 574 cp = hand; 575 hand += HAND_SZ; 576 for ( ; cp < &hand[-1]; cp++) 577 for (tp = cp + 1; tp < hand; tp++) 578 if (*cp > *tp) { 579 temp = *cp; 580 *cp = *tp; 581 *tp = temp; 582 } 583} 584