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
domove(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
check_go(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
playcard(PLAY * pp)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
getmove(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
haspicked(const PLAY * pp)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
account(CARD card)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
prompt(int promptno)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
sort(CARD * hand)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