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