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