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