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