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