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