xref: /dragonfly/games/mille/comp.c (revision ff964c84ebc98df1f03ebbee5c3d32f68bb33f35)
1 /*-
2  * Copyright (c) 1982, 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. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#)comp.c       8.1 (Berkeley) 5/31/93
30  * $FreeBSD: src/games/mille/comp.c,v 1.5 1999/12/12 06:17:24 billf Exp $
31  */
32 
33 #include "mille.h"
34 
35 /*
36  * @(#)comp.c       1.1 (Berkeley) 4/1/82
37  */
38 
39 #define   V_VALUABLE          40
40 
41 void
calcmove(void)42 calcmove(void)
43 {
44           CARD                card;
45           int                 *value;
46           PLAY                *pp, *op;
47           bool                foundend, cango, canstop, foundlow;
48           unsigned int        i, count200, badcount, nummin, nummax, diff;
49           int                 curmin, curmax;
50           CARD                safe, oppos;
51           int                 valbuf[HAND_SZ], count[NUM_CARDS];
52           bool                playit[HAND_SZ];
53 
54           wmove(Score, ERR_Y, ERR_X);   /* get rid of error messages  */
55           wclrtoeol(Score);
56           pp = &Player[COMP];
57           op = &Player[PLAYER];
58           safe = 0;
59           cango = FALSE;
60           canstop = FALSE;
61           foundend = FALSE;
62 
63           /* Try for a Coup Forre, and see what we have. */
64           for (i = 0; i < NUM_CARDS; i++)
65                     count[i] = 0;
66           for (i = 0; i < HAND_SZ; i++) {
67                     card = pp->hand[i];
68                     switch (card) {
69                       case C_STOP:      case C_CRASH:
70                       case C_FLAT:      case C_EMPTY:
71                               if ((playit[i] = canplay(pp, op, card)))
72                                         canstop = TRUE;
73                               goto norm;
74                       case C_LIMIT:
75                               if ((playit[i] = canplay(pp, op, card))
76                                   && Numseen[C_25] == Numcards[C_25]
77                                   && Numseen[C_50] == Numcards[C_50])
78                                         canstop = TRUE;
79                               goto norm;
80                       case C_25:        case C_50:          case C_75:
81                       case C_100:       case C_200:
82                               if ((playit[i] = canplay(pp, op, card))
83                                   && pp->mileage + Value[card] == End)
84                                         foundend = TRUE;
85                               goto norm;
86                       default:
87                               playit[i] = canplay(pp, op, card);
88 norm:
89                               if (playit[i])
90                                         cango = TRUE;
91                               break;
92                       case C_GAS_SAFE:  case C_DRIVE_SAFE:
93                       case C_SPARE_SAFE:          case C_RIGHT_WAY:
94                               if (pp->battle == opposite(card) ||
95                                   (pp->speed == C_LIMIT && card == C_RIGHT_WAY)) {
96                                         Movetype = M_PLAY;
97                                         Card_no = i;
98                                         return;
99                               }
100                               ++safe;
101                               playit[i] = TRUE;
102                               break;
103                     }
104                     if (card >= 0)
105                               ++count[card];
106           }
107 
108           /* No Coup Forre.  Draw to fill hand, then restart, as needed. */
109           if (pp->hand[0] == C_INIT && Topcard > Deck) {
110                     Movetype = M_DRAW;
111                     return;
112           }
113 
114 #ifdef DEBUG
115           if (Debug)
116                     fprintf(outf, "CALCMOVE: cango = %d, canstop = %d, safe = %d\n",
117                               cango, canstop, safe);
118 #endif
119           if (foundend)
120                     foundend = !check_ext(TRUE);
121           for (i = 0; safe && i < HAND_SZ; i++) {
122                     if (issafety(pp->hand[i])) {
123                               if (onecard(op) || (foundend && cango && !canstop)) {
124 #ifdef DEBUG
125                                         if (Debug)
126                                                   fprintf(outf,
127                                                             "CALCMOVE: onecard(op) = %d, foundend = %d\n",
128                                                             onecard(op), foundend);
129 #endif
130 playsafe:
131                                         Movetype = M_PLAY;
132                                         Card_no = i;
133                                         return;
134                               }
135                               oppos = opposite(pp->hand[i]);
136                               if (Numseen[oppos] == Numcards[oppos] &&
137                                   !(pp->hand[i] == C_RIGHT_WAY &&
138                                     Numseen[C_LIMIT] != Numcards[C_LIMIT]))
139                                         goto playsafe;
140                               else if (!cango
141                                   && (op->can_go || !pp->can_go || Topcard < Deck)) {
142                                         card = (Topcard - Deck) - roll(1, 10);
143                                         if ((!pp->mileage) != (!op->mileage))
144                                                   card -= 7;
145 #ifdef DEBUG
146                                         if (Debug)
147                                                   fprintf(outf,
148                                                             "CALCMOVE: card = %d, DECK_SZ / 4 = %d\n",
149                                                             card, DECK_SZ / 4);
150 #endif
151                                         if (card < DECK_SZ / 4)
152                                                   goto playsafe;
153                               }
154                               safe--;
155                               playit[i] = cango;
156                     }
157           }
158           if (!pp->can_go && !isrepair(pp->battle))
159                     Numneed[opposite(pp->battle)]++;
160 redoit:
161           foundlow = (cango || count[C_END_LIMIT] != 0
162                                 || Numseen[C_LIMIT] == Numcards[C_LIMIT]
163                                 || pp->safety[S_RIGHT_WAY] != S_UNKNOWN);
164           foundend = FALSE;
165           count200 = pp->nummiles[C_200];
166           badcount = 0;
167           curmax = -1;
168           curmin = 101;
169           nummin = -1;
170           nummax = -1;
171           value = valbuf;
172           for (i = 0; i < HAND_SZ; i++) {
173                     card = pp->hand[i];
174                     if (issafety(card) || playit[i] == cango) {
175 #ifdef DEBUG
176                               if (Debug)
177                                         fprintf(outf, "CALCMOVE: switch(\"%s\")\n",
178                                                   C_name[card]);
179 #endif
180                               switch (card) {
181                                 case C_25:        case C_50:
182                                         diff = End - pp->mileage;
183                                         /* avoid getting too close */
184                                         if (Topcard > Deck && cango && diff <= 100
185                                             && (int)diff / Value[card] > count[card]
186                                             && (card == C_25 || diff % 50 == 0)) {
187                                                   if (card == C_50 && diff - 50 == 25
188                                                       && count[C_25] > 0)
189                                                             goto okay;
190                                                   *value = 0;
191                                                   cango = FALSE;
192                                                   goto redoit;
193                                         }
194 okay:
195                                         *value = (Value[card] >> 3);
196                                         if (pp->speed == C_LIMIT)
197                                                   ++*value;
198                                         else
199                                                   --*value;
200                                         if (!foundlow
201                                            && (card == C_50 || count[C_50] == 0)) {
202                                                   *value = (pp->mileage ? 10 : 20);
203                                                   foundlow = TRUE;
204                                         }
205                                         goto miles;
206                                 case C_200:
207                                         if (++count200 > 2) {
208                                                   *value = 0;
209                                                   break;
210                                         }
211                                         /* FALLTHROUGH */
212                                 case C_100:
213                                 case C_75:
214                                         *value = (Value[card] >> 3);
215                                         if (pp->speed == C_LIMIT)
216                                                   --*value;
217                                         else
218                                                   ++*value;
219 miles:
220                                         if (pp->mileage + Value[card] > End)
221                                                   *value = (End == 700 ? card : 0);
222                                         else if (pp->mileage + Value[card] == End) {
223                                                   *value = (foundend ? card : V_VALUABLE);
224                                                   foundend = TRUE;
225                                         }
226                                         break;
227                                 case C_END_LIMIT:
228                                         if (pp->safety[S_RIGHT_WAY] != S_UNKNOWN)
229                                                   *value = (pp->safety[S_RIGHT_WAY] ==
230                                                               S_PLAYED ? -1 : 1);
231                                         else if (pp->speed == C_LIMIT &&
232                                                    End - pp->mileage <= 50)
233                                                   *value = 1;
234                                         else if (pp->speed == C_LIMIT
235                                             || Numseen[C_LIMIT] != Numcards[C_LIMIT]) {
236                                                   safe = S_RIGHT_WAY;
237                                                   oppos = C_LIMIT;
238                                                   goto repair;
239                                         }
240                                         else {
241                                                   *value = 0;
242                                                   --count[C_END_LIMIT];
243                                         }
244                                         break;
245                                 case C_REPAIRS:   case C_SPARE:       case C_GAS:
246                                         safe = safety(card) - S_CONV;
247                                         oppos = opposite(card);
248                                         if (pp->safety[safe] != S_UNKNOWN)
249                                                   *value = (pp->safety[safe] ==
250                                                               S_PLAYED ? -1 : 1);
251                                         else if (pp->battle != oppos
252                                             && (Numseen[oppos] == Numcards[oppos] ||
253                                                   Numseen[oppos] + count[card] >
254                                                   Numcards[oppos])) {
255                                                   *value = 0;
256                                                   --count[card];
257                                         }
258                                         else {
259 repair:
260                                                   *value = Numcards[oppos] * 6;
261                                                   *value += Numseen[card] -
262                                                               Numseen[oppos];
263                                                   if (!cango)
264                                                       *value /= (count[card]*count[card]);
265                                                   count[card]--;
266                                         }
267                                         break;
268                                 case C_GO:
269                                         if (pp->safety[S_RIGHT_WAY] != S_UNKNOWN)
270                                                   *value = (pp->safety[S_RIGHT_WAY] ==
271                                                               S_PLAYED ? -1 : 2);
272                                         else if (pp->can_go
273                                          && Numgos + count[C_GO] == Numneed[C_GO]) {
274                                                   *value = 0;
275                                                   --count[C_GO];
276                                         }
277                                         else {
278                                                   *value = Numneed[C_GO] * 3;
279                                                   *value += (Numseen[C_GO] - Numgos);
280                                                   *value /= (count[C_GO] * count[C_GO]);
281                                                   count[C_GO]--;
282                                         }
283                                         break;
284                                 case C_LIMIT:
285                                         if (op->mileage + 50 >= End) {
286                                                   *value = (End == 700 && !cango);
287                                                   break;
288                                         }
289                                         if (canstop || (cango && !op->can_go))
290                                                   *value = 1;
291                                         else {
292                                                   *value = (pp->safety[S_RIGHT_WAY] !=
293                                                               S_UNKNOWN ? 2 : 3);
294                                                   safe = S_RIGHT_WAY;
295                                                   oppos = C_END_LIMIT;
296                                                   goto normbad;
297                                         }
298                                         break;
299                                 case C_CRASH:     case C_EMPTY:       case C_FLAT:
300                                         safe = safety(card) - S_CONV;
301                                         oppos = opposite(card);
302                                         *value = (pp->safety[safe]!=S_UNKNOWN ? 3 : 4);
303 normbad:
304                                         if (op->safety[safe] == S_PLAYED)
305                                                   *value = -1;
306                                         else {
307                                                   *value *= Numneed[oppos] +
308                                                               Numseen[oppos] + 2;
309                                                   if (!pp->mileage || foundend ||
310                                                       onecard(op))
311                                                             *value += 5;
312                                                   if (op->mileage == 0 || onecard(op))
313                                                             *value += 5;
314                                                   if (op->speed == C_LIMIT)
315                                                             *value -= 3;
316                                                   if (cango &&
317                                                       pp->safety[safe] != S_UNKNOWN)
318                                                             *value += 3;
319                                                   if (!cango)
320                                                             *value /= ++badcount;
321                                         }
322                                         break;
323                                 case C_STOP:
324                                         if (op->safety[S_RIGHT_WAY] == S_PLAYED)
325                                                   *value = -1;
326                                         else {
327                                                   *value = (pp->safety[S_RIGHT_WAY] !=
328                                                               S_UNKNOWN ? 3 : 4);
329                                                   *value *= Numcards[C_STOP] +
330                                                               Numseen[C_GO];
331                                                   if (!pp->mileage || foundend ||
332                                                       onecard(op))
333                                                             *value += 5;
334                                                   if (!cango)
335                                                             *value /= ++badcount;
336                                                   if (op->mileage == 0)
337                                                             *value += 5;
338                                                   if ((card == C_LIMIT &&
339                                                        op->speed == C_LIMIT) ||
340                                                       !op->can_go)
341                                                             *value -= 5;
342                                                   if (cango && pp->safety[S_RIGHT_WAY] !=
343                                                                  S_UNKNOWN)
344                                                             *value += 5;
345                                         }
346                                         break;
347                                 case C_GAS_SAFE:  case C_DRIVE_SAFE:
348                                 case C_SPARE_SAFE:          case C_RIGHT_WAY:
349                                         *value = cango ? 0 : 101;
350                                         break;
351                                 case C_INIT:
352                                         *value = 0;
353                                         break;
354                               }
355                     }
356                     else
357                               *value = cango ? 0 : 101;
358                     if (card != C_INIT) {
359                               if (*value >= curmax) {
360                                         nummax = i;
361                                         curmax = *value;
362                               }
363                               if (*value <= curmin) {
364                                         nummin = i;
365                                         curmin = *value;
366                               }
367                     }
368 #ifdef DEBUG
369                     if (Debug)
370                               mvprintw(i + 6, 2, "%3d %-14s", *value,
371                                          C_name[pp->hand[i]]);
372 #endif
373                     value++;
374           }
375           if (!pp->can_go && !isrepair(pp->battle))
376                     Numneed[opposite(pp->battle)]++;
377           if (cango) {
378 play_it:
379                     mvaddstr(MOVE_Y + 1, MOVE_X, "PLAY\n");
380                     Movetype = M_PLAY;
381                     Card_no = nummax;
382           }
383           else {
384                     if (issafety(pp->hand[nummin])) { /* NEVER discard a safety */
385                               nummax = nummin;
386                               goto play_it;
387                     }
388                     mvaddstr(MOVE_Y + 1, MOVE_X, "DISCARD\n");
389                     Movetype = M_DISCARD;
390                     Card_no = nummin;
391           }
392           mvprintw(MOVE_Y + 2, MOVE_X, "%16s", C_name[pp->hand[Card_no]]);
393 }
394 
395 /*
396  * Return true if the given player could conceivably win with his next card.
397  */
398 bool
onecard(const PLAY * pp)399 onecard(const PLAY *pp)
400 {
401           CARD      bat, spd, card;
402 
403           bat = pp->battle;
404           spd = pp->speed;
405           card = -1;
406           if (pp->can_go || ((isrepair(bat) || bat == C_STOP || spd == C_LIMIT) &&
407                                  Numseen[S_RIGHT_WAY] != 0) ||
408               (bat >= 0 && Numseen[safety(bat)] != 0))
409                     switch (End - pp->mileage) {
410                       case 200:
411                               if (pp->nummiles[C_200] == 2)
412                                         return FALSE;
413                               card = C_200;
414                               /* FALLTHROUGH */
415                       case 100:
416                       case 75:
417                               if (card == -1)
418                                         card = (End - pp->mileage == 75 ? C_75 : C_100);
419                               if (spd == C_LIMIT)
420                                         return Numseen[S_RIGHT_WAY] == 0;
421                               /* FALLTHROUGH */
422                       case 50:
423                       case 25:
424                               if (card == -1)
425                                         card = (End - pp->mileage == 25 ? C_25 : C_50);
426                               return Numseen[card] != Numcards[card];
427                     }
428           return FALSE;
429 }
430 
431 bool
canplay(const PLAY * pp,const PLAY * op,CARD card)432 canplay(const PLAY *pp, const PLAY *op, CARD card)
433 {
434           switch (card) {
435             case C_200:
436                     if (pp->nummiles[C_200] == 2)
437                               break;
438                     /* FALLTHROUGH */
439             case C_75:        case C_100:
440                     if (pp->speed == C_LIMIT)
441                               break;
442                     /* FALLTHROUGH */
443             case C_50:
444                     if (pp->mileage + Value[card] > End)
445                               break;
446                     /* FALLTHROUGH */
447             case C_25:
448                     if (pp->can_go)
449                               return TRUE;
450                     break;
451             case C_EMPTY:     case C_FLAT:        case C_CRASH:
452             case C_STOP:
453                     if (op->can_go && op->safety[safety(card) - S_CONV] != S_PLAYED)
454                               return TRUE;
455                     break;
456             case C_LIMIT:
457                     if (op->speed != C_LIMIT &&
458                         op->safety[S_RIGHT_WAY] != S_PLAYED &&
459                         op->mileage + 50 < End)
460                               return TRUE;
461                     break;
462             case C_GAS:       case C_SPARE:       case C_REPAIRS:
463                     if (pp->battle == opposite(card))
464                               return TRUE;
465                     break;
466             case C_GO:
467                     if (!pp->can_go &&
468                         (isrepair(pp->battle) || pp->battle == C_STOP))
469                               return TRUE;
470                     break;
471             case C_END_LIMIT:
472                     if (pp->speed == C_LIMIT)
473                               return TRUE;
474           }
475           return FALSE;
476 }
477