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