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