Merge branch 'vendor/DIFFUTILS'
[dragonfly.git] / games / mille / comp.c
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  * $DragonFly: src/games/mille/comp.c,v 1.4 2008/06/05 18:06:30 swildner Exp $
32  */
33
34 #include "mille.h"
35
36 /*
37  * @(#)comp.c   1.1 (Berkeley) 4/1/82
38  */
39
40 #define V_VALUABLE      40
41
42 void
43 calcmove(void)
44 {
45         CARD            card;
46         int             *value;
47         PLAY            *pp, *op;
48         bool            foundend, cango, canstop, foundlow;
49         unsigned int    i, count200, badcount, nummin, nummax, diff;
50         int             curmin, curmax;
51         CARD            safe, oppos;
52         int             valbuf[HAND_SZ], count[NUM_CARDS];
53         bool            playit[HAND_SZ];
54
55         wmove(Score, ERR_Y, ERR_X);     /* get rid of error messages    */
56         wclrtoeol(Score);
57         pp = &Player[COMP];
58         op = &Player[PLAYER];
59         safe = 0;
60         cango = 0;
61         canstop = FALSE;
62         foundend = FALSE;
63
64         /* Try for a Coup Forre, and see what we have. */
65         for (i = 0; i < NUM_CARDS; i++)
66                 count[i] = 0;
67         for (i = 0; i < HAND_SZ; i++) {
68                 card = pp->hand[i];
69                 switch (card) {
70                   case C_STOP:  case C_CRASH:
71                   case C_FLAT:  case C_EMPTY:
72                         if ((playit[i] = canplay(pp, op, card)) != 0)
73                                 canstop = TRUE;
74                         goto norm;
75                   case C_LIMIT:
76                         if ((playit[i] = canplay(pp, op, card))
77                             && Numseen[C_25] == Numcards[C_25]
78                             && Numseen[C_50] == Numcards[C_50])
79                                 canstop = TRUE;
80                         goto norm;
81                   case C_25:    case C_50:      case C_75:
82                   case C_100:   case C_200:
83                         if ((playit[i] = canplay(pp, op, card))
84                             && pp->mileage + Value[card] == End)
85                                 foundend = TRUE;
86                         goto norm;
87                   default:
88                         playit[i] = canplay(pp, op, card);
89 norm:
90                         if (playit[i])
91                                 ++cango;
92                         break;
93                   case C_GAS_SAFE:      case C_DRIVE_SAFE:
94                   case C_SPARE_SAFE:    case C_RIGHT_WAY:
95                         if (pp->battle == opposite(card) ||
96                             (pp->speed == C_LIMIT && card == C_RIGHT_WAY)) {
97                                 Movetype = M_PLAY;
98                                 Card_no = i;
99                                 return;
100                         }
101                         ++safe;
102                         playit[i] = TRUE;
103                         break;
104                 }
105                 if (card >= 0)
106                         ++count[card];
107         }
108
109         /* No Coup Forre.  Draw to fill hand, then restart, as needed. */
110         if (pp->hand[0] == C_INIT && Topcard > Deck) {
111                 Movetype = M_DRAW;
112                 return;
113         }
114
115 #ifdef DEBUG
116         if (Debug)
117                 fprintf(outf, "CALCMOVE: cango = %d, canstop = %d, safe = %d\n",
118                         cango, canstop, safe);
119 #endif
120         if (foundend)
121                 foundend = !check_ext(TRUE);
122         for (i = 0; safe && i < HAND_SZ; i++) {
123                 if (issafety(pp->hand[i])) {
124                         if (onecard(op) || (foundend && cango && !canstop)) {
125 #ifdef DEBUG
126                                 if (Debug)
127                                         fprintf(outf,
128                                                 "CALCMOVE: onecard(op) = %d, foundend = %d\n",
129                                                 onecard(op), foundend);
130 #endif
131 playsafe:
132                                 Movetype = M_PLAY;
133                                 Card_no = i;
134                                 return;
135                         }
136                         oppos = opposite(pp->hand[i]);
137                         if (Numseen[oppos] == Numcards[oppos] &&
138                             !(pp->hand[i] == C_RIGHT_WAY &&
139                               Numseen[C_LIMIT] != Numcards[C_LIMIT]))
140                                 goto playsafe;
141                         else if (!cango
142                             && (op->can_go || !pp->can_go || Topcard < Deck)) {
143                                 card = (Topcard - Deck) - roll(1, 10);
144                                 if ((!pp->mileage) != (!op->mileage))
145                                         card -= 7;
146 #ifdef DEBUG
147                                 if (Debug)
148                                         fprintf(outf,
149                                                 "CALCMOVE: card = %d, DECK_SZ / 4 = %d\n",
150                                                 card, DECK_SZ / 4);
151 #endif
152                                 if (card < DECK_SZ / 4)
153                                         goto playsafe;
154                         }
155                         safe--;
156                         playit[i] = cango;
157                 }
158         }
159         if (!pp->can_go && !isrepair(pp->battle))
160                 Numneed[opposite(pp->battle)]++;
161 redoit:
162         foundlow = (cango || count[C_END_LIMIT] != 0
163                           || Numseen[C_LIMIT] == Numcards[C_LIMIT]
164                           || pp->safety[S_RIGHT_WAY] != S_UNKNOWN);
165         foundend = FALSE;
166         count200 = pp->nummiles[C_200];
167         badcount = 0;
168         curmax = -1;
169         curmin = 101;
170         nummin = -1;
171         nummax = -1;
172         value = valbuf;
173         for (i = 0; i < HAND_SZ; i++) {
174                 card = pp->hand[i];
175                 if (issafety(card) || playit[i] == (cango != 0)) {
176 #ifdef DEBUG
177                         if (Debug)
178                                 fprintf(outf, "CALCMOVE: switch(\"%s\")\n",
179                                         C_name[card]);
180 #endif
181                         switch (card) {
182                           case C_25:    case C_50:
183                                 diff = End - pp->mileage;
184                                 /* avoid getting too close */
185                                 if (Topcard > Deck && cango && diff <= 100
186                                     && (int)diff / Value[card] > count[card]
187                                     && (card == C_25 || diff % 50 == 0)) {
188                                         if (card == C_50 && diff - 50 == 25
189                                             && count[C_25] > 0)
190                                                 goto okay;
191                                         *value = 0;
192                                         if (--cango <= 0)
193                                                 goto redoit;
194                                         break;
195                                 }
196 okay:
197                                 *value = (Value[card] >> 3);
198                                 if (pp->speed == C_LIMIT)
199                                         ++*value;
200                                 else
201                                         --*value;
202                                 if (!foundlow
203                                    && (card == C_50 || count[C_50] == 0)) {
204                                         *value = (pp->mileage ? 10 : 20);
205                                         foundlow = TRUE;
206                                 }
207                                 goto miles;
208                           case C_200:
209                                 if (++count200 > 2) {
210                                         *value = 0;
211                                         break;
212                                 }
213                           case C_75:    case C_100:
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
399 onecard(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                   case 50:
422                   case 25:
423                         if (card == -1)
424                                 card = (End - pp->mileage == 25 ? C_25 : C_50);
425                         return Numseen[card] != Numcards[card];
426                 }
427         return FALSE;
428 }
429
430 bool
431 canplay(PLAY *pp, PLAY *op, CARD card)
432 {
433         switch (card) {
434           case C_200:
435                 if (pp->nummiles[C_200] == 2)
436                         break;
437                 /* FALLTHROUGH */
438           case C_75:    case C_100:
439                 if (pp->speed == C_LIMIT)
440                         break;
441                 /* FALLTHROUGH */
442           case C_50:
443                 if (pp->mileage + Value[card] > End)
444                         break;
445                 /* FALLTHROUGH */
446           case C_25:
447                 if (pp->can_go)
448                         return TRUE;
449                 break;
450           case C_EMPTY: case C_FLAT:    case C_CRASH:
451           case C_STOP:
452                 if (op->can_go && op->safety[safety(card) - S_CONV] != S_PLAYED)
453                         return TRUE;
454                 break;
455           case C_LIMIT:
456                 if (op->speed != C_LIMIT &&
457                     op->safety[S_RIGHT_WAY] != S_PLAYED &&
458                     op->mileage + 50 < End)
459                         return TRUE;
460                 break;
461           case C_GAS:   case C_SPARE:   case C_REPAIRS:
462                 if (pp->battle == opposite(card))
463                         return TRUE;
464                 break;
465           case C_GO:
466                 if (!pp->can_go &&
467                     (isrepair(pp->battle) || pp->battle == C_STOP))
468                         return TRUE;
469                 break;
470           case C_END_LIMIT:
471                 if (pp->speed == C_LIMIT)
472                         return TRUE;
473         }
474         return FALSE;
475 }