1 /*
2  * eval.c
3  *
4  * by Gary Wong <gtw@gnu.org>, 1998, 1999, 2000, 2001, 2002.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of version 3 or later of the GNU General Public License as
8  * published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  * $Id: eval.c,v 1.477 2018/04/28 22:14:46 plm Exp $
20  */
21 
22 #include "config.h"
23 #include "backgammon.h"
24 #include <glib.h>
25 #include <glib/gstdio.h>
26 #include <locale.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include "isaac.h"
31 #include <md5.h>
32 #include "bearoffgammon.h"
33 #include "positionid.h"
34 #include "matchid.h"
35 #include "matchequity.h"
36 #include "format.h"
37 #include "simd.h"
38 #include "multithread.h"
39 #include "util.h"
40 #include "lib/simd.h"
41 #include "glib-ext.h"
42 
43 typedef void (*classstatusfunc) (char *szOutput);
44 typedef int (*cfunc) (const void *, const void *);
45 
46 /* Race inputs */
47 enum {
48     /* In a race position, bar and the 24 points are always empty, so only */
49     /* 23*4 (92) are needed */
50 
51     /* (0 <= k < 14), RI_OFF + k = */
52     /*                       1 if exactly k+1 checkers are off, 0 otherwise */
53 
54     RI_OFF = 92,
55 
56     /* Number of cross-overs by outside checkers */
57 
58     RI_NCROSS = 92 + 14,
59 
60     HALF_RACE_INPUTS
61 };
62 
63 
64 /* Contact inputs -- see Berliner for most of these */
65 enum {
66     /* n - number of checkers off
67      *
68      * off1 -  1         n >= 5
69      * n/5       otherwise
70      *
71      * off2 -  1         n >= 10
72      * (n-5)/5   n < 5 < 10
73      * 0         otherwise
74      *
75      * off3 -  (n-10)/5  n > 10
76      * 0         otherwise
77      */
78 
79     I_OFF1, I_OFF2, I_OFF3,
80 
81     /* Minimum number of pips required to break contact.
82      *
83      * For each checker x, N(x) is checker location,
84      * C(x) is max({forall o : N(x) - N(o)}, 0)
85      *
86      * Break Contact : (sum over x of C(x)) / 152
87      *
88      * 152 is dgree of contact of start position.
89      */
90     I_BREAK_CONTACT,
91 
92     /* Location of back checker (Normalized to [01])
93      */
94     I_BACK_CHEQUER,
95 
96     /* Location of most backward anchor.  (Normalized to [01])
97      */
98     I_BACK_ANCHOR,
99 
100     /* Forward anchor in opponents home.
101      *
102      * Normalized in the following way:  If there is an anchor in opponents
103      * home at point k (1 <= k <= 6), value is k/6. Otherwise, if there is an
104      * anchor in points (7 <= k <= 12), take k/6 as well. Otherwise set to 2.
105      *
106      * This is an attempt for some continuity, since a 0 would be the "same" as
107      * a forward anchor at the bar.
108      */
109     I_FORWARD_ANCHOR,
110 
111     /* Average number of pips opponent loses from hits.
112      *
113      * Some heuristics are required to estimate it, since we have no idea what
114      * the best move actually is.
115      *
116      * 1. If board is weak (less than 3 anchors), don't consider hitting on
117      * points 22 and 23.
118      * 2. Don't break anchors inside home to hit.
119      */
120     I_PIPLOSS,
121 
122     /* Number of rolls that hit at least one checker.
123      */
124     I_P1,
125 
126     /* Number of rolls that hit at least two checkers.
127      */
128     I_P2,
129 
130     /* How many rolls permit the back checker to escape (Normalized to [01])
131      */
132     I_BACKESCAPES,
133 
134     /* Maximum containment of opponent checkers, from our points 9 to op back
135      * checker.
136      *
137      * Value is (1 - n/36), where n is number of rolls to escape.
138      */
139     I_ACONTAIN,
140 
141     /* Above squared */
142     I_ACONTAIN2,
143 
144     /* Maximum containment, from our point 9 to home.
145      * Value is (1 - n/36), where n is number of rolls to escape.
146      */
147     I_CONTAIN,
148 
149     /* Above squared */
150     I_CONTAIN2,
151 
152     /* For all checkers out of home,
153      * sum (Number of rolls that let x escape * distance from home)
154      *
155      * Normalized by dividing by 3600.
156      */
157     I_MOBILITY,
158 
159     /* One sided moment.
160      * Let A be the point of weighted average:
161      * A = sum of N(x) for all x) / nCheckers.
162      *
163      * Then for all x : A < N(x), M = (average (N(X) - A)^2)
164      *
165      * Diveded by 400 to normalize.
166      */
167     I_MOMENT2,
168 
169     /* Average number of pips lost when on the bar.
170      * Normalized to [01]
171      */
172     I_ENTER,
173 
174     /* Probablity of one checker not entering from bar.
175      * 1 - (1 - n/6)^2, where n is number of closed points in op home.
176      */
177     I_ENTER2,
178 
179     I_TIMING,
180 
181     I_BACKBONE,
182 
183     I_BACKG,
184 
185     I_BACKG1,
186 
187     I_FREEPIP,
188 
189     I_BACKRESCAPES,
190 
191     MORE_INPUTS
192 };
193 
194 #define MINPPERPOINT 4
195 
196 #define NUM_INPUTS ((25 * MINPPERPOINT + MORE_INPUTS) * 2)
197 #define NUM_RACE_INPUTS ( HALF_RACE_INPUTS * 2 )
198 #define NUM_PRUNING_INPUTS (25 * MINPPERPOINT * 2)
199 
200 
201 #if !defined(LOCKING_VERSION)
202 
203 f_FindnSaveBestMoves FindnSaveBestMoves = FindnSaveBestMovesNoLocking;
204 f_FindBestMove FindBestMove = FindBestMoveNoLocking;
205 f_EvaluatePosition EvaluatePosition = EvaluatePositionNoLocking;
206 f_ScoreMove ScoreMove = ScoreMoveNoLocking;
207 f_GeneralCubeDecisionE GeneralCubeDecisionE = GeneralCubeDecisionENoLocking;
208 f_GeneralEvaluationE GeneralEvaluationE = GeneralEvaluationENoLocking;
209 
210 #define FindnSaveBestMoves FindnSaveBestMovesNoLocking
211 #define FindBestMove FindBestMoveNoLocking
212 #define EvaluatePosition EvaluatePositionNoLocking
213 #define ScoreMove ScoreMoveNoLocking
214 #define GeneralCubeDecisionE GeneralCubeDecisionENoLocking
215 #define GeneralEvaluationE GeneralEvaluationENoLocking
216 #define EvaluatePositionCache EvaluatePositionCacheNoLocking
217 #define FindBestMovePlied FindBestMovePliedNoLocking
218 #define GeneralEvaluationEPlied GeneralEvaluationEPliedNoLocking
219 #define EvaluatePositionCubeful3 EvaluatePositionCubeful3NoLocking
220 #define ScoreMoves ScoreMovesNoLocking
221 #define ScoreMovesPruned ScoreMovesPrunedNoLocking
222 #define FindBestMoveInEval FindBestMoveInEvalNoLocking
223 #define GeneralEvaluationEPliedCubeful GeneralEvaluationEPliedCubefulNoLocking
224 #define EvaluatePositionCubeful4 EvaluatePositionCubeful4NoLocking
225 #define CacheAdd CacheAddNoLocking
226 #define CacheLookup CacheLookupNoLocking
227 
228 static int EvaluatePositionCache(NNState * nnStates, const TanBoard anBoard, float arOutput[],
229                                  cubeinfo * const pci, const evalcontext * pecx, int nPlies, positionclass pc);
230 
231 static int FindBestMovePlied(int anMove[8], int nDice0, int nDice1,
232                              TanBoard anBoard, const cubeinfo * pci,
233                              const evalcontext * pec, int nPlies, movefilter aamf[MAX_FILTER_PLIES][MAX_FILTER_PLIES]);
234 
235 static int anEscapes[0x1000];
236 static int anEscapes1[0x1000];
237 
238 static int anPoint[16] = { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
239 
240 neuralnet nnContact, nnRace, nnCrashed;
241 
242 neuralnet nnpContact, nnpRace, nnpCrashed;
243 
244 bearoffcontext *pbcOS = NULL;
245 bearoffcontext *pbcTS = NULL;
246 bearoffcontext *pbc1 = NULL;
247 bearoffcontext *pbc2 = NULL;
248 bearoffcontext *apbcHyper[3] = { NULL, NULL, NULL };
249 
250 evalCache cEval;
251 evalCache cpEval;
252 unsigned int cCache;
253 int fInterrupt = FALSE;
254 int fMatchCancelled = FALSE;
255 
256 /* variation of backgammon used by gnubg */
257 
258 bgvariation bgvDefault = VARIATION_STANDARD;
259 
260 /* the number of chequers for the variations */
261 
262 int anChequers[NUM_VARIATIONS] = { 15, 15, 1, 2, 3 };
263 
264 const char *aszVariations[NUM_VARIATIONS] = {
265     N_("Standard backgammon"),
266     N_("Nackgammon"),
267     N_("1-chequer hypergammon"),
268     N_("2-chequer hypergammon"),
269     N_("3-chequer hypergammon")
270 };
271 
272 const char *aszVariationCommands[NUM_VARIATIONS] = {
273     "standard",
274     "nackgammon",
275     "1-chequer-hypergammon",
276     "2-chequer-hypergammon",
277     "3-chequer-hypergammon"
278 };
279 
280 cubeinfo ciCubeless = { 1, 0, 0, 0, {0, 0}, FALSE, FALSE, FALSE,
281 {1.0, 1.0, 1.0, 1.0}, VARIATION_STANDARD
282 };
283 
284 const char *aszEvalType[] = {
285     N_("No evaluation"),
286     N_("Neural net evaluation"),
287     N_("Rollout")
288 };
289 
290 evalcontext ecBasic = { FALSE, 0, FALSE, TRUE, 0.0 };
291 
292 /* defaults for the filters  - 0 ply uses no filters */
293 
294 #include "movefilters.inc"
295 
296 movefilter defaultFilters[MAX_FILTER_PLIES][MAX_FILTER_PLIES] = MOVEFILTER_NORMAL;
297 
298 
299 /* Random context, for generating non-deterministic noisy evaluations. */
300 static randctx rc;
301 
302 /*
303  * predefined settings
304  */
305 
306 const char *aszSettings[NUM_SETTINGS] = {
307     N_("setting|beginner"),
308     N_("setting|casual play"),
309     N_("setting|intermediate"),
310     N_("setting|advanced"),
311     N_("setting|expert"),
312     N_("setting|world class"),
313     N_("setting|supremo"),
314     N_("setting|grandmaster"),
315     N_("setting|4ply")
316 };
317 
318 /* which evaluation context does the predefined settings use */
319 evalcontext aecSettings[NUM_SETTINGS] = {
320     {TRUE, 0, FALSE, TRUE, 0.060f},     /* beginner */
321     {TRUE, 0, FALSE, TRUE, 0.050f},     /* casual player */
322     {TRUE, 0, FALSE, TRUE, 0.040f},     /* intermediate */
323     {TRUE, 0, FALSE, TRUE, 0.015f},     /* advanced */
324     {TRUE, 0, FALSE, TRUE, 0.0f},       /* expert */
325     {TRUE, 2, TRUE, TRUE, 0.0f},        /* world class */
326     {TRUE, 2, TRUE, TRUE, 0.0f},        /* supremo */
327     {TRUE, 3, TRUE, TRUE, 0.0f},        /* grand master */
328     {TRUE, 4, TRUE, TRUE, 0.0f},        /* 4ply */
329 };
330 
331 /* which move filter does the predefined settings use */
332 int aiSettingsMoveFilter[NUM_SETTINGS] = {
333     -1,                         /* beginner: n/a */
334     -1,                         /* casual play: n/a */
335     -1,                         /* intermediate: n/a */
336     -1,                         /* advanced: n/a */
337     -1,                         /* expert: n/a */
338     2,                          /* wc: normal */
339     3,                          /* supremo: large */
340     3,                          /* grandmaster: large */
341     3,                          /* 4ply: large */
342 };
343 
344 /* the predefined move filters */
345 
346 const char *aszMoveFilterSettings[NUM_MOVEFILTER_SETTINGS] = {
347     N_("Tiny"),
348     N_("Narrow"),
349     N_("Normal"),
350     N_("Large"),
351     N_("Huge")
352 };
353 
354 movefilter aaamfMoveFilterSettings[NUM_MOVEFILTER_SETTINGS][MAX_FILTER_PLIES][MAX_FILTER_PLIES] = {
355     MOVEFILTER_TINY,
356     MOVEFILTER_NARROW,
357     MOVEFILTER_NORMAL,
358     MOVEFILTER_LARGE,
359     MOVEFILTER_HUGE
360 };
361 
362 
363 const char *aszDoubleTypes[NUM_DOUBLE_TYPES] = {
364     N_("doubletype|Double"),
365     N_("doubletype|Beaver"),
366     N_("doubletype|Raccoon")
367 };
368 
369 /* parameters for EvalEfficiency */
370 
371 static float rTSCubeX = 0.6f;   /* for match play only */
372 float rOSCubeX = 0.6f;
373 float rRaceFactorX = 0.00125f;
374 float rRaceCoefficientX = 0.55f;
375 float rRaceMax = 0.7f;
376 float rRaceMin = 0.6f;
377 float rCrashedX = 0.68f;
378 float rContactX = 0.68f;
379 
380 
381 #ifdef HAVE___BUILTIN_CLZ
382 static inline int
msb32(int n)383 msb32(int n)
384 {
385     return 31 - __builtin_clz(n);  /* or __builtin_clz() ^ 31 */
386 }
387 #else
388 /* from rosettacode.org */
389 static inline int
msb32(int n)390 msb32(int n)
391 {
392     int b = 0;
393 #define step(x) if (n >= 1 << x) b += x, n >>= x
394     step(16);
395     step(8);
396     step(4);
397     step(2);
398     step(1);
399 #undef step
400     return b;
401 }
402 #endif
403 
404 static void
ComputeTable0(void)405 ComputeTable0(void)
406 {
407     int i, c, n0, n1;
408 
409     for (i = 0; i < 0x1000; i++) {
410         c = 0;
411 
412         for (n0 = 0; n0 <= 5; n0++)
413             for (n1 = 0; n1 <= n0; n1++)
414                 if (!(i & (1 << (n0 + n1 + 1))) && !((i & (1 << n0)) && (i & (1 << n1))))
415                     c += (n0 == n1) ? 1 : 2;
416 
417         anEscapes[i] = c;
418     }
419 }
420 
421 static int
Escapes(const unsigned int anBoard[25],int n)422 Escapes(const unsigned int anBoard[25], int n)
423 {
424 
425     int i, af = 0, m;
426 
427     m = (n < 12) ? n : 12;
428 
429     for (i = 0; i < m; i++)
430         af |= (anPoint[anBoard[24 + i - n]] << i);
431 
432     return anEscapes[af];
433 }
434 
435 static void
ComputeTable1(void)436 ComputeTable1(void)
437 {
438     int i, c, n0, n1, low;
439 
440     anEscapes1[0] = 0;
441 
442     for (i = 1; i < 0x1000; i++) {
443         c = 0;
444 
445         low = 0;
446         while (!(i & (1 << low))) {
447             ++low;
448         }
449 
450         for (n0 = 0; n0 <= 5; n0++)
451             for (n1 = 0; n1 <= n0; n1++) {
452 
453                 if ((n0 + n1 + 1 > low) && !(i & (1 << (n0 + n1 + 1))) && !((i & (1 << n0)) && (i & (1 << n1)))) {
454                     c += (n0 == n1) ? 1 : 2;
455                 }
456             }
457 
458         anEscapes1[i] = c;
459     }
460 }
461 
462 static int
Escapes1(const unsigned int anBoard[25],int n)463 Escapes1(const unsigned int anBoard[25], int n)
464 {
465 
466     int i, af = 0, m;
467 
468     m = (n < 12) ? n : 12;
469 
470     for (i = 0; i < m; i++)
471         af |= (anPoint[anBoard[24 + i - n]] << i);
472 
473     return anEscapes1[af];
474 }
475 
476 
477 static void
ComputeTable(void)478 ComputeTable(void)
479 {
480     ComputeTable0();
481     ComputeTable1();
482 }
483 
484 static void
DestroyWeights(void)485 DestroyWeights(void)
486 {
487     NeuralNetDestroy(&nnContact);
488     NeuralNetDestroy(&nnCrashed);
489     NeuralNetDestroy(&nnRace);
490 
491     NeuralNetDestroy(&nnpContact);
492     NeuralNetDestroy(&nnpCrashed);
493     NeuralNetDestroy(&nnpRace);
494 }
495 
496 extern int
EvalShutdown(void)497 EvalShutdown(void)
498 {
499 
500     int i;
501 
502     /* close bearoff databases */
503 
504     BearoffClose(pbc1);
505     BearoffClose(pbc2);
506     BearoffClose(pbcOS);
507     BearoffClose(pbcTS);
508     for (i = 0; i < 3; ++i)
509         BearoffClose(apbcHyper[i]);
510 
511     /* destroy neural nets */
512 
513     DestroyWeights();
514 
515     /* destroy cache */
516 
517     CacheDestroy(&cEval);
518     CacheDestroy(&cpEval);
519 
520     return 0;
521 
522 }
523 
524 static int
binary_weights_failed(char * filename,FILE * weights)525 binary_weights_failed(char *filename, FILE * weights)
526 {
527     float r;
528 
529     if (!weights) {
530         g_print(_("couldn't open %s"), filename);
531         g_print("\n");
532         return -1;
533     }
534     if (fread(&r, sizeof r, 1, weights) < 1) {
535         g_print(_("couldn't read %s"), filename);
536         g_print("\n");
537         return -2;
538     }
539     if (r != WEIGHTS_MAGIC_BINARY) {
540         g_print(_("%s is not a weights file"), filename);
541         g_print("\n");
542         return -3;
543     }
544     if (fread(&r, sizeof r, 1, weights) < 1) {
545         g_print(_("couldn't read %s"), filename);
546         g_print("\n");
547         return -4;
548     }
549     if (r != WEIGHTS_VERSION_BINARY) {
550         char buf[20];
551         sprintf(buf, "%.2f", r);
552         g_print(_("weights file %s, has incorrect version (%s), expected (%s)"), filename, buf, WEIGHTS_VERSION);
553         g_print("\n");
554         return -5;
555     }
556 
557     return 0;
558 }
559 
560 static int
weights_failed(char * filename,FILE * weights)561 weights_failed(char *filename, FILE * weights)
562 {
563     char file_version[16];
564     if (!weights) {
565         g_print(_("couldn't open %s"), filename);
566         g_print("\n");
567         return -1;
568     }
569 
570     if (fscanf(weights, "GNU Backgammon %15s\n", file_version) != 1) {
571         g_print(_("%s is not a weights file"), filename);
572         g_print("\n");
573         return -2;
574     }
575     if (strcmp(file_version, WEIGHTS_VERSION)) {
576         g_print(_("weights file %s, has incorrect version (%s), expected (%s)"),
577                 filename, file_version, WEIGHTS_VERSION);
578         g_print("\n");
579         return -3;
580     }
581     return 0;
582 }
583 
584 extern void
EvalInitialise(char * szWeights,char * szWeightsBinary,int fNoBearoff,void (* pfProgress)(unsigned int))585 EvalInitialise(char *szWeights, char *szWeightsBinary, int fNoBearoff, void (*pfProgress) (unsigned int))
586 {
587     FILE *pfWeights = NULL;
588     int i, fReadWeights = FALSE;
589     static int fInitialised = FALSE;
590     char *gnubg_bearoff;
591     char *gnubg_bearoff_os;
592 #if defined(USE_SIMD_INSTRUCTIONS)
593     int result, simderror = TRUE;
594 #endif
595 
596     if (!fInitialised) {
597 #if defined(USE_SIMD_INSTRUCTIONS)
598         result = SIMD_Supported();
599         switch (result) {
600         case -1:
601             outputerrf(_("Can't check for SIMD support\n"));
602             break;
603         case -2:
604             outputerrf(_("No cpuid check available\n"));
605             break;
606         case 0:
607             /* No SIMD support */
608             break;
609         case 1:
610             /* SIMD support */
611             simderror = FALSE;
612             break;
613         default:
614             outputerrf(_("Unknown error while doing SIMD support test\n"));
615         }
616 
617         if (simderror) {
618 #if defined(USE_AVX)
619             outputerrf(_
620                        ("\nThis version of GNU Backgammon is compiled with AVX support but this machine does not support AVX\n"));
621 #else
622             outputerrf(_
623                        ("\nThis version of GNU Backgammon is compiled with SSE support but this machine does not support SSE\n"));
624 #endif
625             exit(EXIT_FAILURE);
626         }
627 #endif
628         cCache = 0x1 << CACHE_SIZE_DEFAULT;
629         if (CacheCreate(&cEval, cCache)) {
630             PrintError("CacheCreate");
631             return;
632         }
633 
634         if (CacheCreate(&cpEval, 0x1 << 16)) {
635             PrintError("CacheCreate");
636             return;
637         }
638 
639         ComputeTable();
640 
641         rc.randrsl[0] = (ub4) time(NULL);
642         for (i = 0; i < RANDSIZ; i++)
643             rc.randrsl[i] = rc.randrsl[0];
644         irandinit(&rc, TRUE);
645 
646         fInitialised = TRUE;
647     }
648 
649     if (!fNoBearoff) {
650         gnubg_bearoff_os = BuildFilename("gnubg_os0.bd");
651         if (!pbc1)
652             pbc1 = BearoffInit(gnubg_bearoff_os, BO_IN_MEMORY|BO_MUST_BE_ONE_SIDED, NULL);
653         g_free(gnubg_bearoff_os);
654 
655         if (!pbc1)
656             pbc1 = BearoffInit(NULL, BO_HEURISTIC, pfProgress);
657 
658         /* read two-sided db from gnubg.bd */
659         gnubg_bearoff = BuildFilename("gnubg_ts0.bd");
660         pbc2 = BearoffInit(gnubg_bearoff, BO_IN_MEMORY | BO_MUST_BE_TWO_SIDED, NULL);
661         g_free(gnubg_bearoff);
662 
663         if (!pbc2)
664             fprintf(stderr,
665                     "\n***WARNING***\n\n"
666                     "GNU Backgammon will not use the two-sided bearoff\n"
667                     "database since the gnubg_ts0.bd could not be found.\n"
668                     "You should obtain this file or generate it yourself\n"
669                     "with the command: makebearoff -t 6x6 -f gnubg_ts0.bd\n"
670                     "You can also generate other bearoff databases; see\n" "README for more details\n\n");
671 
672         gnubg_bearoff_os = BuildFilename("gnubg_os.bd");
673         /* init one-sided db */
674         pbcOS = BearoffInit(gnubg_bearoff_os, BO_IN_MEMORY|BO_MUST_BE_ONE_SIDED, NULL);
675         g_free(gnubg_bearoff_os);
676 
677         gnubg_bearoff = BuildFilename("gnubg_ts.bd");
678         /* init two-sided db */
679         pbcTS = BearoffInit(gnubg_bearoff, BO_IN_MEMORY|BO_MUST_BE_TWO_SIDED, NULL);
680         g_free(gnubg_bearoff);
681 
682         /* hyper-gammon databases */
683 
684         for (i = 0; i < 3; ++i) {
685             char *fn;
686             char sz[10];
687             sprintf(sz, "hyper%1d.bd", i + 1);
688             fn = BuildFilename(sz);
689             apbcHyper[i] = BearoffInit(fn, BO_IN_MEMORY, NULL);
690             g_free(fn);
691         }
692 
693     }
694 
695     if (szWeightsBinary) {
696         pfWeights = gnubg_g_fopen(szWeightsBinary, "rb");
697         if (!binary_weights_failed(szWeightsBinary, pfWeights)) {
698             if (!fReadWeights && !(fReadWeights =
699                                    !NeuralNetLoadBinary(&nnContact, pfWeights) &&
700                                    !NeuralNetLoadBinary(&nnRace, pfWeights) &&
701                                    !NeuralNetLoadBinary(&nnCrashed, pfWeights) &&
702                                    !NeuralNetLoadBinary(&nnpContact, pfWeights) &&
703                                    !NeuralNetLoadBinary(&nnpCrashed, pfWeights) &&
704                                    !NeuralNetLoadBinary(&nnpRace, pfWeights))) {
705                 perror(szWeightsBinary);
706             }
707         }
708         if (pfWeights)
709             fclose(pfWeights);
710         pfWeights = NULL;
711     }
712 
713     if (!fReadWeights && szWeights) {
714         pfWeights = gnubg_g_fopen(szWeights, "r");
715         if (!weights_failed(szWeights, pfWeights)) {
716             setlocale(LC_ALL, "C");
717             if (!(fReadWeights =
718                   !NeuralNetLoad(&nnContact, pfWeights) &&
719                   !NeuralNetLoad(&nnRace, pfWeights) &&
720                   !NeuralNetLoad(&nnCrashed, pfWeights) &&
721                   !NeuralNetLoad(&nnpContact, pfWeights) &&
722                   !NeuralNetLoad(&nnpCrashed, pfWeights) &&
723                   !NeuralNetLoad(&nnpRace, pfWeights)
724                 ))
725                 perror(szWeights);
726             setlocale(LC_ALL, "");
727         }
728         if (pfWeights)
729             fclose(pfWeights);
730         pfWeights = NULL;
731     }
732 
733     g_assert(fReadWeights);
734 
735     g_assert(nnContact.cInput == NUM_INPUTS && nnContact.cOutput == NUM_OUTPUTS);
736     g_assert(nnCrashed.cInput == NUM_INPUTS && nnCrashed.cOutput == NUM_OUTPUTS);
737     g_assert(nnRace.cInput == NUM_RACE_INPUTS && nnRace.cOutput == NUM_OUTPUTS);
738 
739     g_assert(nnpContact.cInput == NUM_PRUNING_INPUTS && nnpContact.cOutput == NUM_OUTPUTS);
740     g_assert(nnpCrashed.cInput == NUM_PRUNING_INPUTS && nnpCrashed.cOutput == NUM_OUTPUTS);
741     g_assert(nnpRace.cInput == NUM_PRUNING_INPUTS && nnpRace.cOutput == NUM_OUTPUTS);
742 
743     if (!fReadWeights) {
744         outputerrf(_("GNU Backgammon couldn't find a weights file."));
745         exit(EXIT_FAILURE);
746     }
747 
748 }
749 
750 /* Calculates inputs for any contact position, for one player only. */
751 
752 static void
CalculateHalfInputs(const unsigned int anBoard[25],const unsigned int anBoardOpp[25],float afInput[])753 CalculateHalfInputs(const unsigned int anBoard[25], const unsigned int anBoardOpp[25], float afInput[])
754 {
755     int i, j, k, l, nOppBack, n, aHit[39], nBoard;
756 
757     /* aanCombination[n] -
758      * How many ways to hit from a distance of n pips.
759      * Each number is an index into aIntermediate below.
760      */
761     static const int aanCombination[24][5] = {
762         {0, -1, -1, -1, -1},    /*  1 */
763         {1, 2, -1, -1, -1},     /*  2 */
764         {3, 4, 5, -1, -1},      /*  3 */
765         {6, 7, 8, 9, -1},       /*  4 */
766         {10, 11, 12, -1, -1},   /*  5 */
767         {13, 14, 15, 16, 17},   /*  6 */
768         {18, 19, 20, -1, -1},   /*  7 */
769         {21, 22, 23, 24, -1},   /*  8 */
770         {25, 26, 27, -1, -1},   /*  9 */
771         {28, 29, -1, -1, -1},   /* 10 */
772         {30, -1, -1, -1, -1},   /* 11 */
773         {31, 32, 33, -1, -1},   /* 12 */
774         {-1, -1, -1, -1, -1},   /* 13 */
775         {-1, -1, -1, -1, -1},   /* 14 */
776         {34, -1, -1, -1, -1},   /* 15 */
777         {35, -1, -1, -1, -1},   /* 16 */
778         {-1, -1, -1, -1, -1},   /* 17 */
779         {36, -1, -1, -1, -1},   /* 18 */
780         {-1, -1, -1, -1, -1},   /* 19 */
781         {37, -1, -1, -1, -1},   /* 20 */
782         {-1, -1, -1, -1, -1},   /* 21 */
783         {-1, -1, -1, -1, -1},   /* 22 */
784         {-1, -1, -1, -1, -1},   /* 23 */
785         {38, -1, -1, -1, -1}    /* 24 */
786     };
787 
788     /* One way to hit */
789     typedef struct _Inter {
790         /* if true, all intermediate points (if any) are required;
791          * if false, one of two intermediate points are required.
792          * Set to true for a direct hit, but that can be checked with
793          * nFaces == 1,
794          */
795         int fAll;
796 
797         /* Intermediate points required */
798         int anIntermediate[3];
799 
800         /* Number of faces used in hit (1 to 4) */
801         int nFaces;
802 
803         /* Number of pips used to hit */
804         int nPips;
805     } Inter;
806 
807     const Inter *pi;
808     /* All ways to hit */
809     static const Inter aIntermediate[39] = {
810         {1, {0, 0, 0}, 1, 1},   /*  0: 1x hits 1 */
811         {1, {0, 0, 0}, 1, 2},   /*  1: 2x hits 2 */
812         {1, {1, 0, 0}, 2, 2},   /*  2: 11 hits 2 */
813         {1, {0, 0, 0}, 1, 3},   /*  3: 3x hits 3 */
814         {0, {1, 2, 0}, 2, 3},   /*  4: 21 hits 3 */
815         {1, {1, 2, 0}, 3, 3},   /*  5: 11 hits 3 */
816         {1, {0, 0, 0}, 1, 4},   /*  6: 4x hits 4 */
817         {0, {1, 3, 0}, 2, 4},   /*  7: 31 hits 4 */
818         {1, {2, 0, 0}, 2, 4},   /*  8: 22 hits 4 */
819         {1, {1, 2, 3}, 4, 4},   /*  9: 11 hits 4 */
820         {1, {0, 0, 0}, 1, 5},   /* 10: 5x hits 5 */
821         {0, {1, 4, 0}, 2, 5},   /* 11: 41 hits 5 */
822         {0, {2, 3, 0}, 2, 5},   /* 12: 32 hits 5 */
823         {1, {0, 0, 0}, 1, 6},   /* 13: 6x hits 6 */
824         {0, {1, 5, 0}, 2, 6},   /* 14: 51 hits 6 */
825         {0, {2, 4, 0}, 2, 6},   /* 15: 42 hits 6 */
826         {1, {3, 0, 0}, 2, 6},   /* 16: 33 hits 6 */
827         {1, {2, 4, 0}, 3, 6},   /* 17: 22 hits 6 */
828         {0, {1, 6, 0}, 2, 7},   /* 18: 61 hits 7 */
829         {0, {2, 5, 0}, 2, 7},   /* 19: 52 hits 7 */
830         {0, {3, 4, 0}, 2, 7},   /* 20: 43 hits 7 */
831         {0, {2, 6, 0}, 2, 8},   /* 21: 62 hits 8 */
832         {0, {3, 5, 0}, 2, 8},   /* 22: 53 hits 8 */
833         {1, {4, 0, 0}, 2, 8},   /* 23: 44 hits 8 */
834         {1, {2, 4, 6}, 4, 8},   /* 24: 22 hits 8 */
835         {0, {3, 6, 0}, 2, 9},   /* 25: 63 hits 9 */
836         {0, {4, 5, 0}, 2, 9},   /* 26: 54 hits 9 */
837         {1, {3, 6, 0}, 3, 9},   /* 27: 33 hits 9 */
838         {0, {4, 6, 0}, 2, 10},  /* 28: 64 hits 10 */
839         {1, {5, 0, 0}, 2, 10},  /* 29: 55 hits 10 */
840         {0, {5, 6, 0}, 2, 11},  /* 30: 65 hits 11 */
841         {1, {6, 0, 0}, 2, 12},  /* 31: 66 hits 12 */
842         {1, {4, 8, 0}, 3, 12},  /* 32: 44 hits 12 */
843         {1, {3, 6, 9}, 4, 12},  /* 33: 33 hits 12 */
844         {1, {5, 10, 0}, 3, 15}, /* 34: 55 hits 15 */
845         {1, {4, 8, 12}, 4, 16}, /* 35: 44 hits 16 */
846         {1, {6, 12, 0}, 3, 18}, /* 36: 66 hits 18 */
847         {1, {5, 10, 15}, 4, 20},        /* 37: 55 hits 20 */
848         {1, {6, 12, 18}, 4, 24} /* 38: 66 hits 24 */
849     };
850 
851     /* aaRoll[n] - All ways to hit with the n'th roll
852      * Each entry is an index into aIntermediate above.
853      */
854 
855     static const int aaRoll[21][4] = {
856         {0, 2, 5, 9},           /* 11 */
857         {1, 8, 17, 24},         /* 22 */
858         {3, 16, 27, 33},        /* 33 */
859         {6, 23, 32, 35},        /* 44 */
860         {10, 29, 34, 37},       /* 55 */
861         {13, 31, 36, 38},       /* 66 */
862         {0, 1, 4, -1},          /* 21 */
863         {0, 3, 7, -1},          /* 31 */
864         {1, 3, 12, -1},         /* 32 */
865         {0, 6, 11, -1},         /* 41 */
866         {1, 6, 15, -1},         /* 42 */
867         {3, 6, 20, -1},         /* 43 */
868         {0, 10, 14, -1},        /* 51 */
869         {1, 10, 19, -1},        /* 52 */
870         {3, 10, 22, -1},        /* 53 */
871         {6, 10, 26, -1},        /* 54 */
872         {0, 13, 18, -1},        /* 61 */
873         {1, 13, 21, -1},        /* 62 */
874         {3, 13, 25, -1},        /* 63 */
875         {6, 13, 28, -1},        /* 64 */
876         {10, 13, 30, -1}        /* 65 */
877     };
878 
879     /* One roll stat */
880 
881     struct {
882         /* number of chequers this roll hits */
883         int nChequers;
884 
885         /* count of pips this roll hits */
886         int nPips;
887     } aRoll[21];
888 
889     {
890         int n = 0;
891 
892         for (nOppBack = 24; nOppBack >= 0; --nOppBack) {
893             if (anBoardOpp[nOppBack]) {
894                 break;
895             }
896         }
897 
898         nOppBack = 23 - nOppBack;
899 
900         for (i = nOppBack + 1; i < 25; i++)
901             if (anBoard[i])
902                 n += (i + 1 - nOppBack) * anBoard[i];
903 
904         g_assert(n);
905 
906         afInput[I_BREAK_CONTACT] = n / (15 + 152.0f);
907     }
908     {
909         unsigned int p = 0;
910 
911         for (i = 0; i < nOppBack; i++) {
912             if (anBoard[i])
913                 p += (i + 1) * anBoard[i];
914         }
915 
916         afInput[I_FREEPIP] = p / 100.0f;
917     }
918 
919     {
920         int t = 0;
921         int no = 0;
922 
923         int m = (nOppBack >= 11) ? nOppBack : 11;
924 
925         t += 24 * anBoard[24];
926         no += anBoard[24];
927 
928         for (i = 23; i > m; --i) {
929             if (anBoard[i] && anBoard[i] != 2) {
930                 int n = ((anBoard[i] > 2) ? (anBoard[i] - 2) : 1);
931                 no += n;
932                 t += i * n;
933             }
934         }
935 
936         for (; i >= 6; --i) {
937             if (anBoard[i]) {
938                 int n = anBoard[i];
939                 no += n;
940                 t += i * n;
941             }
942         }
943 
944         for (i = 5; i >= 0; --i) {
945             if (anBoard[i] > 2) {
946                 t += i * (anBoard[i] - 2);
947                 no += (anBoard[i] - 2);
948             } else if (anBoard[i] < 2) {
949                 int n = (2 - anBoard[i]);
950 
951                 if (no >= n) {
952                     t -= i * n;
953                     no -= n;
954                 }
955             }
956         }
957 
958         afInput[I_TIMING] = t / 100.0f;
959     }
960 
961     /* Back chequer */
962 
963     {
964         int nBack;
965 
966         for (nBack = 24; nBack >= 0; --nBack) {
967             if (anBoard[nBack]) {
968                 break;
969             }
970         }
971 
972         afInput[I_BACK_CHEQUER] = nBack / 24.0f;
973 
974         /* Back anchor */
975 
976         for (i = ((nBack == 24) ? 23 : nBack); i >= 0; --i) {
977             if (anBoard[i] >= 2) {
978                 break;
979             }
980         }
981 
982         afInput[I_BACK_ANCHOR] = i / 24.0f;
983 
984         /* Forward anchor */
985 
986         n = 0;
987         for (j = 18; j <= i; ++j) {
988             if (anBoard[j] >= 2) {
989                 n = 24 - j;
990                 break;
991             }
992         }
993 
994         if (n == 0) {
995             for (j = 17; j >= 12; --j) {
996                 if (anBoard[j] >= 2) {
997                     n = 24 - j;
998                     break;
999                 }
1000             }
1001         }
1002 
1003         afInput[I_FORWARD_ANCHOR] = n == 0 ? 2.0f : n / 6.0f;
1004     }
1005 
1006 
1007     /* Piploss */
1008 
1009     nBoard = 0;
1010     for (i = 0; i < 6; i++)
1011         if (anBoard[i])
1012             nBoard++;
1013 
1014     memset(aHit, 0, sizeof(aHit));
1015 
1016     /* for every point we'd consider hitting a blot on, */
1017 
1018     for (i = (nBoard > 2) ? 23 : 21; i >= 0; i--)
1019         /* if there's a blot there, then */
1020 
1021         if (anBoardOpp[i] == 1)
1022             /* for every point beyond */
1023 
1024             for (j = 24 - i; j < 25; j++)
1025                 /* if we have a hitter and are willing to hit */
1026 
1027                 if (anBoard[j] && !(j < 6 && anBoard[j] == 2))
1028                     /* for every roll that can hit from that point */
1029 
1030                     for (n = 0; n < 5; n++) {
1031                         if (aanCombination[j - 24 + i][n] == -1)
1032                             break;
1033 
1034                         /* find the intermediate points required to play */
1035 
1036                         pi = aIntermediate + aanCombination[j - 24 + i][n];
1037 
1038                         if (pi->fAll) {
1039                             /* if nFaces is 1, there are no intermediate points */
1040 
1041                             if (pi->nFaces > 1) {
1042                                 /* all the intermediate points are required */
1043 
1044                                 for (k = 0; k < 3 && pi->anIntermediate[k] > 0; k++)
1045                                     if (anBoardOpp[i - pi->anIntermediate[k]] > 1)
1046                                         /* point is blocked; look for other hits */
1047                                         goto cannot_hit;
1048                             }
1049                         } else {
1050                             /* either of two points are required */
1051 
1052                             if (anBoardOpp[i - pi->anIntermediate[0]] > 1 && anBoardOpp[i - pi->anIntermediate[1]] > 1) {
1053                                 /* both are blocked; look for other hits */
1054                                 goto cannot_hit;
1055                             }
1056                         }
1057 
1058                         /* enter this shot as available */
1059 
1060                         aHit[aanCombination[j - 24 + i][n]] |= 1 << j;
1061                       cannot_hit:;
1062                     }
1063 
1064     memset(aRoll, 0, sizeof(aRoll));
1065 
1066     if (!anBoard[24]) {
1067         /* we're not on the bar; for each roll, */
1068 
1069         for (i = 0; i < 21; i++) {
1070             n = -1;             /* (hitter used) */
1071 
1072             /* for each way that roll hits, */
1073             for (j = 0; j < 4; j++) {
1074                 int r = aaRoll[i][j];
1075 
1076                 if (r < 0)
1077                     break;
1078 
1079                 if (!aHit[r])
1080                     continue;
1081 
1082                 pi = aIntermediate + r;
1083 
1084                 if (pi->nFaces == 1) {
1085                     /* direct shot */
1086                     k = msb32(aHit[r]);
1087                     /* select the most advanced blot; if we still have
1088                      * a chequer that can hit there */
1089 
1090                     if (n != k || anBoard[k] > 1)
1091                         aRoll[i].nChequers++;
1092 
1093                     n = k;
1094 
1095                     if (k - pi->nPips + 1 > aRoll[i].nPips)
1096                         aRoll[i].nPips = k - pi->nPips + 1;
1097 
1098                     /* if rolling doubles, check for multiple
1099                      * direct shots */
1100 
1101                     if (aaRoll[i][3] >= 0 && aHit[r] & ~(1 << k))
1102                         aRoll[i].nChequers++;
1103 
1104                 } else {
1105                     /* indirect shot */
1106                     if (!aRoll[i].nChequers)
1107                         aRoll[i].nChequers = 1;
1108 
1109                     /* find the most advanced hitter */
1110 
1111                     k = msb32(aHit[r]);
1112 
1113                     if (k - pi->nPips + 1 > aRoll[i].nPips)
1114                         aRoll[i].nPips = k - pi->nPips + 1;
1115 
1116                     /* check for blots hit on intermediate points */
1117 
1118                     for (l = 0; l < 3 && pi->anIntermediate[l] > 0; l++)
1119                         if (anBoardOpp[23 - k + pi->anIntermediate[l]] == 1) {
1120 
1121                             aRoll[i].nChequers++;
1122                             break;
1123                         }
1124                 }
1125             }
1126         }
1127     } else if (anBoard[24] == 1) {
1128         /* we have one on the bar; for each roll, */
1129 
1130         for (i = 0; i < 21; i++) {
1131             n = 0;              /* (free to use either die to enter) */
1132 
1133             for (j = 0; j < 4; j++) {
1134                 int r = aaRoll[i][j];
1135 
1136                 if (r < 0)
1137                     break;
1138 
1139                 if (!aHit[r])
1140                     continue;
1141 
1142                 pi = aIntermediate + r;
1143 
1144                 if (pi->nFaces == 1) {
1145                     /* direct shot */
1146 
1147                     /* FIXME: There must be a more profitable way to use
1148                      * the possibility of finding the msb quickly,
1149                      * but I don't understand the code below. */
1150 #ifdef HAVE___BUILTIN_CLZ
1151                     /* This shortcut is worthwhile only if msb32 is fast */
1152                     for (k = msb32(aHit[r]); k > 0; k--) {
1153 #else
1154                     for (k = 24; k > 0; k--) {
1155 #endif
1156                         if (aHit[r] & (1 << k)) {
1157                             /* if we need this die to enter, we can't hit elsewhere */
1158 
1159                             if (n && k != 24)
1160                                 break;
1161 
1162                             /* if this isn't a shot from the bar, the
1163                              * other die must be used to enter */
1164 
1165                             if (k != 24) {
1166                                 int npip = aIntermediate[aaRoll[i][1 - j]].nPips;
1167 
1168                                 if (anBoardOpp[npip - 1] > 1)
1169                                     break;
1170 
1171                                 n = 1;
1172                             }
1173 
1174                             aRoll[i].nChequers++;
1175 
1176                             if (k - pi->nPips + 1 > aRoll[i].nPips)
1177                                 aRoll[i].nPips = k - pi->nPips + 1;
1178                         }
1179                     }
1180                 } else {
1181                     /* indirect shot -- consider from the bar only */
1182                     if (!(aHit[r] & (1 << 24)))
1183                         continue;
1184 
1185                     if (!aRoll[i].nChequers)
1186                         aRoll[i].nChequers = 1;
1187 
1188                     if (25 - pi->nPips > aRoll[i].nPips)
1189                         aRoll[i].nPips = 25 - pi->nPips;
1190 
1191                     /* check for blots hit on intermediate points */
1192                     for (k = 0; k < 3 && pi->anIntermediate[k] > 0; k++)
1193                         if (anBoardOpp[pi->anIntermediate[k] + 1] == 1) {
1194 
1195                             aRoll[i].nChequers++;
1196                             break;
1197                         }
1198                 }
1199             }
1200         }
1201     } else {
1202         /* we have more than one on the bar --
1203          * count only direct shots from point 24 */
1204 
1205         for (i = 0; i < 21; i++) {
1206             /* for the first two ways that hit from the bar */
1207 
1208             for (j = 0; j < 2; j++) {
1209                 int r = aaRoll[i][j];
1210 
1211                 if (!(aHit[r] & (1 << 24)))
1212                     continue;
1213 
1214                 pi = aIntermediate + r;
1215 
1216                 /* only consider direct shots */
1217 
1218                 if (pi->nFaces != 1)
1219                     continue;
1220 
1221                 aRoll[i].nChequers++;
1222 
1223                 if (25 - pi->nPips > aRoll[i].nPips)
1224                     aRoll[i].nPips = 25 - pi->nPips;
1225             }
1226         }
1227     }
1228 
1229     {
1230         int np = 0;
1231         int n1 = 0;
1232         int n2 = 0;
1233 
1234         for (i = 0; i < 6; i++) {
1235             int nc = aRoll[i].nChequers;
1236 
1237             np += aRoll[i].nPips;
1238 
1239             if (nc > 0) {
1240                 n1 += 1;
1241 
1242                 if (nc > 1) {
1243                     n2 += 1;
1244                 }
1245             }
1246         }
1247 
1248         for (; i < 21; i++) {
1249             int nc = aRoll[i].nChequers;
1250 
1251             np += aRoll[i].nPips * 2;
1252 
1253             if (nc > 0) {
1254                 n1 += 2;
1255 
1256                 if (nc > 1) {
1257                     n2 += 2;
1258                 }
1259             }
1260         }
1261 
1262         afInput[I_PIPLOSS] = np / (12.0f * 36.0f);
1263 
1264         afInput[I_P1] = n1 / 36.0f;
1265         afInput[I_P2] = n2 / 36.0f;
1266     }
1267 
1268     afInput[I_BACKESCAPES] = Escapes(anBoard, 23 - nOppBack) / 36.0f;
1269 
1270     afInput[I_BACKRESCAPES] = Escapes1(anBoard, 23 - nOppBack) / 36.0f;
1271 
1272     for (n = 36, i = 15; i < 24 - nOppBack; i++)
1273         if ((j = Escapes(anBoard, i)) < n)
1274             n = j;
1275 
1276     afInput[I_ACONTAIN] = (36 - n) / 36.0f;
1277     afInput[I_ACONTAIN2] = afInput[I_ACONTAIN] * afInput[I_ACONTAIN];
1278 
1279     if (nOppBack < 0) {
1280         /* restart loop, point 24 should not be included */
1281         i = 15;
1282         n = 36;
1283     }
1284 
1285     for (; i < 24; i++)
1286         if ((j = Escapes(anBoard, i)) < n)
1287             n = j;
1288 
1289 
1290     afInput[I_CONTAIN] = (36 - n) / 36.0f;
1291     afInput[I_CONTAIN2] = afInput[I_CONTAIN] * afInput[I_CONTAIN];
1292 
1293     for (n = 0, i = 6; i < 25; i++)
1294         if (anBoard[i])
1295             n += (i - 5) * anBoard[i] * Escapes(anBoardOpp, i);
1296 
1297     afInput[I_MOBILITY] = n / 3600.0f;
1298 
1299     j = 0;
1300     n = 0;
1301     for (i = 0; i < 25; i++) {
1302         int ni = anBoard[i];
1303 
1304         if (ni) {
1305             j += ni;
1306             n += i * ni;
1307         }
1308     }
1309 
1310     if (j) {
1311         n = (n + j - 1) / j;
1312     }
1313 
1314     j = 0;
1315     for (k = 0, i = n + 1; i < 25; i++) {
1316         int ni = anBoard[i];
1317 
1318         if (ni) {
1319             j += ni;
1320             k += ni * (i - n) * (i - n);
1321         }
1322     }
1323 
1324     if (j) {
1325         k = (k + j - 1) / j;
1326     }
1327 
1328     afInput[I_MOMENT2] = k / 400.0f;
1329 
1330     if (anBoard[24] > 0) {
1331         int loss = 0;
1332         int two = anBoard[24] > 1;
1333 
1334         for (i = 0; i < 6; ++i) {
1335             if (anBoardOpp[i] > 1) {
1336                 /* any double loses */
1337 
1338                 loss += 4 * (i + 1);
1339 
1340                 for (j = i + 1; j < 6; ++j) {
1341                     if (anBoardOpp[j] > 1) {
1342                         loss += 2 * (i + j + 2);
1343                     } else {
1344                         if (two) {
1345                             loss += 2 * (i + 1);
1346                         }
1347                     }
1348                 }
1349             } else {
1350                 if (two) {
1351                     for (j = i + 1; j < 6; ++j) {
1352                         if (anBoardOpp[j] > 1) {
1353                             loss += 2 * (j + 1);
1354                         }
1355                     }
1356                 }
1357             }
1358         }
1359 
1360         afInput[I_ENTER] = loss / (36.0f * (49.0f / 6.0f));
1361     } else {
1362         afInput[I_ENTER] = 0.0f;
1363     }
1364 
1365     n = 0;
1366     for (i = 0; i < 6; i++) {
1367         n += anBoardOpp[i] > 1;
1368     }
1369 
1370     afInput[I_ENTER2] = (36 - (n - 6) * (n - 6)) / 36.0f;
1371 
1372     {
1373         int pa = -1;
1374         int w = 0;
1375         int tot = 0;
1376         int np;
1377 
1378         for (np = 23; np > 0; --np) {
1379             if (anBoard[np] >= 2) {
1380                 if (pa == -1) {
1381                     pa = np;
1382                     continue;
1383                 }
1384 
1385                 {
1386                     int d = pa - np;
1387 
1388                     static int ac[23] = { 11, 11, 11, 11, 11, 11, 11,
1389                         6, 5, 4, 3, 2,
1390                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
1391                     };
1392 
1393                     w += ac[d] * anBoard[pa];
1394                     tot += anBoard[pa];
1395                 }
1396             }
1397         }
1398 
1399         if (tot) {
1400             afInput[I_BACKBONE] = 1 - (w / (tot * 11.0f));
1401         } else {
1402             afInput[I_BACKBONE] = 0;
1403         }
1404     }
1405 
1406     {
1407         unsigned int nAc = 0;
1408 
1409         for (i = 18; i < 24; ++i) {
1410             if (anBoard[i] > 1) {
1411                 ++nAc;
1412             }
1413         }
1414 
1415         afInput[I_BACKG] = 0.0;
1416         afInput[I_BACKG1] = 0.0;
1417 
1418         if (nAc >= 1) {
1419             unsigned int tot = 0;
1420             for (i = 18; i < 25; ++i) {
1421                 tot += anBoard[i];
1422             }
1423 
1424             if (nAc > 1) {
1425                 /* g_assert( tot >= 4 ); */
1426 
1427                 afInput[I_BACKG] = (tot - 3) / 4.0f;
1428             } else if (nAc == 1) {
1429                 afInput[I_BACKG1] = tot / 8.0f;
1430             }
1431         }
1432     }
1433 }
1434 
1435 
1436 static void
1437 CalculateRaceInputs(const TanBoard anBoard, float inputs[])
1438 {
1439     unsigned int side;
1440 
1441     for (side = 0; side < 2; ++side) {
1442         unsigned int i, k;
1443 
1444         const unsigned int *const board = anBoard[side];
1445         float *const afInput = inputs + side * HALF_RACE_INPUTS;
1446 
1447         unsigned int menOff = 15;
1448 
1449         {
1450             g_assert(board[23] == 0 && board[24] == 0);
1451         }
1452 
1453         /* Points */
1454         for (i = 0; i < 23; ++i) {
1455             unsigned int const nc = board[i];
1456 
1457             k = i * 4;
1458 
1459             menOff -= nc;
1460 
1461             afInput[k++] = (nc == 1) ? 1.0f : 0.0f;
1462             afInput[k++] = (nc == 2) ? 1.0f : 0.0f;
1463             afInput[k++] = (nc >= 3) ? 1.0f : 0.0f;
1464             afInput[k] = nc > 3 ? (nc - 3) / 2.0f : 0.0f;
1465         }
1466 
1467         /* Men off */
1468         for (k = 0; k < 14; ++k) {
1469             afInput[RI_OFF + k] = (menOff == (k + 1)) ? 1.0f : 0.0f;
1470         }
1471 
1472         {
1473             unsigned int nCross = 0;
1474 
1475             for (k = 1; k < 4; ++k) {
1476                 for (i = 6 * k; i < 6 * k + 6; ++i) {
1477                     unsigned int const nc = board[i];
1478 
1479                     if (nc) {
1480                         nCross += nc * k;
1481                     }
1482                 }
1483             }
1484 
1485             afInput[RI_NCROSS] = nCross / 10.0f;
1486         }
1487     }
1488 }
1489 
1490 /* baseInputs() is now in lib/inputs.c */
1491 
1492 static void
1493 menOffAll(const unsigned int *anBoard, float *afInput)
1494 {
1495     /* Men off */
1496     int menOff = 15;
1497     int i;
1498 
1499     for (i = 0; i < 25; i++) {
1500         menOff -= anBoard[i];
1501     }
1502 
1503     if (menOff <= 5) {
1504         afInput[0] = menOff ? menOff / 5.0f : 0.0f;
1505         afInput[1] = 0.0f;
1506         afInput[2] = 0.0f;
1507     } else if (menOff <= 10) {
1508         afInput[0] = 1.0f;
1509         afInput[1] = (menOff - 5) / 5.0f;
1510         afInput[2] = 0.0f;
1511     } else {
1512         afInput[0] = 1.0;
1513         afInput[1] = 1.0;
1514         afInput[2] = (menOff - 10) / 5.0f;
1515     }
1516 }
1517 
1518 static void
1519 menOffNonCrashed(const unsigned int *anBoard, float *afInput)
1520 {
1521     int menOff = 15;
1522     int i;
1523 
1524     for (i = 0; i < 25; ++i) {
1525         menOff -= anBoard[i];
1526     }
1527     {
1528         g_assert(menOff <= 8);
1529     }
1530 
1531     if (menOff <= 2) {
1532         afInput[0] = menOff ? menOff / 3.0f : 0.0f;
1533         afInput[1] = 0.0f;
1534         afInput[2] = 0.0f;
1535     } else if (menOff <= 5) {
1536         afInput[0] = 1.0f;
1537         afInput[1] = (menOff - 3) / 3.0f;
1538         afInput[2] = 0.0f;
1539     } else {
1540         afInput[0] = 1.0f;
1541         afInput[1] = 1.0f;
1542         afInput[2] = (menOff - 6) / 3.0f;
1543     }
1544 
1545 }
1546 
1547 /* Calculates contact neural net inputs from the board position. */
1548 
1549 static void
1550 CalculateContactInputs(const TanBoard anBoard, float arInput[])
1551 {
1552     baseInputs(anBoard, arInput);
1553 
1554     {
1555         float *b = arInput + MINPPERPOINT * 25 * 2;
1556 
1557         /* I accidentally switched sides (0 and 1) when I trained the net */
1558         menOffNonCrashed(anBoard[0], b + I_OFF1);
1559 
1560         CalculateHalfInputs(anBoard[1], anBoard[0], b);
1561     }
1562 
1563     {
1564         float *b = arInput + (MINPPERPOINT * 25 * 2 + MORE_INPUTS);
1565 
1566         menOffNonCrashed(anBoard[1], b + I_OFF1);
1567 
1568         CalculateHalfInputs(anBoard[0], anBoard[1], b);
1569     }
1570 }
1571 
1572 /* Calculates crashed neural net inputs from the board position. */
1573 
1574 static void
1575 CalculateCrashedInputs(const TanBoard anBoard, float arInput[])
1576 {
1577     baseInputs(anBoard, arInput);
1578 
1579     {
1580         float *b = arInput + MINPPERPOINT * 25 * 2;
1581 
1582         menOffAll(anBoard[1], b + I_OFF1);
1583 
1584         CalculateHalfInputs(anBoard[1], anBoard[0], b);
1585     }
1586 
1587     {
1588         float *b = arInput + (MINPPERPOINT * 25 * 2 + MORE_INPUTS);
1589 
1590         menOffAll(anBoard[0], b + I_OFF1);
1591 
1592         CalculateHalfInputs(anBoard[0], anBoard[1], b);
1593     }
1594 }
1595 
1596 extern void
1597 swap_us(unsigned int *p0, unsigned int *p1)
1598 {
1599     unsigned int n = *p0;
1600 
1601     *p0 = *p1;
1602     *p1 = n;
1603 }
1604 
1605 extern void
1606 swap(int *p0, int *p1)
1607 {
1608     int n = *p0;
1609 
1610     *p0 = *p1;
1611     *p1 = n;
1612 }
1613 
1614 extern void
1615 SwapSides(TanBoard anBoard)
1616 {
1617 
1618     int i, n;
1619 
1620     for (i = 0; i < 25; i++) {
1621         n = anBoard[0][i];
1622         anBoard[0][i] = anBoard[1][i];
1623         anBoard[1][i] = n;
1624     }
1625 }
1626 
1627 /* An upper bound on the number of turns it can take to complete a bearoff
1628  * from bearoff position ID i. */
1629 static int
1630 MaxTurns(int id)
1631 {
1632     unsigned short int aus[32];
1633     int i;
1634 
1635     BearoffDist(pbc1, id, NULL, NULL, NULL, aus, NULL);
1636 
1637     for (i = 31; i >= 0; i--) {
1638         if (aus[i])
1639             return i;
1640     }
1641 
1642     return -1;
1643 }
1644 
1645 extern void
1646 SanityCheck(const TanBoard anBoard, float arOutput[])
1647 {
1648     int i, j, nciq, ac[2], anBack[2], anCross[2], anGammonCross[2], anMaxTurns[2], fContact;
1649 
1650     g_assert(arOutput[OUTPUT_WIN] >= 0.0f && arOutput[OUTPUT_WIN] <= 1.0f);
1651     g_assert(arOutput[OUTPUT_WINGAMMON] >= 0.0f && arOutput[OUTPUT_WINGAMMON] <= 1.0f);
1652     g_assert(arOutput[OUTPUT_WINBACKGAMMON] >= 0.0f && arOutput[OUTPUT_WINBACKGAMMON] <= 1.0f);
1653     g_assert(arOutput[OUTPUT_LOSEGAMMON] >= 0.0f && arOutput[OUTPUT_LOSEGAMMON] <= 1.0f);
1654     g_assert(arOutput[OUTPUT_LOSEBACKGAMMON] >= 0.0f && arOutput[OUTPUT_LOSEBACKGAMMON] <= 1.0f);
1655 
1656     ac[0] = ac[1] = anBack[0] = anBack[1] = anCross[0] = anCross[1] = 0;
1657     anGammonCross[0] = anGammonCross[1] = 1;
1658 
1659     for (j = 0; j < 2; j++) {
1660         for (i = 0, nciq = 0; i < 6; i++)
1661             if (anBoard[j][i]) {
1662                 anBack[j] = i;
1663                 nciq += anBoard[j][i];
1664             }
1665         ac[j] = anCross[j] = nciq;
1666 
1667         for (i = 6, nciq = 0; i < 12; i++)
1668             if (anBoard[j][i]) {
1669                 anBack[j] = i;
1670                 nciq += anBoard[j][i];
1671             }
1672         ac[j] += nciq;
1673         anCross[j] += 2 * nciq;
1674         anGammonCross[j] += nciq;
1675 
1676         for (i = 12, nciq = 0; i < 18; i++)
1677             if (anBoard[j][i]) {
1678                 anBack[j] = i;
1679                 nciq += anBoard[j][i];
1680             }
1681         ac[j] += nciq;
1682         anCross[j] += 3 * nciq;
1683         anGammonCross[j] += 2 * nciq;
1684 
1685         for (i = 18, nciq = 0; i < 24; i++)
1686             if (anBoard[j][i]) {
1687                 anBack[j] = i;
1688                 nciq += anBoard[j][i];
1689             }
1690         ac[j] += nciq;
1691         anCross[j] += 4 * nciq;
1692         anGammonCross[j] += 3 * nciq;
1693 
1694         if (anBoard[j][24]) {
1695             anBack[j] = 24;
1696             ac[j] += anBoard[j][24];
1697             anCross[j] += 5 * anBoard[j][24];
1698             anGammonCross[j] += 4 * anBoard[j][24];
1699         }
1700     }
1701 
1702     fContact = anBack[0] + anBack[1] >= 24;
1703 
1704     if (unlikely(!fContact)) {
1705         for (i = 0; i < 2; i++)
1706             if (anBack[i] < 6 && pbc1)
1707                 anMaxTurns[i] = MaxTurns(PositionBearoff(anBoard[i], pbc1->nPoints, pbc1->nChequers));
1708             else
1709                 anMaxTurns[i] = anCross[i] * 2;
1710 
1711         if (unlikely(!anMaxTurns[1]))
1712             anMaxTurns[1] = 1;
1713 
1714     }
1715 
1716     if (unlikely(!fContact) && anCross[0] > 4 * (anMaxTurns[1] - 1))
1717         /* Certain win */
1718         arOutput[OUTPUT_WIN] = 1.0f;
1719 
1720     if (unlikely(ac[0] < 15))
1721         /* Opponent has borne off; no gammons or backgammons possible */
1722         arOutput[OUTPUT_WINGAMMON] = arOutput[OUTPUT_WINBACKGAMMON] = 0.0f;
1723     else if (unlikely(!fContact)) {
1724         if (anCross[1] > 8 * anGammonCross[0])
1725             /* Gammon impossible */
1726             arOutput[OUTPUT_WINGAMMON] = 0.0f;
1727         else if (anGammonCross[0] > 4 * (anMaxTurns[1] - 1))
1728             /* Certain gammon */
1729             arOutput[OUTPUT_WINGAMMON] = 1.0f;
1730         if (anBack[0] < 18)
1731             /* Backgammon impossible */
1732             arOutput[OUTPUT_WINBACKGAMMON] = 0.0f;
1733     }
1734 
1735     if (unlikely(!fContact) && anCross[1] > 4 * anMaxTurns[0])
1736         /* Certain loss */
1737         arOutput[OUTPUT_WIN] = 0.0f;
1738 
1739     if (unlikely(ac[1] < 15))
1740         /* Player has borne off; no gammon or backgammon losses possible */
1741         arOutput[OUTPUT_LOSEGAMMON] = arOutput[OUTPUT_LOSEBACKGAMMON] = 0.0f;
1742     else if (unlikely(!fContact)) {
1743         if (anCross[0] > 8 * anGammonCross[1] - 4)
1744             /* Gammon loss impossible */
1745             arOutput[OUTPUT_LOSEGAMMON] = 0.0f;
1746         else if (anGammonCross[1] > 4 * anMaxTurns[0])
1747             /* Certain gammon loss */
1748             arOutput[OUTPUT_LOSEGAMMON] = 1.0f;
1749         if (anBack[1] < 18)
1750             /* Backgammon impossible */
1751             arOutput[OUTPUT_LOSEBACKGAMMON] = 0.0f;
1752     }
1753 
1754     /* gammons must be less than wins */
1755     if (unlikely(arOutput[OUTPUT_WINGAMMON] > arOutput[OUTPUT_WIN])) {
1756         arOutput[OUTPUT_WINGAMMON] = arOutput[OUTPUT_WIN];
1757     }
1758 
1759     {
1760         float lose = 1.0f - arOutput[OUTPUT_WIN];
1761         if (unlikely(arOutput[OUTPUT_LOSEGAMMON] > lose)) {
1762             arOutput[OUTPUT_LOSEGAMMON] = lose;
1763         }
1764     }
1765 
1766     /* Backgammons cannot exceed gammons */
1767     if (unlikely(arOutput[OUTPUT_WINBACKGAMMON] > arOutput[OUTPUT_WINGAMMON]))
1768         arOutput[OUTPUT_WINBACKGAMMON] = arOutput[OUTPUT_WINGAMMON];
1769 
1770     if (unlikely(arOutput[OUTPUT_LOSEBACKGAMMON] > arOutput[OUTPUT_LOSEGAMMON]))
1771         arOutput[OUTPUT_LOSEBACKGAMMON] = arOutput[OUTPUT_LOSEGAMMON];
1772 
1773     if (fContact) {
1774         float noise = 1 / 10000.0f;
1775 
1776         for (i = OUTPUT_WINGAMMON; i < NUM_OUTPUTS; ++i) {
1777             if (unlikely(arOutput[i] < noise)) {
1778                 arOutput[i] = 0.0f;
1779             }
1780         }
1781     }
1782 
1783 }
1784 
1785 
1786 extern positionclass
1787 ClassifyPosition(const TanBoard anBoard, const bgvariation bgv)
1788 {
1789     int nOppBack = -1, nBack = -1;
1790 
1791     for (nOppBack = 24; nOppBack >= 0; --nOppBack) {
1792         if (anBoard[0][nOppBack]) {
1793             break;
1794         }
1795     }
1796 
1797     for (nBack = 24; nBack >= 0; --nBack) {
1798         if (anBoard[1][nBack]) {
1799             break;
1800         }
1801     }
1802 
1803     if (unlikely(nBack < 0 || nOppBack < 0))
1804         return CLASS_OVER;
1805 
1806     /* special classes for hypergammon variants */
1807 
1808     switch (bgv) {
1809     case VARIATION_HYPERGAMMON_1:
1810         return CLASS_HYPERGAMMON1;
1811 
1812     case VARIATION_HYPERGAMMON_2:
1813         return CLASS_HYPERGAMMON2;
1814 
1815     case VARIATION_HYPERGAMMON_3:
1816         return CLASS_HYPERGAMMON3;
1817 
1818     case VARIATION_STANDARD:
1819     case VARIATION_NACKGAMMON:
1820 
1821         /* normal backgammon */
1822 
1823         if (nBack + nOppBack > 22) {
1824 
1825             /* contact position */
1826 
1827             unsigned int const N = 6;
1828             unsigned int i;
1829             unsigned int side;
1830 
1831             for (side = 0; side < 2; ++side) {
1832                 unsigned int tot = 0;
1833 
1834                 const unsigned int *board = anBoard[side];
1835 
1836                 for (i = 0; i < 25; ++i) {
1837                     tot += board[i];
1838                 }
1839 
1840                 if (unlikely(tot <= N)) {
1841                     return CLASS_CRASHED;
1842                 } else {
1843                     if (unlikely(board[0] > 1)) {
1844                         if (unlikely(tot <= (N + board[0]))) {
1845                             return CLASS_CRASHED;
1846                         } else {
1847                             if (unlikely((1 + tot - (board[0] + board[1])) <= N) && board[1] > 1) {
1848                                 return CLASS_CRASHED;
1849                             }
1850                         }
1851                     } else {
1852                         if (unlikely(tot <= (N + (board[1] - 1)))) {
1853                             return CLASS_CRASHED;
1854                         }
1855                     }
1856                 }
1857             }
1858 
1859             return CLASS_CONTACT;
1860         } else {
1861 
1862             if (unlikely(isBearoff(pbc2, anBoard)))
1863                 return CLASS_BEAROFF2;
1864 
1865             if (unlikely(isBearoff(pbcTS, anBoard)))
1866                 return CLASS_BEAROFF_TS;
1867 
1868             if (unlikely(isBearoff(pbc1, anBoard)))
1869                 return CLASS_BEAROFF1;
1870 
1871             if (unlikely(isBearoff(pbcOS, anBoard)))
1872                 return CLASS_BEAROFF_OS;
1873 
1874             return CLASS_RACE;
1875 
1876         }
1877 
1878     default:
1879 
1880         g_assert_not_reached();
1881 
1882     }
1883 
1884     return CLASS_OVER;          /* for fussy compilers */
1885 }
1886 
1887 static int
1888 EvalBearoff2(const TanBoard anBoard, float arOutput[], const bgvariation UNUSED(bgv), NNState * UNUSED(nnStates))
1889 {
1890     g_assert(pbc2);
1891 
1892     return BearoffEval(pbc2, anBoard, arOutput);
1893 }
1894 
1895 static int
1896 EvalBearoffOS(const TanBoard anBoard, float arOutput[], const bgvariation UNUSED(bgv), NNState * UNUSED(nnStates))
1897 {
1898 
1899     return BearoffEval(pbcOS, anBoard, arOutput);
1900 
1901 }
1902 
1903 
1904 static int
1905 EvalBearoffTS(const TanBoard anBoard, float arOutput[], const bgvariation UNUSED(bgv), NNState * UNUSED(nnStates))
1906 {
1907 
1908     return BearoffEval(pbcTS, anBoard, arOutput);
1909 
1910 }
1911 
1912 static int
1913 EvalHypergammon1(const TanBoard anBoard, float arOutput[], const bgvariation UNUSED(bgv), NNState * UNUSED(nnStates))
1914 {
1915 
1916     return BearoffEval(apbcHyper[0], anBoard, arOutput);
1917 
1918 }
1919 
1920 static int
1921 EvalHypergammon2(const TanBoard anBoard, float arOutput[], const bgvariation UNUSED(bgv), NNState * UNUSED(nnStates))
1922 {
1923 
1924     return BearoffEval(apbcHyper[1], anBoard, arOutput);
1925 
1926 }
1927 
1928 static int
1929 EvalHypergammon3(const TanBoard anBoard, float arOutput[], const bgvariation UNUSED(bgv), NNState * UNUSED(nnStates))
1930 {
1931 
1932     return BearoffEval(apbcHyper[2], anBoard, arOutput);
1933 
1934 }
1935 
1936 static int
1937 EvalBearoff1(const TanBoard anBoard, float arOutput[], const bgvariation UNUSED(bgv), NNState * UNUSED(nnStates))
1938 {
1939 
1940     return BearoffEval(pbc1, anBoard, arOutput);
1941 
1942 }
1943 
1944 enum {
1945     /* gammon possible by side on roll */
1946     G_POSSIBLE = 0x1,
1947     /* backgammon possible by side on roll */
1948     BG_POSSIBLE = 0x2,
1949     /* gammon possible by side not on roll */
1950     OG_POSSIBLE = 0x4,
1951     /* backgammon possible by side not on roll */
1952     OBG_POSSIBLE = 0x8
1953 };
1954 
1955 /* side - side that potentially can win a backgammon */
1956 /* Return - Probablity that side will win a backgammon */
1957 
1958 static float
1959 raceBGprob(const TanBoard anBoard, int side, const bgvariation bgv)
1960 {
1961     int totMenHome = 0;
1962     int totPipsOp = 0;
1963     unsigned int i;
1964     TanBoard dummy;
1965 
1966     for (i = 0; i < 6; ++i) {
1967         totMenHome += anBoard[side][i];
1968     }
1969 
1970     for (i = 22; i >= 18; --i) {
1971         totPipsOp += anBoard[1 - side][i] * (i - 17);
1972     }
1973 
1974     if (!((totMenHome + 3) / 4 - (side == 1 ? 1 : 0) <= (totPipsOp + 2) / 3)) {
1975         return 0.0;
1976     }
1977 
1978     for (i = 0; i < 25; ++i) {
1979         dummy[side][i] = anBoard[side][i];
1980     }
1981 
1982     for (i = 0; i < 6; ++i) {
1983         dummy[1 - side][i] = anBoard[1 - side][18 + i];
1984     }
1985 
1986     for (i = 6; i < 25; ++i) {
1987         dummy[1 - side][i] = 0;
1988     }
1989 
1990     {
1991         float p = 0.0f;
1992         const long *bgp = getRaceBGprobs(dummy[1 - side]);
1993         if (bgp) {
1994             int k = PositionBearoff(anBoard[side], pbc1->nPoints, pbc1->nChequers);
1995             unsigned short int aProb[32];
1996 
1997             unsigned int j;
1998 
1999             unsigned long scale = (side == 0) ? 36 : 1;
2000 
2001             BearoffDist(pbc1, k, NULL, NULL, NULL, aProb, NULL);
2002 
2003             for (j = 1 - side; j < RBG_NPROBS; j++) {
2004                 unsigned long sum = 0;
2005                 scale *= 36;
2006                 for (i = 1; i <= j + side; ++i) {
2007                     sum += aProb[i];
2008                 }
2009                 p += ((float) bgp[j]) / scale * sum;
2010             }
2011 
2012             p /= 65535.0f;
2013 
2014         } else {
2015             float ap[5];
2016 
2017             if (PositionBearoff(dummy[0], 6, 15) > 923 || PositionBearoff(dummy[1], 6, 15) > 923) {
2018                 EvalBearoff1((ConstTanBoard) dummy, ap, bgv, NULL);
2019             } else {
2020                 EvalBearoff2((ConstTanBoard) dummy, ap, bgv, NULL);
2021             }
2022 
2023             p = (side == 1 ? ap[0] : 1 - ap[0]);
2024         }
2025 
2026         return MIN(p, 1.0f);
2027     }
2028 }
2029 
2030 extern void
2031 EvalRaceBG(const TanBoard anBoard, float arOutput[], const bgvariation bgv)
2032 
2033 /* anBoard[1] is on roll */
2034 {
2035     /* total men for side not on roll */
2036     int totMen0 = 0;
2037 
2038     /* total men for side on roll */
2039     int totMen1 = 0;
2040 
2041     /* a set flag for every possible outcome */
2042     int any = 0;
2043 
2044     int i;
2045 
2046     for (i = 23; i >= 0; --i) {
2047         totMen0 += anBoard[0][i];
2048         totMen1 += anBoard[1][i];
2049     }
2050 
2051     if (totMen1 == 15) {
2052         any |= OG_POSSIBLE;
2053     }
2054 
2055     if (totMen0 == 15) {
2056         any |= G_POSSIBLE;
2057     }
2058 
2059     if (any) {
2060         if (any & OG_POSSIBLE) {
2061             for (i = 23; i >= 18; --i) {
2062                 if (anBoard[1][i] > 0) {
2063                     break;
2064                 }
2065             }
2066             if (i >= 18) {
2067                 any |= OBG_POSSIBLE;
2068             }
2069         }
2070 
2071         if (any & G_POSSIBLE) {
2072             for (i = 23; i >= 18; --i) {
2073                 if (anBoard[0][i] > 0) {
2074                     break;
2075                 }
2076             }
2077 
2078             if (i >= 18) {
2079                 any |= BG_POSSIBLE;
2080             }
2081         }
2082     }
2083 
2084     if (any & (BG_POSSIBLE | OBG_POSSIBLE)) {
2085         /* side that can have the backgammon */
2086         int side = (any & BG_POSSIBLE) ? 1 : 0;
2087 
2088         float pr = raceBGprob(anBoard, side, bgv);
2089 
2090         if (pr > 0.0f) {
2091             if (side == 1) {
2092                 arOutput[OUTPUT_WINBACKGAMMON] = pr;
2093 
2094                 if (arOutput[OUTPUT_WINGAMMON] < arOutput[OUTPUT_WINBACKGAMMON]) {
2095                     arOutput[OUTPUT_WINGAMMON] = arOutput[OUTPUT_WINBACKGAMMON];
2096                 }
2097             } else {
2098                 arOutput[OUTPUT_LOSEBACKGAMMON] = pr;
2099 
2100                 if (arOutput[OUTPUT_LOSEGAMMON] < arOutput[OUTPUT_LOSEBACKGAMMON]) {
2101                     arOutput[OUTPUT_LOSEGAMMON] = arOutput[OUTPUT_LOSEBACKGAMMON];
2102                 }
2103             }
2104         } else {
2105             if (side == 1) {
2106                 arOutput[OUTPUT_WINBACKGAMMON] = 0.0;
2107             } else {
2108                 arOutput[OUTPUT_LOSEBACKGAMMON] = 0.0;
2109             }
2110         }
2111     }
2112 }
2113 
2114 static int
2115 EvalRace(const TanBoard anBoard, float arOutput[], const bgvariation bgv, NNState * nnStates)
2116 {
2117     SSE_ALIGN(float arInput[NUM_RACE_INPUTS]);
2118 
2119     CalculateRaceInputs(anBoard, arInput);
2120 
2121 #if defined(USE_SIMD_INSTRUCTIONS)
2122     if (NeuralNetEvaluateSSE(&nnRace, arInput, arOutput, nnStates ? nnStates + (CLASS_RACE - CLASS_RACE) : NULL))
2123 #else
2124     if (NeuralNetEvaluate(&nnRace, arInput, arOutput, nnStates ? nnStates + (CLASS_RACE - CLASS_RACE) : NULL))
2125 #endif
2126         return -1;
2127 
2128     /* special evaluation of backgammons overrides net output */
2129 
2130     EvalRaceBG(anBoard, arOutput, bgv);
2131 
2132     /* sanity check will take care of rest */
2133 
2134     return 0;
2135 }
2136 
2137 static int
2138 EvalContact(const TanBoard anBoard, float arOutput[], const bgvariation UNUSED(bgv), NNState * nnStates)
2139 {
2140     SSE_ALIGN(float arInput[NUM_INPUTS]);
2141 
2142     CalculateContactInputs(anBoard, arInput);
2143 
2144 #if defined(USE_SIMD_INSTRUCTIONS)
2145     return NeuralNetEvaluateSSE(&nnContact, arInput, arOutput,
2146                                 nnStates ? nnStates + (CLASS_CONTACT - CLASS_RACE) : NULL);
2147 #else
2148     return NeuralNetEvaluate(&nnContact, arInput, arOutput, nnStates ? nnStates + (CLASS_CONTACT - CLASS_RACE) : NULL);
2149 #endif
2150 }
2151 
2152 static int
2153 EvalCrashed(const TanBoard anBoard, float arOutput[], const bgvariation UNUSED(bgv), NNState * nnStates)
2154 {
2155     SSE_ALIGN(float arInput[NUM_INPUTS]);
2156 
2157     CalculateCrashedInputs(anBoard, arInput);
2158 
2159 #if defined(USE_SIMD_INSTRUCTIONS)
2160     return NeuralNetEvaluateSSE(&nnCrashed, arInput, arOutput,
2161                                 nnStates ? nnStates + (CLASS_CRASHED - CLASS_RACE) : NULL);
2162 #else
2163     return NeuralNetEvaluate(&nnCrashed, arInput, arOutput, nnStates ? nnStates + (CLASS_CRASHED - CLASS_RACE) : NULL);
2164 #endif
2165 }
2166 
2167 extern int
2168 EvalOver(const TanBoard anBoard, float arOutput[], const bgvariation bgv, NNState * UNUSED(nnStates))
2169 {
2170     int i, c;
2171     int n = anChequers[bgv];
2172 
2173     for (i = 0; i < 25; i++)
2174         if (anBoard[0][i])
2175             break;
2176 
2177     if (i == 25) {
2178         /* opponent has no pieces on board; player has lost */
2179         arOutput[OUTPUT_WIN] = arOutput[OUTPUT_WINGAMMON] = arOutput[OUTPUT_WINBACKGAMMON] = 0.0;
2180 
2181         for (i = 0, c = 0; i < 25; i++)
2182             c += anBoard[1][i];
2183 
2184         if (c == n) {
2185             /* player still has all pieces on board; loses gammon */
2186             arOutput[OUTPUT_LOSEGAMMON] = 1.0;
2187 
2188             for (i = 18; i < 25; i++)
2189                 if (anBoard[1][i]) {
2190                     /* player still has pieces in opponent's home board;
2191                      * loses backgammon */
2192                     arOutput[OUTPUT_LOSEBACKGAMMON] = 1.0;
2193 
2194                     return 0;
2195                 }
2196 
2197             arOutput[OUTPUT_LOSEBACKGAMMON] = 0.0;
2198 
2199             return 0;
2200         }
2201 
2202         arOutput[OUTPUT_LOSEGAMMON] = arOutput[OUTPUT_LOSEBACKGAMMON] = 0.0;
2203 
2204         return 0;
2205     }
2206 
2207     for (i = 0; i < 25; i++)
2208         if (anBoard[1][i])
2209             break;
2210 
2211     if (i == 25) {
2212         /* player has no pieces on board; wins */
2213         arOutput[OUTPUT_WIN] = 1.0;
2214         arOutput[OUTPUT_LOSEGAMMON] = arOutput[OUTPUT_LOSEBACKGAMMON] = 0.0;
2215 
2216         for (i = 0, c = 0; i < 25; i++)
2217             c += anBoard[0][i];
2218 
2219         if (c == n) {
2220             /* opponent still has all pieces on board; win gammon */
2221             arOutput[OUTPUT_WINGAMMON] = 1.0;
2222 
2223             for (i = 18; i < 25; i++)
2224                 if (anBoard[0][i]) {
2225                     /* opponent still has pieces in player's home board;
2226                      * win backgammon */
2227                     arOutput[OUTPUT_WINBACKGAMMON] = 1.0;
2228 
2229                     return 0;
2230                 }
2231 
2232             arOutput[OUTPUT_WINBACKGAMMON] = 0.0;
2233 
2234             return 0;
2235         }
2236 
2237         arOutput[OUTPUT_WINGAMMON] = arOutput[OUTPUT_WINBACKGAMMON] = 0.0;
2238     }
2239 
2240     return 0;
2241 
2242 }
2243 
2244 classevalfunc acef[N_CLASSES] = {
2245     EvalOver,
2246     EvalHypergammon1,
2247     EvalHypergammon2,
2248     EvalHypergammon3,
2249     EvalBearoff2, EvalBearoffTS,
2250     EvalBearoff1, EvalBearoffOS,
2251     EvalRace, EvalCrashed, EvalContact
2252 };
2253 
2254 extern float
2255 Noise(const evalcontext * pec, const TanBoard anBoard, int iOutput)
2256 {
2257     float r;
2258 
2259     if (pec->fDeterministic) {
2260         char auchBoard[50], auch[16];
2261         int i;
2262 
2263         for (i = 0; i < 25; i++) {
2264             auchBoard[i << 1] = (char) anBoard[0][i];
2265             auchBoard[(i << 1) + 1] = (char) anBoard[1][i];
2266         }
2267 
2268         auchBoard[0] += iOutput;
2269 
2270         md5_buffer(auchBoard, 50, auch);
2271 
2272         /* We can't use a Box-Muller transform here, because generating
2273          * a point in the unit circle requires a potentially unbounded
2274          * number of integers, and all we have is the board.  So we
2275          * just take the sum of the bytes in the hash, which (by the
2276          * central limit theorem) should have a normal-ish distribution. */
2277 
2278         r = 0.0f;
2279         for (i = 0; i < 16; i++)
2280             r += auch[i];
2281 
2282         r -= 2040.0f;
2283         r /= 295.6f;
2284     } else {
2285         /* Box-Muller transform of a point in the unit circle. */
2286         float x, y;
2287 
2288         do {
2289             x = (float) irand(&rc) * 2.0f / UB4MAXVAL - 1.0f;
2290             y = (float) irand(&rc) * 2.0f / UB4MAXVAL - 1.0f;
2291             r = x * x + y * y;
2292         } while (r > 1.0f || r == 0.0f);
2293 
2294         r = y * sqrtf(-2.0f * logf(r) / r);
2295     }
2296 
2297     r *= pec->rNoise;
2298 
2299     if (iOutput == OUTPUT_WINGAMMON || iOutput == OUTPUT_LOSEGAMMON)
2300         r *= 0.25f;
2301     else if (iOutput == OUTPUT_WINBACKGAMMON || iOutput == OUTPUT_LOSEBACKGAMMON)
2302         r *= 0.01f;
2303 
2304     return r;
2305 }
2306 
2307 extern int
2308 EvalKey(const evalcontext * pec, const int nPlies, const cubeinfo * pci, int fCubefulEquity)
2309 {
2310 
2311     int iKey;
2312     /*
2313      * Bit 00-03: nPlies
2314      * Bit 04   : fCubeful
2315      * Bit 05   : fMove
2316      * Bit 06   : fUsePrune
2317      * Bit 07-12: anScore[ 0 ]
2318      * Bit 13-18: anScore[ 1 ]
2319      * Bit 19-22: log2(nCube)
2320      * Bit 23-24: fCubeOwner
2321      * Bit 25   : fCrawford
2322      * Bit 26   : fJacoby
2323      * Bit 27   : fBeavers
2324      */
2325 
2326     iKey = (nPlies | (pec->fCubeful << 4) | (pci->fMove << 5));
2327 
2328     if (nPlies)
2329         iKey ^= ((pec->fUsePrune) << 6);
2330 
2331 
2332     if (nPlies || fCubefulEquity) {
2333         /* In match play, the score and cube value and position are important. */
2334         if (pci->nMatchTo)
2335             iKey ^=
2336                 ((pci->nMatchTo - pci->anScore[pci->fMove] - 1) << 7) ^
2337                 ((pci->nMatchTo - pci->anScore[!pci->fMove] - 1) << 13) ^
2338                 (LogCube(pci->nCube) << 19) ^
2339                 ((pci->fCubeOwner < 0 ? 2 : pci->fCubeOwner == pci->fMove) << 23) ^ (pci->fCrawford << 25);
2340         else if (pec->fCubeful || fCubefulEquity)
2341             /* in cubeful money games the cube position and rules are important. */
2342             iKey ^=
2343                 ((pci->fCubeOwner < 0 ? 2 :
2344                   pci->fCubeOwner == pci->fMove) << 23) ^ (pci->fJacoby << 26) ^ (pci->fBeavers << 27);
2345 
2346         if (fCubefulEquity)
2347             iKey ^= 0x6a47b47e;
2348     }
2349 
2350     return iKey;
2351 
2352 }
2353 
2354 extern int
2355 PerfectCubeful(bearoffcontext * pbc, const TanBoard anBoard, float arEquity[])
2356 {
2357 
2358     unsigned int nUs = PositionBearoff(anBoard[1], pbc->nPoints, pbc->nChequers);
2359     unsigned int nThem = PositionBearoff(anBoard[0], pbc->nPoints, pbc->nChequers);
2360     unsigned int n = Combination(pbc->nPoints + pbc->nChequers, pbc->nPoints);
2361     unsigned int iPos = nUs * n + nThem;
2362 
2363     return BearoffCubeful(pbc, iPos, arEquity, NULL);
2364 
2365 }
2366 
2367 
2368 extern int
2369 EvaluatePerfectCubeful(const TanBoard anBoard, float arEquity[], const bgvariation bgv)
2370 {
2371 
2372     positionclass pc = ClassifyPosition(anBoard, bgv);
2373 
2374     g_assert(pc <= CLASS_PERFECT);
2375 
2376     switch (pc) {
2377     case CLASS_BEAROFF2:
2378         return PerfectCubeful(pbc2, anBoard, arEquity);
2379     case CLASS_BEAROFF_TS:
2380         return PerfectCubeful(pbcTS, anBoard, arEquity);
2381     default:
2382         g_assert_not_reached();
2383     }
2384 
2385     return -1;
2386 
2387 }
2388 
2389 extern void
2390 InvertEvaluation(float ar[NUM_OUTPUTS])
2391 {
2392 
2393     float r;
2394 
2395     ar[OUTPUT_WIN] = 1.0f - ar[OUTPUT_WIN];
2396 
2397     r = ar[OUTPUT_WINGAMMON];
2398     ar[OUTPUT_WINGAMMON] = ar[OUTPUT_LOSEGAMMON];
2399     ar[OUTPUT_LOSEGAMMON] = r;
2400 
2401     r = ar[OUTPUT_WINBACKGAMMON];
2402     ar[OUTPUT_WINBACKGAMMON] = ar[OUTPUT_LOSEBACKGAMMON];
2403     ar[OUTPUT_LOSEBACKGAMMON] = r;
2404 }
2405 
2406 extern void
2407 InvertEvaluationR(float ar[NUM_ROLLOUT_OUTPUTS], const cubeinfo * pci)
2408 {
2409     /* invert win, gammon etc. */
2410 
2411     InvertEvaluation(ar);
2412 
2413     /* invert equities */
2414 
2415     ar[OUTPUT_EQUITY] = -ar[OUTPUT_EQUITY];
2416 
2417     if (pci->nMatchTo)
2418         ar[OUTPUT_CUBEFUL_EQUITY] = 1.0f - ar[OUTPUT_CUBEFUL_EQUITY];
2419     else
2420         ar[OUTPUT_CUBEFUL_EQUITY] = -ar[OUTPUT_CUBEFUL_EQUITY];
2421 
2422 
2423 }
2424 
2425 
2426 extern int
2427 GameStatus(const TanBoard anBoard, const bgvariation bgv)
2428 {
2429 
2430     SSE_ALIGN(float ar[NUM_OUTPUTS]) = {
2431     0, 0, 0, 0, 0};             /* NUM_OUTPUTS are 5 */
2432 
2433     if (ClassifyPosition(anBoard, bgv) != CLASS_OVER)
2434         return 0;
2435 
2436     EvalOver(anBoard, ar, bgv, NULL);
2437 
2438     if (ar[OUTPUT_WINBACKGAMMON] > 0.0f || ar[OUTPUT_LOSEBACKGAMMON] > 0.0f)
2439         return 3;
2440 
2441     if (ar[OUTPUT_WINGAMMON] > 0.0f || ar[OUTPUT_LOSEGAMMON] > 0.0f)
2442         return 2;
2443 
2444     return 1;
2445 
2446 }
2447 
2448 /*
2449  * Utility returns the "correct" cubeless equity based on the current
2450  * gammon values.
2451  *
2452  * Use UtilityME to get the "true" money equity.
2453  */
2454 
2455 extern float
2456 Utility(float ar[NUM_OUTPUTS], const cubeinfo * pci)
2457 {
2458 
2459     if (!pci->nMatchTo) {
2460 
2461         /* equity calculation for money game */
2462 
2463         /* For money game the gammon price is the same for both
2464          * players, so there is no need to use pci->fMove. */
2465 
2466         return
2467             ar[OUTPUT_WIN] * 2.0f - 1.0f +
2468             (ar[OUTPUT_WINGAMMON] - ar[OUTPUT_LOSEGAMMON]) *
2469             pci->arGammonPrice[0] + (ar[OUTPUT_WINBACKGAMMON] - ar[OUTPUT_LOSEBACKGAMMON]) * pci->arGammonPrice[1];
2470 
2471     } else {
2472 
2473         /* equity calculation for match play */
2474 
2475         return ar[OUTPUT_WIN] * 2.0f - 1.0f + ar[OUTPUT_WINGAMMON] * pci->arGammonPrice[pci->fMove]
2476             - ar[OUTPUT_LOSEGAMMON] * pci->arGammonPrice[!pci->fMove]
2477             + ar[OUTPUT_WINBACKGAMMON] * pci->arGammonPrice[2 + pci->fMove]
2478             - ar[OUTPUT_LOSEBACKGAMMON] * pci->arGammonPrice[2 + !pci->fMove];
2479 
2480     }
2481 
2482 }
2483 
2484 /*
2485  * UtilityME is identical to Utility for match play.
2486  * For money play it returns the money equity instead of the
2487  * correct cubeless equity.
2488  */
2489 
2490 extern float
2491 UtilityME(float ar[NUM_OUTPUTS], const cubeinfo * pci)
2492 {
2493 
2494     if (!pci->nMatchTo)
2495 
2496         /* calculate money equity */
2497 
2498         return
2499             ar[OUTPUT_WIN] * 2.0f - 1.0f +
2500             (ar[OUTPUT_WINGAMMON] - ar[OUTPUT_LOSEGAMMON]) + (ar[OUTPUT_WINBACKGAMMON] - ar[OUTPUT_LOSEBACKGAMMON]);
2501 
2502     else
2503 
2504         return Utility(ar, pci);
2505 
2506 }
2507 
2508 
2509 extern float
2510 mwc2eq(const float rMwc, const cubeinfo * pci)
2511 {
2512 
2513     /* mwc if I win/lose */
2514 
2515     float rMwcWin, rMwcLose;
2516 
2517     rMwcWin = getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo,
2518                     pci->fMove, pci->nCube, pci->fMove, pci->fCrawford, aafMET, aafMETPostCrawford);
2519 
2520     rMwcLose = getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo,
2521                      pci->fMove, pci->nCube, !pci->fMove, pci->fCrawford, aafMET, aafMETPostCrawford);
2522 
2523     /*
2524      * make linear inter- or extrapolation:
2525      * equity       mwc
2526      *  -1          rMwcLose
2527      *  +1          rMwcWin
2528      *
2529      * Interpolation formula:
2530      *
2531      *       2 * rMwc - ( rMwcWin + rMwcLose )
2532      * rEq = ---------------------------------
2533      *            rMwcWin - rMwcLose
2534      *
2535      * FIXME: numerical problems?
2536      * If you are trailing 30-away, 1-away the difference between
2537      * 29-away, 1-away and 30-away, 0-away is not very large, and it may
2538      * give numerical problems.
2539      *
2540      */
2541 
2542     return (2.0f * rMwc - (rMwcWin + rMwcLose)) / (rMwcWin - rMwcLose);
2543 
2544 }
2545 
2546 extern float
2547 eq2mwc(const float rEq, const cubeinfo * pci)
2548 {
2549 
2550     /* mwc if I win/lose */
2551 
2552     float rMwcWin, rMwcLose;
2553 
2554     rMwcWin = getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo,
2555                     pci->fMove, pci->nCube, pci->fMove, pci->fCrawford, aafMET, aafMETPostCrawford);
2556 
2557     rMwcLose = getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo,
2558                      pci->fMove, pci->nCube, !pci->fMove, pci->fCrawford, aafMET, aafMETPostCrawford);
2559 
2560     /*
2561      * Linear inter- or extrapolation.
2562      * Solve the formula in the routine above (mwc2eq):
2563      *
2564      *        rEq * ( rMwcWin - rMwcLose ) + ( rMwcWin + rMwcLose )
2565      * rMwc = -----------------------------------------------------
2566      *                                   2
2567      */
2568 
2569     return 0.5f * (rEq * (rMwcWin - rMwcLose) + (rMwcWin + rMwcLose));
2570 
2571 }
2572 
2573 /*
2574  * Convert standard error MWC to standard error equity
2575  *
2576  */
2577 
2578 extern float
2579 se_mwc2eq(const float rMwc, const cubeinfo * pci)
2580 {
2581 
2582     /* mwc if I win/lose */
2583 
2584     float rMwcWin, rMwcLose;
2585 
2586     rMwcWin = getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo,
2587                     pci->fMove, pci->nCube, pci->fMove, pci->fCrawford, aafMET, aafMETPostCrawford);
2588 
2589     rMwcLose = getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo,
2590                      pci->fMove, pci->nCube, !pci->fMove, pci->fCrawford, aafMET, aafMETPostCrawford);
2591 
2592     return 2.0f / (rMwcWin - rMwcLose) * rMwc;
2593 
2594 }
2595 
2596 /*
2597  * Convert standard error equity to standard error mwc
2598  *
2599  */
2600 
2601 extern float
2602 se_eq2mwc(const float rEq, const cubeinfo * pci)
2603 {
2604 
2605     /* mwc if I win/lose */
2606 
2607     float rMwcWin, rMwcLose;
2608 
2609     rMwcWin = getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo,
2610                     pci->fMove, pci->nCube, pci->fMove, pci->fCrawford, aafMET, aafMETPostCrawford);
2611 
2612     rMwcLose = getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo,
2613                      pci->fMove, pci->nCube, !pci->fMove, pci->fCrawford, aafMET, aafMETPostCrawford);
2614 
2615     /*
2616      * Linear inter- or extrapolation.
2617      * Solve the formula in the routine above (mwc2eq):
2618      *
2619      *        rEq * ( rMwcWin - rMwcLose ) + ( rMwcWin + rMwcLose )
2620      * rMwc = -----------------------------------------------------
2621      *                                   2
2622      */
2623 
2624     return 0.5f * rEq * (rMwcWin - rMwcLose);
2625 
2626 }
2627 
2628 extern int
2629 ApplySubMove(TanBoard anBoard, const int iSrc, const int nRoll, const int fCheckLegal)
2630 {
2631 
2632     int iDest = iSrc - nRoll;
2633 
2634     if (fCheckLegal && (nRoll < 1 || nRoll > 6)) {
2635         /* Invalid dice roll */
2636         errno = EINVAL;
2637         return -1;
2638     }
2639 
2640     if (iSrc < 0 || iSrc > 24 || iDest > 24 || anBoard[1][iSrc] < 1) {
2641         /* Invalid point number, or source point is empty */
2642         errno = EINVAL;
2643         return -1;
2644     }
2645 
2646     anBoard[1][iSrc]--;
2647 
2648     if (iDest < 0)
2649         return 0;
2650 
2651     if (anBoard[0][23 - iDest]) {
2652         if (anBoard[0][23 - iDest] > 1) {
2653             /* Trying to move to a point already made by the opponent */
2654             errno = EINVAL;
2655             return -1;
2656         }
2657         anBoard[1][iDest] = 1;
2658         anBoard[0][23 - iDest] = 0;
2659         anBoard[0][24]++;
2660     } else
2661         anBoard[1][iDest]++;
2662 
2663     return 0;
2664 }
2665 
2666 extern int
2667 ApplyMove(TanBoard anBoard, const int anMove[8], const int fCheckLegal)
2668 {
2669     int i;
2670 
2671     for (i = 0; i < 8 && anMove[i] >= 0; i += 2)
2672         if (ApplySubMove(anBoard, anMove[i], anMove[i] - anMove[i + 1], fCheckLegal))
2673             return -1;
2674 
2675     return 0;
2676 }
2677 
2678 static void
2679 SaveMoves(movelist * pml, unsigned int cMoves, unsigned int cPip, int anMoves[], const TanBoard anBoard, int fPartial)
2680 {
2681     unsigned int i, j;
2682     move *pm;
2683     positionkey key;
2684 
2685     if (fPartial) {
2686         /* Save all moves, even incomplete ones */
2687         if (cMoves > pml->cMaxMoves)
2688             pml->cMaxMoves = cMoves;
2689 
2690         if (cPip > pml->cMaxPips)
2691             pml->cMaxPips = cPip;
2692     } else {
2693         /* Save only legal moves: if the current move moves plays less
2694          * chequers or pips than those already found, it is illegal; if
2695          * it plays more, the old moves are illegal. */
2696         if (cMoves < pml->cMaxMoves || cPip < pml->cMaxPips)
2697             return;
2698 
2699         if (cMoves > pml->cMaxMoves || cPip > pml->cMaxPips)
2700             pml->cMoves = 0;
2701 
2702         pml->cMaxMoves = cMoves;
2703         pml->cMaxPips = cPip;
2704     }
2705 
2706     pm = pml->amMoves + pml->cMoves;
2707 
2708     PositionKey(anBoard, &key);
2709 
2710     for (i = 0; i < pml->cMoves; i++) {
2711         move *pm = &(pml->amMoves[i]);
2712 
2713         if (EqualKeys(key, pm->key)) {
2714             if (cMoves > pm->cMoves || cPip > pm->cPips) {
2715                 for (j = 0; j < cMoves * 2; j++)
2716                     pm->anMove[j] = anMoves[j] > -1 ? anMoves[j] : -1;
2717 
2718                 if (cMoves < 4)
2719                     pm->anMove[cMoves * 2] = -1;
2720 
2721                 pm->cMoves = cMoves;
2722                 pm->cPips = cPip;
2723             }
2724 
2725             return;
2726         }
2727     }
2728 
2729     for (i = 0; i < cMoves * 2; i++)
2730         pm->anMove[i] = anMoves[i] > -1 ? anMoves[i] : -1;
2731 
2732     if (cMoves < 4)
2733         pm->anMove[cMoves * 2] = -1;
2734 
2735     CopyKey(key, pm->key);
2736 
2737     pm->cMoves = cMoves;
2738     pm->cPips = cPip;
2739     pm->cmark = CMARK_NONE;
2740 
2741     for (i = 0; i < NUM_OUTPUTS; i++)
2742         pm->arEvalMove[i] = 0.0;
2743 
2744     pml->cMoves++;
2745 
2746     g_assert(pml->cMoves < MAX_INCOMPLETE_MOVES);
2747 }
2748 
2749 static int
2750 LegalMove(const TanBoard anBoard, int iSrc, int nPips)
2751 {
2752 
2753     int nBack;
2754     const int iDest = iSrc - nPips;
2755 
2756     if (iDest >= 0) {           /* Here we can do the Chris rule check */
2757         return (anBoard[0][23 - iDest] < 2);
2758     }
2759     /* otherwise, attempting to bear off */
2760 
2761     for (nBack = 24; nBack > 0; nBack--)
2762         if (anBoard[1][nBack] > 0)
2763             break;
2764 
2765     return (nBack <= 5 && (iSrc == nBack || iDest == -1));
2766 }
2767 
2768 static int
2769 GenerateMovesSub(movelist * pml, int anRoll[], int nMoveDepth,
2770                  int iPip, int cPip, const TanBoard anBoard, int anMoves[], int fPartial)
2771 {
2772     int i, fUsed = 0;
2773     TanBoard anBoardNew;
2774 
2775     if (nMoveDepth > 3 || !anRoll[nMoveDepth])
2776         return TRUE;
2777 
2778     if (anBoard[1][24]) {       /* on bar */
2779         if (anBoard[0][anRoll[nMoveDepth] - 1] >= 2)
2780             return TRUE;
2781 
2782         anMoves[nMoveDepth * 2] = 24;
2783         anMoves[nMoveDepth * 2 + 1] = 24 - anRoll[nMoveDepth];
2784 
2785         for (i = 0; i < 25; i++) {
2786             anBoardNew[0][i] = anBoard[0][i];
2787             anBoardNew[1][i] = anBoard[1][i];
2788         }
2789 
2790         ApplySubMove(anBoardNew, 24, anRoll[nMoveDepth], TRUE);
2791 
2792         if (GenerateMovesSub(pml, anRoll, nMoveDepth + 1, 23, cPip +
2793                              anRoll[nMoveDepth], (ConstTanBoard) anBoardNew, anMoves, fPartial))
2794             SaveMoves(pml, nMoveDepth + 1, cPip + anRoll[nMoveDepth], anMoves, (ConstTanBoard) anBoardNew, fPartial);
2795 
2796         return fPartial;
2797     } else {
2798         for (i = iPip; i >= 0; i--)
2799             if (anBoard[1][i] && LegalMove(anBoard, i, anRoll[nMoveDepth])) {
2800                 anMoves[nMoveDepth * 2] = i;
2801                 anMoves[nMoveDepth * 2 + 1] = i - anRoll[nMoveDepth];
2802 
2803                 memcpy(anBoardNew, anBoard, sizeof(anBoardNew));
2804 
2805                 ApplySubMove(anBoardNew, i, anRoll[nMoveDepth], TRUE);
2806 
2807                 if (GenerateMovesSub(pml, anRoll, nMoveDepth + 1,
2808                                      anRoll[0] == anRoll[1] ? i : 23,
2809                                      cPip + anRoll[nMoveDepth], (ConstTanBoard) anBoardNew, anMoves, fPartial))
2810                     SaveMoves(pml, nMoveDepth + 1, cPip +
2811                               anRoll[nMoveDepth], anMoves, (ConstTanBoard) anBoardNew, fPartial);
2812 
2813                 fUsed = 1;
2814             }
2815     }
2816 
2817     return !fUsed || fPartial;
2818 }
2819 
2820 extern int
2821 CompareMoves(const move * pm0, const move * pm1)
2822 {
2823 
2824     /*high score first */
2825     return (pm1->rScore > pm0->rScore || (pm1->rScore == pm0->rScore && pm1->rScore2 > pm0->rScore2)) ? 1 : -1;
2826 }
2827 
2828 static int
2829 CompareMovesGeneral(const move * pm0, const move * pm1)
2830 {
2831     TanBoard board[2];
2832     int back[2] = { -1, -1 };
2833     int a, b;
2834 
2835     int i = cmp_evalsetup(&pm0->esMove, &pm1->esMove);
2836 
2837     if (i)
2838         return -i;              /* sort descending */
2839 
2840     /* find the "back" chequer */
2841     PositionFromKey(board[0], &pm0->key);
2842     PositionFromKey(board[1], &pm1->key);
2843     for (a = 0; a < 2; a++) {
2844         for (b = 24; b > -1; b--) {
2845             if (board[a][1][b] > 0) {
2846                 back[a] = b;
2847                 break;
2848             }
2849         }
2850     }
2851 
2852     /* Rounding errors when collating evaluations with lookahead make
2853      * comparing for equality unreliable. The first check below catches
2854      * situations that would be obvious and puzzling to a human opponent.
2855      * In general, the third check is probably not reached as often as it
2856      * should and, among equal moves, the most "natural" one may not
2857      * always be chosen. */
2858 
2859     /* Winning now is always at least as good as winning later */
2860     if (back[0] == -1)
2861         return -1;
2862     if (back[1] == -1)
2863         return 1;
2864 
2865     if (pm0->rScore != pm1->rScore || pm0->rScore2 != pm1->rScore2)
2866         return CompareMoves(pm0, pm1);
2867 
2868     /* If everything else is equal "back" chequer at high point bad */
2869     return (back[0] > back[1] ? 1 : -1);
2870 }
2871 
2872 extern int
2873 GenerateMoves(movelist * pml, const TanBoard anBoard, int n0, int n1, int fPartial)
2874 {
2875 
2876     int anRoll[4], anMoves[8];
2877     anRoll[0] = n0;
2878     anRoll[1] = n1;
2879 
2880     anRoll[2] = anRoll[3] = ((n0 == n1) ? n0 : 0);
2881 
2882     pml->cMoves = pml->cMaxMoves = pml->cMaxPips = pml->iMoveBest = 0;
2883     pml->amMoves = MT_Get_aMoves();
2884     GenerateMovesSub(pml, anRoll, 0, 23, 0, anBoard, anMoves, fPartial);
2885 
2886     if (anRoll[0] != anRoll[1]) {
2887         swap(anRoll, anRoll + 1);
2888 
2889         GenerateMovesSub(pml, anRoll, 0, 23, 0, anBoard, anMoves, fPartial);
2890     }
2891 
2892     return pml->cMoves;
2893 }
2894 
2895 
2896 extern float
2897 KleinmanCount(int nPipOnRoll, int nPipNotOnRoll)
2898 {
2899     int nDiff, nSum;
2900     float rK;
2901 
2902     nDiff = nPipNotOnRoll - nPipOnRoll;
2903     nSum = nPipNotOnRoll + nPipOnRoll;
2904 
2905     if (nSum > 4) {
2906         rK = (nDiff + 4) / (2 * sqrtf(nSum - 4));
2907         return 0.5f * (1.0f + erff(rK));
2908     } else
2909         return 0.f;
2910 }
2911 
2912 extern int
2913 KeithCount(const TanBoard anBoard, int pn[2])
2914 {
2915     unsigned int anPips[2];
2916     int i, x;
2917     PipCount(anBoard, anPips);
2918     for (i = 0; i < 2; i++) {
2919         pn[i] = anPips[i];
2920         pn[i] += (MAX(1, anBoard[i][0]) - 1) * 2;
2921         pn[i] += MAX(1, anBoard[i][1]) - 1;
2922         pn[i] += MAX(3, anBoard[i][2]) - 3;
2923         for (x = 3; x < 6; x++)
2924             if (!anBoard[i][x])
2925                 pn[i]++;
2926     }
2927     return 0;
2928 }
2929 
2930 extern int
2931 IsightCount(const TanBoard anBoard, int pn[2])
2932 {
2933     unsigned int anPips[2];
2934     int anMenLeft[2] = { 0, 0 }, anCrossOver[2] = { 0, 0 };
2935     int i, x;
2936 
2937     PipCount(anBoard, anPips);
2938 
2939     for (x = 0; x < 25; x++) {
2940         anMenLeft[0] += anBoard[0][x];
2941         anMenLeft[1] += anBoard[1][x];
2942         anCrossOver[0] += anBoard[0][x] * (x / 6);
2943         anCrossOver[1] += anBoard[1][x] * (x / 6);
2944     }
2945 
2946     for (i = 0; i < 2; i++) {
2947         pn[i] = anPips[i];
2948         if (anMenLeft[i] > anMenLeft[1 - i])
2949             pn[i] += (anMenLeft[i] - anMenLeft[1 - i]);
2950         pn[i] += (MAX(2, anBoard[i][0]) - 2) * 2;
2951         pn[i] += MAX(2, anBoard[i][1]) - 2;
2952         pn[i] += MAX(3, anBoard[i][2]) - 3;
2953         for (x = 3; x < 6; x++)
2954             if (!anBoard[i][x] && anBoard[1 - i][x])
2955                 pn[i]++;
2956         if (anCrossOver[i] > anCrossOver[1 - i])
2957             pn[i] += (anCrossOver[i] - anCrossOver[1 - i]);
2958     }
2959     return 0;
2960 }
2961 
2962 extern int
2963 ThorpCount(const TanBoard anBoard, int *pnLeader, float *adjusted, int *pnTrailer)
2964 {
2965 
2966     int anCovered[2], anMenLeft[2];
2967     int x;
2968     unsigned int anPips[2];
2969 
2970     PipCount(anBoard, anPips);
2971 
2972     anMenLeft[0] = 0;
2973     anMenLeft[1] = 0;
2974     for (x = 0; x < 25; x++) {
2975         anMenLeft[0] += anBoard[0][x];
2976         anMenLeft[1] += anBoard[1][x];
2977     }
2978 
2979     anCovered[0] = 0;
2980     anCovered[1] = 0;
2981     for (x = 0; x < 6; x++) {
2982         if (anBoard[0][x])
2983             anCovered[0]++;
2984         if (anBoard[1][x])
2985             anCovered[1]++;
2986     }
2987 
2988     *pnLeader = anPips[1];
2989     *pnLeader += 2 * anMenLeft[1];
2990     *pnLeader += anBoard[1][0];
2991     *pnLeader -= anCovered[1];
2992     if (*pnLeader > 30)
2993         *adjusted = (float) (*pnLeader * 1.1f);
2994     else
2995         *adjusted = (float) *pnLeader;
2996 
2997     *pnTrailer = anPips[0];
2998     *pnTrailer += 2 * anMenLeft[0];
2999     *pnTrailer += anBoard[0][0];
3000     *pnTrailer -= anCovered[0];
3001 
3002     return 0;
3003 
3004 }
3005 
3006 
3007 extern void
3008 PipCount(const TanBoard anBoard, unsigned int anPips[2])
3009 {
3010 
3011     int i;
3012 
3013     anPips[0] = 0;
3014     anPips[1] = 0;
3015 
3016     for (i = 0; i < 25; i++) {
3017         anPips[0] += anBoard[0][i] * (i + 1);
3018         anPips[1] += anBoard[1][i] * (i + 1);
3019     }
3020 }
3021 
3022 
3023 
3024 static void
3025 StatusHypergammon1(char *sz)
3026 {
3027 
3028     BearoffStatus(apbcHyper[0], sz);
3029 
3030 }
3031 
3032 static void
3033 StatusHypergammon2(char *sz)
3034 {
3035 
3036     BearoffStatus(apbcHyper[1], sz);
3037 
3038 }
3039 
3040 static void
3041 StatusHypergammon3(char *sz)
3042 {
3043 
3044     BearoffStatus(apbcHyper[2], sz);
3045 
3046 }
3047 
3048 
3049 static void
3050 StatusBearoff2(char *sz)
3051 {
3052 
3053     BearoffStatus(pbc2, sz);
3054 
3055 }
3056 
3057 static void
3058 StatusBearoff1(char *sz)
3059 {
3060 
3061     BearoffStatus(pbc1, sz);
3062 
3063 }
3064 
3065 static void
3066 StatusNeuralNet(neuralnet * pnn, char *szTitle, char *sz)
3067 {
3068     char buf[200];
3069     sz += sprintf(sz, " * %s %s:\n", szTitle, _("neural network evaluator"));
3070     sprintf(buf, _("version %s, %u inputs, %u hidden units"), WEIGHTS_VERSION, pnn->cInput, pnn->cHidden);
3071     sprintf(sz, "   - %s.\n\n", buf);
3072 }
3073 
3074 static void
3075 StatusRace(char *sz)
3076 {
3077 
3078     StatusNeuralNet(&nnRace, _("Race"), sz);
3079 }
3080 
3081 static void
3082 StatusCrashed(char *sz)
3083 {
3084 
3085     StatusNeuralNet(&nnContact, _("Crashed"), sz);
3086 }
3087 
3088 static void
3089 StatusContact(char *sz)
3090 {
3091 
3092     StatusNeuralNet(&nnContact, _("Contact"), sz);
3093 }
3094 
3095 static void
3096 StatusOS(char *sz)
3097 {
3098     BearoffStatus(pbcOS, sz);
3099 }
3100 
3101 static void
3102 StatusTS(char *sz)
3103 {
3104     BearoffStatus(pbcTS, sz);
3105 }
3106 
3107 static classstatusfunc acsf[N_CLASSES] = {
3108     NULL,
3109     StatusHypergammon1, StatusHypergammon2, StatusHypergammon3,
3110     StatusBearoff2, StatusTS,
3111     StatusBearoff1, StatusOS,
3112     StatusRace, StatusCrashed, StatusContact
3113 };
3114 
3115 extern void
3116 EvalStatus(char *szOutput)
3117 {
3118 
3119     int i;
3120 
3121     *szOutput = 0;
3122 
3123     for (i = N_CLASSES - 1; i >= 0; i--)
3124         if (acsf[i])
3125             acsf[i] (strchr(szOutput, 0));
3126 
3127     sprintf(strchr(szOutput, 0), _(" * " "Weights file and databases installed in" ":\n   - %s\n"), getPkgDataDir());
3128 }
3129 
3130 
3131 extern char
3132 *
3133 GetCubeRecommendation(const cubedecision cd)
3134 {
3135     switch (cd) {
3136     case DOUBLE_TAKE:
3137         return _("Double, take");
3138     case DOUBLE_PASS:
3139         return _("Double, pass");
3140     case NODOUBLE_TAKE:
3141         return _("No double, take");
3142     case TOOGOOD_TAKE:
3143         return _("Too good to double, take");
3144     case TOOGOOD_PASS:
3145         return _("Too good to double, pass");
3146     case DOUBLE_BEAVER:
3147         return _("Double, beaver");
3148     case NODOUBLE_BEAVER:
3149         return _("No double, beaver");
3150     case REDOUBLE_TAKE:
3151         return _("Redouble, take");
3152     case REDOUBLE_PASS:
3153         return _("Redouble, pass");
3154     case NO_REDOUBLE_TAKE:
3155         return _("No redouble, take");
3156     case TOOGOODRE_TAKE:
3157         return _("Too good to redouble, take");
3158     case TOOGOODRE_PASS:
3159         return _("Too good to redouble, pass");
3160     case NO_REDOUBLE_BEAVER:
3161         return _("No redouble, beaver");
3162     case NODOUBLE_DEADCUBE:
3163         return _("Never double, take (dead cube)");
3164     case NO_REDOUBLE_DEADCUBE:
3165         return _("Never redouble, take (dead cube)");
3166     case OPTIONAL_DOUBLE_BEAVER:
3167         return _("Optional double, beaver");
3168     case OPTIONAL_DOUBLE_TAKE:
3169         return _("Optional double, take");
3170     case OPTIONAL_REDOUBLE_TAKE:
3171         return _("Optional redouble, take");
3172     case OPTIONAL_DOUBLE_PASS:
3173         return _("Optional double, pass");
3174     case OPTIONAL_REDOUBLE_PASS:
3175         return _("Optional redouble, pass");
3176     default:
3177         return _("Unknown cube decision");
3178     }
3179 }
3180 
3181 
3182 extern void
3183 EvalCacheFlush(void)
3184 {
3185     CacheFlush(&cEval);
3186 }
3187 
3188 void
3189 CommandClearCache(char *UNUSED(sz))
3190 {
3191     EvalCacheFlush();
3192 }
3193 
3194 extern double
3195 GetEvalCacheSize(void)
3196 {
3197     if (cEval.size == 0)
3198         return 0;
3199     else {
3200         double value = log(cEval.size) / log(2);
3201         if (value < 15)
3202             return 0;
3203         if (value < 17)
3204             return .5;          /* Special case for old default 65536 */
3205         if (value >= CACHE_SIZE_GUIMAX)
3206             return CACHE_SIZE_GUIMAX - 16;      /* Maximum value */
3207         else
3208             return value - 16;
3209     }
3210 }
3211 
3212 extern void
3213 SetEvalCacheSize(unsigned int size)
3214 {
3215     EvalCacheResize((size == 0) ? 0 : 1U << (size + 16));
3216 }
3217 
3218 extern unsigned int
3219 GetEvalCacheEntries(void)
3220 {
3221     return cCache;
3222 }
3223 
3224 extern int
3225 GetCacheMB(int size)
3226 {
3227     if (size <= 0)
3228         return 0;
3229     else
3230         return (1 << (size + 15)) * sizeof(cacheNode) / (1024 * 1024);
3231 }
3232 
3233 extern int
3234 EvalCacheResize(unsigned int cNew)
3235 {
3236     cCache = CacheResize(&cEval, cNew);
3237     return cCache;
3238 }
3239 
3240 extern int
3241 EvalCacheStats(unsigned int *pcUsed, unsigned int *pcLookup, unsigned int *pcHit)
3242 {
3243     CacheStats(&cEval, pcLookup, pcHit, pcUsed);
3244     CacheStats(&cpEval, pcLookup + 1, pcHit + 1, pcUsed + 1);
3245     return 0;
3246 }
3247 
3248 extern int
3249 SetCubeInfoMoney(cubeinfo * pci, const int nCube, const int fCubeOwner,
3250                  const int fMove, const int fJacoby, const int fBeavers, const bgvariation bgv)
3251 {
3252 
3253     if (nCube < 1 || fCubeOwner < -1 || fCubeOwner > 1 || fMove < 0 || fMove > 1) {     /* FIXME also illegal if nCube is not a power of 2 */
3254         memset(pci, 0, sizeof(cubeinfo));
3255         return -1;
3256     }
3257 
3258     pci->nCube = nCube;
3259     pci->fCubeOwner = fCubeOwner;
3260     pci->fMove = fMove;
3261     pci->fJacoby = fJacoby;
3262     pci->fBeavers = fBeavers;
3263     pci->nMatchTo = pci->anScore[0] = pci->anScore[1] = pci->fCrawford = 0;
3264     pci->bgv = bgv;
3265 
3266     pci->arGammonPrice[0] = pci->arGammonPrice[1] =
3267         pci->arGammonPrice[2] = pci->arGammonPrice[3] = (fJacoby && fCubeOwner == -1) ? 0.0f : 1.0f;
3268 
3269     return 0;
3270 }
3271 
3272 static int
3273 SetCubeInfoMatch(cubeinfo * pci, const int nCube, const int fCubeOwner,
3274                  const int fMove, const int nMatchTo, const int anScore[2], const int fCrawford, const bgvariation bgv)
3275 {
3276 
3277     if (nCube < 1 || fCubeOwner < -1 || fCubeOwner > 1 || fMove < 0 || fMove > 1 || nMatchTo < 1 || anScore[0] >= nMatchTo || anScore[1] >= nMatchTo) { /* FIXME also illegal if nCube is not a power of 2 */
3278         memset(pci, 0, sizeof(cubeinfo));
3279         return -1;
3280     }
3281 
3282     pci->nCube = nCube;
3283     pci->fCubeOwner = fCubeOwner;
3284     pci->fMove = fMove;
3285     pci->fJacoby = pci->fBeavers = FALSE;
3286     pci->nMatchTo = nMatchTo;
3287     pci->anScore[0] = anScore[0];
3288     pci->anScore[1] = anScore[1];
3289     pci->fCrawford = fCrawford;
3290     pci->bgv = bgv;
3291 
3292     /*
3293      * FIXME: calculate gammon price when initializing program
3294      * instead of recalculating it again and again, or cache it.
3295      */
3296 
3297     {
3298 
3299         int nAway0 = pci->nMatchTo - pci->anScore[0] - 1;
3300         int nAway1 = pci->nMatchTo - pci->anScore[1] - 1;
3301 
3302         if ((!nAway0 || !nAway1) && !fCrawford) {
3303             if (!nAway0)
3304                 memcpy(pci->arGammonPrice, aaaafGammonPricesPostCrawford[LogCube(pci->nCube)]
3305                        [nAway1][0], 4 * sizeof(float));
3306             else
3307                 memcpy(pci->arGammonPrice, aaaafGammonPricesPostCrawford[LogCube(pci->nCube)]
3308                        [nAway0][1], 4 * sizeof(float));
3309         } else
3310             memcpy(pci->arGammonPrice, aaaafGammonPrices[LogCube(pci->nCube)]
3311                    [nAway0][nAway1], 4 * sizeof(float));
3312 
3313     }
3314 
3315     return 0;
3316 }
3317 
3318 extern int
3319 SetCubeInfo(cubeinfo * pci, const int nCube, const int fCubeOwner,
3320             const int fMove, const int nMatchTo, const int anScore[2],
3321             const int fCrawford, const int fJacoby, const int fBeavers, const bgvariation bgv)
3322 {
3323 
3324     return nMatchTo ? SetCubeInfoMatch(pci, nCube, fCubeOwner, fMove,
3325                                        nMatchTo, anScore, fCrawford, bgv) :
3326         SetCubeInfoMoney(pci, nCube, fCubeOwner, fMove, fJacoby, fBeavers, bgv);
3327 }
3328 
3329 
3330 static int
3331 isOptional(const float r1, const float r2)
3332 {
3333 
3334     const float epsilon = 1.0e-5f;
3335 
3336     return (fabsf(r1 - r2) <= epsilon);
3337 
3338 }
3339 
3340 static int
3341 winGammon(const float arOutput[NUM_ROLLOUT_OUTPUTS])
3342 {
3343 
3344     return (arOutput[OUTPUT_WINGAMMON] > 0.0f);
3345 
3346 }
3347 
3348 extern cubedecision
3349 FindBestCubeDecision(float arDouble[], float aarOutput[2][NUM_ROLLOUT_OUTPUTS], const cubeinfo * pci)
3350 {
3351 
3352     /*
3353      * FindBestCubeDecision:
3354      *
3355      *    Calculate optimal cube decision and equity/mwc for this.
3356      *
3357      * Input:
3358      *    arDouble    - array with equities or mwc's:
3359      *                      arDouble[ 1 ]: no double,
3360      *                      arDouble[ 2 ]: double take
3361      *                      arDouble[ 3 ]: double pass
3362      *    pci         - pointer to cube info
3363      *
3364      * Output:
3365      *    arDouble    - array with equities or mwc's
3366      *                      arDouble[ 0 ]: equity for optimal cube decision
3367      *
3368      * Returns:
3369      *    cube decision
3370      *
3371      */
3372 
3373     int f;
3374 
3375     /* Check if cube is available */
3376 
3377     if (!GetDPEq(NULL, NULL, pci)) {
3378 
3379         arDouble[OUTPUT_OPTIMAL] = arDouble[OUTPUT_NODOUBLE];
3380 
3381         /* for match play distinguish between dead cube and not available cube */
3382 
3383         if (pci->nMatchTo && (pci->fCubeOwner < 0 || pci->fCubeOwner == pci->fMove))
3384             return (pci->fCubeOwner == -1) ? NODOUBLE_DEADCUBE : NO_REDOUBLE_DEADCUBE;
3385         else
3386             return NOT_AVAILABLE;
3387 
3388     }
3389 
3390 
3391     /* Cube is available: find optimal cube action */
3392 
3393     if ((arDouble[OUTPUT_TAKE] >= arDouble[OUTPUT_NODOUBLE]) && (arDouble[OUTPUT_DROP] >= arDouble[OUTPUT_NODOUBLE])) {
3394 
3395         /* DT >= ND and DP >= ND */
3396 
3397         /* we have a double */
3398 
3399         if (arDouble[OUTPUT_DROP] > arDouble[OUTPUT_TAKE]) {
3400 
3401             /* 6. DP > DT >= ND: Double, take */
3402 
3403             f = isOptional(arDouble[OUTPUT_TAKE], arDouble[OUTPUT_NODOUBLE]);
3404 
3405             arDouble[OUTPUT_OPTIMAL] = arDouble[OUTPUT_TAKE];
3406 
3407             if (!pci->nMatchTo && arDouble[OUTPUT_TAKE] >= -2.0f && arDouble[OUTPUT_TAKE] <= 0.0f && pci->fBeavers) {
3408                 if (arDouble[OUTPUT_TAKE] * 2.0f < arDouble[OUTPUT_NODOUBLE]) {
3409                     /*not a double if we can beaver */
3410                     return NODOUBLE_BEAVER;
3411                 } else
3412                     /* beaver (jacoby paradox) */
3413                     return f ? OPTIONAL_DOUBLE_BEAVER : DOUBLE_BEAVER;
3414             } else {
3415                 /* ...take */
3416                 if (f)
3417                     return (pci->fCubeOwner == -1) ? OPTIONAL_DOUBLE_TAKE : OPTIONAL_REDOUBLE_TAKE;
3418                 else
3419                     return (pci->fCubeOwner == -1) ? DOUBLE_TAKE : REDOUBLE_TAKE;
3420             }
3421 
3422         } else {
3423 
3424             /* 4. DT >= DP >= ND: Double, pass */
3425 
3426             /*
3427              * the double is optional iff:
3428              * (1) equity(no double) = equity(drop)
3429              * (2) the player can win gammon
3430              * (3a) it's match play
3431              * or if it's money play
3432              * (3b) it's not a centered cube
3433              * or
3434              * (3c) the Jacoby rule is not in effect
3435              */
3436 
3437             arDouble[OUTPUT_OPTIMAL] = arDouble[OUTPUT_DROP];
3438 
3439             if (isOptional(arDouble[OUTPUT_NODOUBLE],
3440                            arDouble[OUTPUT_DROP]) &&
3441                 (winGammon(aarOutput[0]) && (pci->nMatchTo || pci->fCubeOwner != -1 || !pci->fJacoby)))
3442                 return (pci->fCubeOwner == -1) ? OPTIONAL_DOUBLE_PASS : OPTIONAL_REDOUBLE_PASS;
3443             else
3444                 return (pci->fCubeOwner == -1) ? DOUBLE_PASS : REDOUBLE_PASS;
3445 
3446         }
3447     } else {
3448 
3449         /* no double */
3450 
3451         /* ND > DT or ND > DP */
3452 
3453         arDouble[OUTPUT_OPTIMAL] = arDouble[OUTPUT_NODOUBLE];
3454 
3455         if (arDouble[OUTPUT_NODOUBLE] > arDouble[OUTPUT_TAKE]) {
3456 
3457             /* ND > DT */
3458 
3459             if (arDouble[OUTPUT_TAKE] > arDouble[OUTPUT_DROP]) {
3460 
3461                 /* 1. ND > DT > DP: Too good, pass */
3462 
3463                 /* sanity check: don't play on for a gammon if none is possible... */
3464 
3465                 if (winGammon(aarOutput[0]))
3466                     return (pci->fCubeOwner == -1) ? TOOGOOD_PASS : TOOGOODRE_PASS;
3467                 else
3468                     return (pci->fCubeOwner == -1) ? DOUBLE_PASS : REDOUBLE_PASS;
3469 
3470             } else if (arDouble[OUTPUT_NODOUBLE] > arDouble[OUTPUT_DROP]) {
3471 
3472                 /* 2. ND > DP > DT: Too good, take */
3473 
3474                 if (winGammon(aarOutput[0]))
3475                     return (pci->fCubeOwner == -1) ? TOOGOOD_TAKE : TOOGOODRE_TAKE;
3476                 else
3477                     return (pci->fCubeOwner == -1) ? NODOUBLE_TAKE : NO_REDOUBLE_TAKE;
3478 
3479             } else {
3480 
3481                 /* 5. DP > ND > DT: No double, {take, beaver} */
3482 
3483                 if (arDouble[OUTPUT_TAKE] >= -2.0f && arDouble[OUTPUT_TAKE] <= 0.0f && !pci->nMatchTo && pci->fBeavers)
3484                     return (pci->fCubeOwner == -1) ? NODOUBLE_BEAVER : NO_REDOUBLE_BEAVER;
3485                 else
3486                     return (pci->fCubeOwner == -1) ? NODOUBLE_TAKE : NO_REDOUBLE_TAKE;
3487 
3488             }
3489 
3490         } else {
3491 
3492             /* 3. DT >= ND > DP: Too good, pass */
3493 
3494             if (winGammon(aarOutput[0]))
3495                 return (pci->fCubeOwner == -1) ? TOOGOOD_PASS : TOOGOODRE_PASS;
3496             else
3497                 return (pci->fCubeOwner == -1) ? DOUBLE_PASS : REDOUBLE_PASS;
3498 
3499         }
3500 
3501     }
3502 }
3503 
3504 
3505 extern cubedecision
3506 FindCubeDecision(float arDouble[], float aarOutput[][NUM_ROLLOUT_OUTPUTS], const cubeinfo * pci)
3507 {
3508     GetDPEq(NULL, &arDouble[OUTPUT_DROP], pci);
3509     arDouble[OUTPUT_NODOUBLE] = aarOutput[0][OUTPUT_CUBEFUL_EQUITY];
3510     arDouble[OUTPUT_TAKE] = aarOutput[1][OUTPUT_CUBEFUL_EQUITY];
3511 
3512     if (pci->nMatchTo) {
3513         /* convert to normalized money equity */
3514 
3515         int i;
3516 
3517         for (i = 1; i < 4; i++)
3518             arDouble[i] = mwc2eq(arDouble[i], pci);
3519     }
3520 
3521     return FindBestCubeDecision(arDouble, aarOutput, pci);
3522 }
3523 
3524 
3525 
3526 static int
3527 fDoCubeful(cubeinfo * pci)
3528 {
3529 
3530     if (pci->anScore[0] + pci->nCube >= pci->nMatchTo && pci->anScore[1] + pci->nCube >= pci->nMatchTo)
3531         /* cube is dead */
3532         return FALSE;
3533 
3534     if (pci->anScore[0] == pci->nMatchTo - 2 && pci->anScore[1] == pci->nMatchTo - 2)
3535         /* score is -2,-2 */
3536         return FALSE;
3537 
3538     if (pci->fCrawford)
3539         /* cube is dead in Crawford game */
3540         return FALSE;
3541 
3542     return TRUE;
3543 }
3544 
3545 
3546 extern int
3547 GetDPEq(int *pfCube, float *prDPEq, const cubeinfo * pci)
3548 {
3549 
3550     int fCube, fPostCrawford;
3551 
3552     if (!pci->nMatchTo) {
3553 
3554         /* Money game:
3555          * Double, pass equity for money game is 1.0 points, since we always
3556          * calculate equity normed to a 1-cube.
3557          * Take the double branch if the cube is centered or I own the cube. */
3558 
3559         if (prDPEq)
3560             *prDPEq = 1.0;
3561 
3562         fCube = (pci->fCubeOwner == -1) || (pci->fCubeOwner == pci->fMove);
3563 
3564         if (pfCube)
3565             *pfCube = fCube;
3566 
3567     } else {
3568 
3569         /* Match play:
3570          * Equity for double, pass is found from the match equity table.
3571          * Take the double branch is I can/will use cube:
3572          * - if it is not the Crawford game,
3573          * - and if the cube is not dead,
3574          * - and if it is post-Crawford and I'm trailing
3575          * - and if I have access to the cube.
3576          */
3577 
3578         /* FIXME: equity for double, pass */
3579         fPostCrawford = !pci->fCrawford &&
3580             (pci->anScore[0] == pci->nMatchTo - 1 || pci->anScore[1] == pci->nMatchTo - 1);
3581 
3582         fCube = (!pci->fCrawford) &&
3583             (pci->anScore[pci->fMove] + pci->nCube < pci->nMatchTo) &&
3584             (!(fPostCrawford && (pci->anScore[pci->fMove] == pci->nMatchTo - 1)))
3585             && ((pci->fCubeOwner == -1) || (pci->fCubeOwner == pci->fMove));
3586 
3587         if (prDPEq)
3588             *prDPEq =
3589                 getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo,
3590                       pci->fMove, pci->nCube, pci->fMove, pci->fCrawford, aafMET, aafMETPostCrawford);
3591 
3592         if (pfCube)
3593             *pfCube = fCube;
3594 
3595     }
3596 
3597     return fCube;
3598 
3599 }
3600 
3601 
3602 static float
3603 MoneyLive(const float rW, const float rL, const float p, const cubeinfo * pci)
3604 {
3605 
3606     if (pci->fCubeOwner == -1) {
3607 
3608         /* centered cube */
3609         float rTP = (rL - 0.5f) / (rW + rL + 0.5f);
3610         float rCP = (rL + 1.0f) / (rW + rL + 0.5f);
3611 
3612         if (p < rTP)
3613             /* linear interpolation between
3614              * (0,-rL) and ( rTP,-1) */
3615             return (pci->fJacoby) ? -1.0f : (-rL + (-1.0f + rL) * p / rTP);
3616         else if (p < rCP)
3617             /* linear interpolation between
3618              * (rTP,-1) and (rCP,+1) */
3619             return -1.0f + 2.0f * (p - rTP) / (rCP - rTP);
3620         else
3621             /* linear interpolation between
3622              * (rCP,+1) and (1,+rW) */
3623             return (pci->fJacoby) ? 1.0f : (+1.0f + (rW - 1.0f) * (p - rCP) / (1.0f - rCP));
3624 
3625     } else if (pci->fCubeOwner == pci->fMove) {
3626 
3627         /* owned cube */
3628 
3629         /* cash point */
3630         float rCP = (rL + 1.0f) / (rW + rL + 0.5f);
3631 
3632         if (p < rCP)
3633             /* linear interpolation between
3634              * (0,-rL) and (rCP,+1) */
3635             return -rL + (1.0f + rL) * p / rCP;
3636         else
3637             /* linear interpolation between
3638              * (rCP,+1) and (1,+rW) */
3639             return +1.0f + (rW - 1.0f) * (p - rCP) / (1.0f - rCP);
3640 
3641     } else {
3642 
3643         /* unavailable cube */
3644 
3645         /* take point */
3646         float rTP = (rL - 0.5f) / (rW + rL + 0.5f);
3647 
3648         if (p < rTP)
3649             /* linear interpolation between
3650              * (0,-rL) and ( rTP,-1) */
3651             return -rL + (-1.0f + rL) * p / rTP;
3652         else
3653             /* linear interpolation between
3654              * (rTP,-1) and (1,rW) */
3655             return -1.0f + (rW + 1.0f) * (p - rTP) / (1.0f - rTP);
3656 
3657     }
3658 
3659 }
3660 
3661 
3662 extern float
3663 Cl2CfMoney(float arOutput[NUM_OUTPUTS], cubeinfo * pci, float rCubeX)
3664 {
3665     const float epsilon = 0.0000001f;
3666     const float omepsilon = 0.9999999f;
3667 
3668     float rW, rL;
3669     float rEqDead, rEqLive;
3670 
3671     /* money game */
3672 
3673     /* Transform cubeless 0-ply equity to cubeful 0-ply equity using
3674      * Rick Janowski's formulas [insert ref here]. */
3675 
3676     /* First calculate average win and loss W and L: */
3677 
3678     if (arOutput[OUTPUT_WIN] > epsilon)
3679         rW = 1.0f + (arOutput[OUTPUT_WINGAMMON] + arOutput[OUTPUT_WINBACKGAMMON]) / arOutput[OUTPUT_WIN];
3680     else {
3681         /* basically a dead cube */
3682         return Utility(arOutput, pci);
3683     }
3684 
3685     if (arOutput[OUTPUT_WIN] < omepsilon)
3686         rL = 1.0f + (arOutput[OUTPUT_LOSEGAMMON] + arOutput[OUTPUT_LOSEBACKGAMMON]) / (1.0f - arOutput[OUTPUT_WIN]);
3687     else {
3688         /* basically a dead cube */
3689         return Utility(arOutput, pci);
3690     }
3691 
3692 
3693     rEqDead = Utility(arOutput, pci);
3694     rEqLive = MoneyLive(rW, rL, arOutput[OUTPUT_WIN], pci);
3695 
3696     return rEqDead * (1.0f - rCubeX) + rEqLive * rCubeX;
3697 
3698 }
3699 
3700 static float
3701 Cl2CfMatchCentered(float arOutput[NUM_OUTPUTS], cubeinfo * pci, float rCubeX)
3702 {
3703 
3704     /* normalized score */
3705 
3706     float rG0, rBG0, rG1, rBG1;
3707     float arCP[2];
3708 
3709     float rMWCDead, rMWCLive, rMWCWin, rMWCLose;
3710     float rMWCOppCash, rMWCCash, rOppTG, rTG;
3711     float aarMETResult[2][DTLBP1 + 1];
3712 
3713     /* Centered cube */
3714 
3715     /* Calculate normal, gammon, and backgammon ratios */
3716 
3717     if (arOutput[OUTPUT_WIN] > 0.0f) {
3718         rG0 = (arOutput[OUTPUT_WINGAMMON] - arOutput[OUTPUT_WINBACKGAMMON]) / arOutput[OUTPUT_WIN];
3719         rBG0 = arOutput[OUTPUT_WINBACKGAMMON] / arOutput[OUTPUT_WIN];
3720     } else {
3721         rG0 = 0.0f;
3722         rBG0 = 0.0f;
3723     }
3724 
3725     if (arOutput[OUTPUT_WIN] < 1.0f) {
3726         rG1 = (arOutput[OUTPUT_LOSEGAMMON] - arOutput[OUTPUT_LOSEBACKGAMMON]) / (1.0f - arOutput[OUTPUT_WIN]);
3727         rBG1 = arOutput[OUTPUT_LOSEBACKGAMMON] / (1.0f - arOutput[OUTPUT_WIN]);
3728     } else {
3729         rG1 = 0.0f;
3730         rBG1 = 0.0f;
3731     }
3732 
3733     /* MWC(dead cube) = cubeless equity */
3734 
3735     rMWCDead = eq2mwc(Utility(arOutput, pci), pci);
3736 
3737     /* Get live cube cash points */
3738 
3739     GetPoints(arOutput, pci, arCP);
3740 
3741     getMEMultiple(pci->anScore[0], pci->anScore[1], pci->nMatchTo,
3742                   pci->nCube, -1, -1, pci->fCrawford, aafMET, aafMETPostCrawford, aarMETResult[0], aarMETResult[1]);
3743 
3744     rMWCCash = aarMETResult[pci->fMove][NDW];
3745 
3746     rMWCOppCash = aarMETResult[pci->fMove][NDL];
3747 
3748     rOppTG = 1.0f - arCP[!pci->fMove];
3749     rTG = arCP[pci->fMove];
3750 
3751     if (arOutput[OUTPUT_WIN] <= rOppTG) {
3752 
3753         /* Opp too good to double */
3754 
3755         rMWCLose = (1.0f - rG1 - rBG1) * aarMETResult[pci->fMove][NDL]
3756             + rG1 * aarMETResult[pci->fMove][NDLG]
3757             + rBG1 * aarMETResult[pci->fMove][NDLB];
3758 
3759         if (rOppTG > 0.0f)
3760             /* avoid division by zero */
3761             rMWCLive = rMWCLose + (rMWCOppCash - rMWCLose) * arOutput[OUTPUT_WIN] / rOppTG;
3762         else
3763             rMWCLive = rMWCLose;
3764 
3765         /* (1-x) MWC(dead) + x MWC(live) */
3766 
3767         return rMWCDead * (1.0f - rCubeX) + rMWCLive * rCubeX;
3768 
3769     } else if (arOutput[OUTPUT_WIN] < rTG) {
3770 
3771         /* In doubling window */
3772 
3773         rMWCLive = rMWCOppCash + (rMWCCash - rMWCOppCash) * (arOutput[OUTPUT_WIN] - rOppTG) / (rTG - rOppTG);
3774         return rMWCDead * (1.0f - rCubeX) + rMWCLive * rCubeX;
3775 
3776     } else {
3777 
3778         /* I'm too good to double */
3779 
3780         /* MWC(live cube) linear interpolation between the
3781          * points:
3782          *
3783          * p = TG, MWC = I win 1 point
3784          * p = 1, MWC = I win (normal, gammon, or backgammon)
3785          *
3786          */
3787 
3788         rMWCWin = (1.0f - rG0 - rBG0) * aarMETResult[pci->fMove][NDW]
3789             + rG0 * aarMETResult[pci->fMove][NDWG]
3790             + rBG0 * aarMETResult[pci->fMove][NDWB];
3791 
3792         if (rTG < 1.0f)
3793             rMWCLive = rMWCCash + (rMWCWin - rMWCCash) * (arOutput[OUTPUT_WIN] - rTG) / (1.0f - rTG);
3794         else
3795             rMWCLive = rMWCWin;
3796 
3797         /* (1-x) MWC(dead) + x MWC(live) */
3798 
3799         return rMWCDead * (1.0f - rCubeX) + rMWCLive * rCubeX;
3800 
3801     }
3802 
3803 }
3804 
3805 static float
3806 Cl2CfMatchOwned(float arOutput[NUM_OUTPUTS], cubeinfo * pci, float rCubeX)
3807 {
3808 
3809     /* normalized score */
3810 
3811     float rG0, rBG0, rG1, rBG1;
3812     float arCP[2];
3813 
3814     float rMWCDead, rMWCLive, rMWCWin, rMWCLose;
3815     float rMWCCash, rTG;
3816     float aarMETResult[2][DTLBP1 + 1];
3817 
3818     /* I own cube */
3819 
3820     /* Calculate normal, gammon, and backgammon ratios */
3821 
3822     if (arOutput[OUTPUT_WIN] > 0.0f) {
3823         rG0 = (arOutput[OUTPUT_WINGAMMON] - arOutput[OUTPUT_WINBACKGAMMON]) / arOutput[OUTPUT_WIN];
3824         rBG0 = arOutput[OUTPUT_WINBACKGAMMON] / arOutput[OUTPUT_WIN];
3825     } else {
3826         rG0 = 0.0f;
3827         rBG0 = 0.0f;
3828     }
3829 
3830     if (arOutput[OUTPUT_WIN] < 1.0f) {
3831         rG1 = (arOutput[OUTPUT_LOSEGAMMON] - arOutput[OUTPUT_LOSEBACKGAMMON]) / (1.0f - arOutput[OUTPUT_WIN]);
3832         rBG1 = arOutput[OUTPUT_LOSEBACKGAMMON] / (1.0f - arOutput[OUTPUT_WIN]);
3833     } else {
3834         rG1 = 0.0;
3835         rBG1 = 0.0;
3836     }
3837 
3838     /* MWC(dead cube) = cubeless equity */
3839 
3840     rMWCDead = eq2mwc(Utility(arOutput, pci), pci);
3841 
3842     /* Get live cube cash points */
3843 
3844     GetPoints(arOutput, pci, arCP);
3845 
3846     getMEMultiple(pci->anScore[0], pci->anScore[1], pci->nMatchTo,
3847                   pci->nCube, -1, -1, pci->fCrawford, aafMET, aafMETPostCrawford, aarMETResult[0], aarMETResult[1]);
3848 
3849     rMWCCash = aarMETResult[pci->fMove][NDW];
3850 
3851     rTG = arCP[pci->fMove];
3852 
3853     if (arOutput[OUTPUT_WIN] <= rTG) {
3854 
3855         /* MWC(live cube) linear interpolation between the
3856          * points:
3857          *
3858          * p = 0, MWC = I lose (normal, gammon, or backgammon)
3859          * p = TG, MWC = I win 1 point
3860          *
3861          */
3862 
3863         rMWCLose = (1.0f - rG1 - rBG1) * aarMETResult[pci->fMove][NDL]
3864             + rG1 * aarMETResult[pci->fMove][NDLG]
3865             + rBG1 * aarMETResult[pci->fMove][NDLB];
3866 
3867         if (rTG > 0.0f)
3868             rMWCLive = rMWCLose + (rMWCCash - rMWCLose) * arOutput[OUTPUT_WIN] / rTG;
3869         else
3870             rMWCLive = rMWCLose;
3871 
3872         /* (1-x) MWC(dead) + x MWC(live) */
3873 
3874         return rMWCDead * (1.0f - rCubeX) + rMWCLive * rCubeX;
3875 
3876     } else {
3877 
3878         /* we are too good to double */
3879 
3880         /* MWC(live cube) linear interpolation between the
3881          * points:
3882          *
3883          * p = TG, MWC = I win 1 point
3884          * p = 1, MWC = I win (normal, gammon, or backgammon)
3885          *
3886          */
3887 
3888         rMWCWin = (1.0f - rG0 - rBG0) * aarMETResult[pci->fMove][NDW]
3889             + rG0 * aarMETResult[pci->fMove][NDWG]
3890             + rBG0 * aarMETResult[pci->fMove][NDWB];
3891 
3892         if (rTG < 1.0f)
3893             rMWCLive = rMWCCash + (rMWCWin - rMWCCash) * (arOutput[OUTPUT_WIN] - rTG) / (1.0f - rTG);
3894         else
3895             rMWCLive = rMWCWin;
3896 
3897         /* (1-x) MWC(dead) + x MWC(live) */
3898 
3899         return rMWCDead * (1.0f - rCubeX) + rMWCLive * rCubeX;
3900 
3901     }
3902 
3903 }
3904 
3905 
3906 static float
3907 Cl2CfMatchUnavailable(float arOutput[NUM_OUTPUTS], cubeinfo * pci, float rCubeX)
3908 {
3909 
3910     /* normalized score */
3911 
3912     float rG0, rBG0, rG1, rBG1;
3913     float arCP[2];
3914 
3915     float rMWCDead, rMWCLive, rMWCWin, rMWCLose;
3916     float rMWCOppCash, rOppTG;
3917     float aarMETResult[2][DTLBP1 + 1];
3918 
3919     /* I own cube */
3920 
3921     /* Calculate normal, gammon, and backgammon ratios */
3922 
3923     if (arOutput[OUTPUT_WIN] > 0.0f) {
3924         rG0 = (arOutput[OUTPUT_WINGAMMON] - arOutput[OUTPUT_WINBACKGAMMON]) / arOutput[OUTPUT_WIN];
3925         rBG0 = arOutput[OUTPUT_WINBACKGAMMON] / arOutput[OUTPUT_WIN];
3926     } else {
3927         rG0 = 0.0f;
3928         rBG0 = 0.0f;
3929     }
3930 
3931     if (arOutput[OUTPUT_WIN] < 1.0f) {
3932         rG1 = (arOutput[OUTPUT_LOSEGAMMON] - arOutput[OUTPUT_LOSEBACKGAMMON]) / (1.0f - arOutput[OUTPUT_WIN]);
3933         rBG1 = arOutput[OUTPUT_LOSEBACKGAMMON] / (1.0f - arOutput[OUTPUT_WIN]);
3934     } else {
3935         rG1 = 0.0;
3936         rBG1 = 0.0;
3937     }
3938 
3939     /* MWC(dead cube) = cubeless equity */
3940 
3941     rMWCDead = eq2mwc(Utility(arOutput, pci), pci);
3942 
3943     /* Get live cube cash points */
3944 
3945     GetPoints(arOutput, pci, arCP);
3946 
3947     getMEMultiple(pci->anScore[0], pci->anScore[1], pci->nMatchTo,
3948                   pci->nCube, -1, -1, pci->fCrawford, aafMET, aafMETPostCrawford, aarMETResult[0], aarMETResult[1]);
3949 
3950     rMWCOppCash = aarMETResult[pci->fMove][NDL];
3951 
3952     rOppTG = 1.0f - arCP[!pci->fMove];
3953 
3954     if (arOutput[OUTPUT_WIN] <= rOppTG) {
3955 
3956         /* Opponent is too good to double.
3957          *
3958          * MWC(live cube) linear interpolation between the
3959          * points:
3960          *
3961          * p = 0, MWC = opp win normal, gammon, backgammon
3962          * p = OppTG, MWC = opp cashes
3963          *
3964          */
3965 
3966         rMWCLose = (1.0f - rG1 - rBG1) * aarMETResult[pci->fMove][NDL]
3967             + rG1 * aarMETResult[pci->fMove][NDLG]
3968             + rBG1 * aarMETResult[pci->fMove][NDLB];
3969 
3970         if (rOppTG > 0.0f)
3971             /* avoid division by zero */
3972             rMWCLive = rMWCLose + (rMWCOppCash - rMWCLose) * arOutput[OUTPUT_WIN] / rOppTG;
3973         else
3974             rMWCLive = rMWCLose;
3975 
3976         /* (1-x) MWC(dead) + x MWC(live) */
3977 
3978         return rMWCDead * (1.0f - rCubeX) + rMWCLive * rCubeX;
3979 
3980     } else {
3981 
3982         /* MWC(live cube) linear interpolation between the
3983          * points:
3984          *
3985          * p = OppTG, MWC = opponent cashes
3986          * p = 1, MWC = I win (normal, gammon, or backgammon)
3987          *
3988          */
3989 
3990         rMWCWin = (1.0f - rG0 - rBG0) * aarMETResult[pci->fMove][NDW]
3991             + rG0 * aarMETResult[pci->fMove][NDWG]
3992             + rBG0 * aarMETResult[pci->fMove][NDWB];
3993 
3994         rMWCLive = rMWCOppCash + (rMWCWin - rMWCOppCash) * (arOutput[OUTPUT_WIN] - rOppTG) / (1.0f - rOppTG);
3995 
3996         /* (1-x) MWC(dead) + x MWC(live) */
3997 
3998         return rMWCDead * (1.0f - rCubeX) + rMWCLive * rCubeX;
3999 
4000     }
4001 
4002 }
4003 
4004 
4005 extern float
4006 Cl2CfMatch(float arOutput[NUM_OUTPUTS], cubeinfo * pci, float rCubeX)
4007 {
4008     /* Check if this requires a cubeful evaluation */
4009 
4010     if (!fDoCubeful(pci)) {
4011 
4012         /* cubeless eval */
4013 
4014         return eq2mwc(Utility(arOutput, pci), pci);
4015 
4016     } /* fDoCubeful */
4017     else {
4018 
4019         /* cubeful eval */
4020 
4021         if (pci->fCubeOwner == -1)
4022             return Cl2CfMatchCentered(arOutput, pci, rCubeX);
4023         else if (pci->fCubeOwner == pci->fMove)
4024             return Cl2CfMatchOwned(arOutput, pci, rCubeX);
4025         else
4026             return Cl2CfMatchUnavailable(arOutput, pci, rCubeX);
4027 
4028     }
4029 
4030 }
4031 
4032 
4033 
4034 extern float
4035 EvalEfficiency(const TanBoard anBoard, positionclass pc)
4036 {
4037     /* Since it's somewhat costly to call CalcInputs, the
4038      * inputs should preferably be cached to save time. */
4039 
4040     switch (pc) {
4041     case CLASS_OVER:
4042         return 0.0f;            /* dead cube */
4043 
4044     case CLASS_HYPERGAMMON1:
4045     case CLASS_HYPERGAMMON2:
4046     case CLASS_HYPERGAMMON3:
4047 
4048         /* FIXME */
4049 
4050         return 0.60f;
4051 
4052     case CLASS_BEAROFF1:
4053     case CLASS_BEAROFF_OS:
4054         /* FIXME: calculate based on #rolls to get off.
4055          * For example, 15 rolls probably have cube eff. of
4056          * 0.7, and 1.25 rolls have cube eff. of 1.0.
4057          *
4058          * It's not so important to have cube eff. correct here as an
4059          * n-ply evaluation will take care of last-roll and 2nd-last-roll
4060          * situations. */
4061 
4062         return rOSCubeX;
4063 
4064     case CLASS_RACE:
4065         {
4066             unsigned int anPips[2];
4067 
4068             float rEff;
4069 
4070             PipCount(anBoard, anPips);
4071 
4072             rEff = anPips[1] * rRaceFactorX + rRaceCoefficientX;
4073             if (rEff > rRaceMax)
4074                 return rRaceMax;
4075             else {
4076                 if (rEff < rRaceMin)
4077                     return rRaceMin;
4078                 else
4079                     return rEff;
4080             }
4081         }
4082 
4083     case CLASS_CONTACT:
4084 
4085         /* FIXME: should CLASS_CRASHED be handled differently? */
4086 
4087         /* FIXME: use Oystein's values published in rec.games.backgammon,
4088          * or work some other semiempirical values */
4089 
4090         /* FIXME: very important: use opponents inputs as well */
4091 
4092         return rContactX;
4093 
4094     case CLASS_CRASHED:
4095 
4096         return rCrashedX;
4097 
4098     case CLASS_BEAROFF2:
4099     case CLASS_BEAROFF_TS:
4100 
4101         return rTSCubeX;        /* for match play only */
4102 
4103     default:
4104         g_assert_not_reached();
4105 
4106     }
4107     return 0;
4108 
4109 }
4110 
4111 
4112 extern char *
4113 FormatEval(char *sz, evalsetup * pes)
4114 {
4115 
4116     switch (pes->et) {
4117     case EVAL_NONE:
4118         strcpy(sz, "");
4119         break;
4120     case EVAL_EVAL:
4121         sprintf(sz, "%s %1u-%s", pes->ec.fCubeful ? _("Cubeful") : _("Cubeless"), pes->ec.nPlies, _("ply"));
4122         break;
4123     case EVAL_ROLLOUT:
4124         sprintf(sz, "%s", _("Rollout"));
4125         break;
4126     default:
4127         sprintf(sz, "Unknown (%d)", (int) pes->et);
4128         break;
4129     }
4130 
4131     return sz;
4132 
4133 }
4134 
4135 #if 0
4136 static void
4137 CalcCubefulEquity(positionclass pc, float arOutput[NUM_ROLLOUT_OUTPUTS], int nPlies, int fDT, cubeinfo * pci)
4138 {
4139 
4140     float rND, rDT, rDP, r;
4141     int fCube;
4142     cubeinfo ci;
4143     float ar[NUM_ROLLOUT_OUTPUTS];
4144 
4145     int fMax = !(nPlies % 2);
4146 
4147     memcpy(&ar[0], &arOutput[0], NUM_OUTPUTS * sizeof(float));
4148 
4149     if (!nPlies) {
4150 
4151         /* leaf node */
4152 
4153         if (pc == CLASS_OVER || (pci->nMatchTo && !fDoCubeful(pci))) {
4154 
4155             /* cubeless */
4156 
4157             rND = Utility(arOutput, pci);
4158 
4159         } else {
4160 
4161             /* cubeful */
4162 
4163             rND = (pci->nMatchTo) ? mwc2eq(Cl2CfMatch(arOutput, pci), pci) : Cl2CfMoney(arOutput, pci);
4164 
4165         }
4166 
4167     } else {
4168 
4169         /* internal node; recurse */
4170 
4171         SetCubeInfo(&ci,
4172                     pci->nCube, pci->fCubeOwner,
4173                     !pci->fMove, pci->nMatchTo, pci->anScore, pci->fCrawford, pci->fJacoby, pci->fBeavers, pci->bgv);
4174 
4175         CalcCubefulEquity(pc, ar, nPlies - 1, TRUE, &ci);
4176 
4177         rND = ar[OUTPUT_CUBEFUL_EQUITY];
4178 
4179     }
4180 
4181     GetDPEq(&fCube, &rDP, pci);
4182 
4183     if (pci->nMatchTo)
4184         rDP = mwc2eq(rDP, pci);
4185 
4186     if (fCube && fDT) {
4187 
4188         /* double, take */
4189 
4190         if (!nPlies) {
4191 
4192             SetCubeInfo(&ci, 2 * pci->nCube, !pci->fMove, pci->fMove,
4193                         pci->nMatchTo, pci->anScore, pci->fCrawford, pci->fJacoby, pci->fBeavers, pci->bgv);
4194 
4195             /* leaf node */
4196 
4197             if (pc == CLASS_OVER || (pci->nMatchTo && !fDoCubeful(&ci))) {
4198 
4199                 /* cubeless */
4200 
4201                 rDT = Utility(arOutput, &ci);
4202                 if (pci->nMatchTo)
4203                     rDT *= 2.0;
4204 
4205             } else {
4206 
4207                 /* cubeful */
4208 
4209                 rDT = (pci->nMatchTo) ? mwc2eq(Cl2CfMatch(arOutput, &ci), &ci) : 2.0 * Cl2CfMoney(arOutput, &ci);
4210 
4211             }
4212 
4213         } else {
4214 
4215             /* internal node; recurse */
4216 
4217             SetCubeInfo(&ci, 2 * pci->nCube, !pci->fMove, !pci->fMove,
4218                         pci->nMatchTo, pci->anScore, pci->fCrawford, pci->fJacoby, pci->fBeavers, pci->bgv);
4219 
4220             CalcCubefulEquity(pc, ar, nPlies - 1, TRUE, &ci);
4221 
4222             rDT = ar[OUTPUT_CUBEFUL_EQUITY];
4223             if (!ci.nMatchTo)
4224                 rDT *= 2.0;
4225 
4226         }
4227 
4228         if (fMax) {
4229 
4230             /* maximize my equity */
4231 
4232             if (rDP > rND && rDT > rND) {
4233 
4234                 /* it's a double */
4235 
4236                 if (rDT >= rDP)
4237                     r = rDP;    /* pass */
4238                 else
4239                     r = rDT;    /* take */
4240 
4241             } else
4242                 r = rND;        /* no double */
4243 
4244         } else {
4245 
4246             /* minimize my equity */
4247 
4248             rDP = -rDP;
4249 
4250             if (rDP < rND && rDT < rND) {
4251 
4252                 /* it's a double */
4253 
4254                 if (rDT < rDP)
4255                     r = rDP;    /* pass */
4256                 else
4257                     r = rDT;    /* take */
4258 
4259             } else
4260                 r = rND;        /* no double */
4261 
4262         }
4263     } else {
4264         r = rND;
4265         rDT = 0.0;
4266     }
4267 
4268     arOutput[OUTPUT_EQUITY] = UtilityME(arOutput, pci);
4269     arOutput[OUTPUT_CUBEFUL_EQUITY] = r;
4270 
4271 }
4272 #endif                          /* !LOCKING_VERSION */
4273 
4274 
4275 /*
4276  * Compare two evalcontexts.
4277  *
4278  * Input:
4279  *    - pec1, pec2: the two evalcontexts to compare
4280  *
4281  * Output:
4282  *    None.
4283  *
4284  * Returns:
4285  *    -1 if  *pec1 "<" *pec2
4286  *     0 if  *pec1 "=" *pec2
4287  *    +1 if  *pec1 ">" *pec2
4288  *
4289  */
4290 
4291 extern int
4292 cmp_evalcontext(const evalcontext * pec1, const evalcontext * pec2)
4293 {
4294 
4295     /* Check if plies are different */
4296 
4297     if (pec1->nPlies < pec2->nPlies)
4298         return -1;
4299     else if (pec1->nPlies > pec2->nPlies)
4300         return +1;
4301 
4302     /* Check for cubeful evals */
4303 
4304     if (pec1->fCubeful < pec2->fCubeful)
4305         return -1;
4306     else if (pec1->fCubeful > pec2->fCubeful)
4307         return +1;
4308 
4309     /* Noise  */
4310 
4311     if (pec1->rNoise > pec2->rNoise)
4312         return -1;
4313     else if (pec1->rNoise < pec2->rNoise)
4314         return +1;
4315 
4316     if (pec1->rNoise > 0) {
4317 
4318         if (pec1->fDeterministic < pec2->fDeterministic)
4319             return -1;
4320         else if (pec1->fDeterministic > pec2->fDeterministic)
4321             return +1;
4322 
4323     }
4324 
4325     if (pec1->nPlies > 0) {
4326         int nPrune1 = (pec1->fUsePrune);
4327         int nPrune2 = (pec2->fUsePrune);
4328         if (nPrune1 > nPrune2)
4329             return -1;
4330         else if (nPrune1 < nPrune2)
4331             return +1;
4332     }
4333 
4334     return 0;
4335 
4336 }
4337 
4338 
4339 /*
4340  * Compare two rolloutcontexts.
4341  *
4342  * Input:
4343  *    - prc1, prc2: the two evalsetups to compare
4344  *
4345  * Output:
4346  *    None.
4347  *
4348  * Returns:
4349  *    -1 if  *prc1 "<" *prc2
4350  *     0 if  *prc1 "=" *prc2
4351  *    +1 if  *prc1 ">" *prc2
4352  *
4353  */
4354 
4355 static int
4356 cmp_rolloutcontext(const rolloutcontext * UNUSED(prc1), const rolloutcontext * UNUSED(prc2))
4357 {
4358 
4359     /* FIXME: write me */
4360 
4361     return 0;
4362 
4363 
4364 }
4365 
4366 
4367 /*
4368  * Compare two evalsetups.
4369  *
4370  * Input:
4371  *    - pes1, pes2: the two evalsetups to compare
4372  *
4373  * Output:
4374  *    None.
4375  *
4376  * Returns:
4377  *    -1 if  *pes1 "<" *pes2
4378  *     0 if  *pes1 "=" *pes2
4379  *    +1 if  *pes1 ">" *pes2
4380  *
4381  */
4382 
4383 extern int
4384 cmp_evalsetup(const evalsetup * pes1, const evalsetup * pes2)
4385 {
4386 
4387     /* Check for different evaltypes */
4388 
4389     if (pes1->et < pes2->et)
4390         return -1;
4391     else if (pes1->et > pes2->et)
4392         return +1;
4393 
4394     /* The two evaltypes are identical */
4395 
4396     switch (pes1->et) {
4397     case EVAL_NONE:
4398         return 0;
4399 
4400     case EVAL_EVAL:
4401         return cmp_evalcontext(&pes1->ec, &pes2->ec);
4402 
4403     case EVAL_ROLLOUT:
4404         return cmp_rolloutcontext(&pes1->rc, &pes2->rc);
4405 
4406     default:
4407         g_assert_not_reached();
4408     }
4409 
4410     return 0;
4411 }
4412 
4413 
4414 static void
4415 calculate_gammon_rates(float aarRates[2][2], float arOutput[], cubeinfo * pci)
4416 {
4417 
4418     if (arOutput[OUTPUT_WIN] > 0.0f) {
4419         aarRates[pci->fMove][0] = (arOutput[OUTPUT_WINGAMMON] - arOutput[OUTPUT_WINBACKGAMMON]) / arOutput[OUTPUT_WIN];
4420         aarRates[pci->fMove][1] = arOutput[OUTPUT_WINBACKGAMMON] / arOutput[OUTPUT_WIN];
4421     } else {
4422         aarRates[pci->fMove][0] = aarRates[pci->fMove][1] = 0.0f;
4423     }
4424 
4425     if (arOutput[OUTPUT_WIN] < 1.0f) {
4426         aarRates[!pci->fMove][0] =
4427             (arOutput[OUTPUT_LOSEGAMMON] - arOutput[OUTPUT_LOSEBACKGAMMON]) / (1.0f - arOutput[OUTPUT_WIN]);
4428         aarRates[!pci->fMove][1] = arOutput[OUTPUT_LOSEBACKGAMMON] / (1.0f - arOutput[OUTPUT_WIN]);
4429     } else {
4430         aarRates[!pci->fMove][0] = aarRates[!pci->fMove][1] = 0.0f;
4431     }
4432 
4433 }
4434 
4435 /*
4436  * Get current gammon rates
4437  *
4438  * Input:
4439  *   anBoard: current board
4440  *   pci: current cubeinfo
4441  *   pec: eval context
4442  *
4443  * Output:
4444  *   aarRates: gammon and backgammon rates (first index is player)
4445  *
4446  */
4447 
4448 extern int
4449 getCurrentGammonRates(float aarRates[2][2],
4450                       float arOutput[], const TanBoard anBoard, cubeinfo * pci, const evalcontext * pec)
4451 {
4452 
4453     if (EvaluatePosition(NULL, anBoard, arOutput, pci, pec) < 0)
4454         return -1;
4455 
4456     calculate_gammon_rates(aarRates, arOutput, pci);
4457 
4458     return 0;
4459 
4460 }
4461 
4462 /*
4463  * Get take, double, beaver, etc points for money game using
4464  * Rick Janowski's formulae:
4465  *   http://www.bkgm.com/articles/Janowski/cubeformulae.pdf
4466  *
4467  * Input:
4468  *   fJacoby, fBeavers: flags for different flavours of money game
4469  *   aarRates: gammon and backgammon rates (first index is player)
4470  *
4471  * Output:
4472  *   aaarPoints: the points
4473  *
4474  */
4475 
4476 extern void
4477 getMoneyPoints(float aaarPoints[2][7][2], const int fJacoby, const int fBeavers, float aarRates[2][2])
4478 {
4479 
4480     float arCLV[2];             /* average cubeless value of games won */
4481     float rW, rL;
4482     int i;
4483 
4484     /* calculate average cubeless value of games won */
4485 
4486     for (i = 0; i < 2; i++)
4487         arCLV[i] = 1.0f + aarRates[i][0] + 2.0f * aarRates[i][1];
4488 
4489     /* calculate points */
4490 
4491     for (i = 0; i < 2; i++) {
4492 
4493         /* Determine rW and rL from Rick's formulae */
4494 
4495         rW = arCLV[i];
4496         rL = arCLV[!i];
4497 
4498         /* Determine points */
4499 
4500         /* take point */
4501 
4502         aaarPoints[i][0][0] = (rL - 0.5f) / (rW + rL);
4503         aaarPoints[i][0][1] = (rL - 0.5f) / (rW + rL + 0.5f);
4504 
4505         /* beaver point */
4506 
4507         aaarPoints[i][1][0] = rL / (rW + rL);
4508         aaarPoints[i][1][1] = rL / (rW + rL + 0.5f);
4509 
4510         /* raccoon point */
4511 
4512         aaarPoints[i][2][0] = rL / (rW + rL);
4513         aaarPoints[i][2][1] = (rL + 0.5f) / (rW + rL + 0.5f);
4514 
4515         /* initial double point */
4516 
4517         if (!fJacoby) {
4518             /* without Jacoby */
4519             aaarPoints[i][3][0] = rL / (rW + rL);
4520         } else {
4521             /* with Jacoby */
4522 
4523             if (fBeavers)
4524                 /* with beavers */
4525                 aaarPoints[i][3][0] = (rL - 0.25f) / (rL + rW - 0.5f);
4526             else
4527                 /* without beavers */
4528                 aaarPoints[i][3][0] = (rL - 0.5f) / (rL + rW - 1.0f);
4529 
4530         }
4531         aaarPoints[i][3][1] = (rL + 1.0f) / (rL + rW + 0.5f);
4532 
4533         /* redouble point */
4534         aaarPoints[i][4][0] = rL / (rW + rL);
4535         aaarPoints[i][4][1] = (rL + 1.0f) / (rL + rW + 0.5f);
4536 
4537         /* cash point */
4538 
4539         aaarPoints[i][5][0] = (rL + 0.5f) / (rW + rL);
4540         aaarPoints[i][5][1] = (rL + 1.0f) / (rW + rL + 0.5f);
4541 
4542         /* too good point */
4543 
4544         aaarPoints[i][6][0] = (rL + 1.0f) / (rW + rL);
4545         aaarPoints[i][6][1] = (rL + 1.0f) / (rW + rL + 0.5f);
4546 
4547     }
4548 
4549 }
4550 
4551 extern void
4552 GetECF3(float arCubeful[], int cci, float arCf[], cubeinfo aci[])
4553 {
4554 
4555     int i, ici;
4556     float rND, rDT, rDP;
4557 
4558     for (ici = 0, i = 0; ici < cci; ici++, i += 2) {
4559 
4560         if (aci[i + 1].nCube > 0) {
4561 
4562             /* cube available */
4563 
4564             rND = arCf[i];
4565 
4566             if (aci[0].nMatchTo)
4567                 rDT = arCf[i + 1];
4568             else
4569                 rDT = 2.0f * arCf[i + 1];
4570 
4571             GetDPEq(NULL, &rDP, &aci[i]);
4572 
4573             if (rDT >= rND && rDP >= rND) {
4574 
4575                 /* double */
4576 
4577                 if (rDT >= rDP)
4578                     /* pass */
4579                     arCubeful[ici] = rDP;
4580                 else
4581                     /* take */
4582                     arCubeful[ici] = rDT;
4583 
4584             } else {
4585 
4586                 /* no double */
4587 
4588                 arCubeful[ici] = rND;
4589 
4590             }
4591 
4592 
4593         } else {
4594 
4595             /* no cube available: always no double */
4596 
4597             arCubeful[ici] = arCf[i];
4598 
4599         }
4600 
4601     }
4602 
4603 }
4604 
4605 
4606 extern void
4607 MakeCubePos(const cubeinfo aciCubePos[], const int cci, const int fTop, cubeinfo aci[], const int fInvert)
4608 {
4609     int i, ici;
4610 
4611     for (ici = 0, i = 0; ici < cci; ici++) {
4612 
4613         /* no double */
4614 
4615         if (aciCubePos[ici].nCube > 0) {
4616 
4617             SetCubeInfo(&aci[i],
4618                         aciCubePos[ici].nCube,
4619                         aciCubePos[ici].fCubeOwner,
4620                         fInvert ?
4621                         !aciCubePos[ici].fMove : aciCubePos[ici].fMove,
4622                         aciCubePos[ici].nMatchTo,
4623                         aciCubePos[ici].anScore,
4624                         aciCubePos[ici].fCrawford,
4625                         aciCubePos[ici].fJacoby, aciCubePos[ici].fBeavers, aciCubePos[ici].bgv);
4626 
4627         } else {
4628 
4629             aci[i].nCube = -1;
4630 
4631         }
4632 
4633         i++;
4634 
4635         if (!fTop && aciCubePos[ici].nCube > 0 && GetDPEq(NULL, NULL, &aciCubePos[ici]))
4636             /* we may double */
4637             SetCubeInfo(&aci[i],
4638                         2 * aciCubePos[ici].nCube,
4639                         !aciCubePos[ici].fMove,
4640                         fInvert ?
4641                         !aciCubePos[ici].fMove : aciCubePos[ici].fMove,
4642                         aciCubePos[ici].nMatchTo,
4643                         aciCubePos[ici].anScore,
4644                         aciCubePos[ici].fCrawford,
4645                         aciCubePos[ici].fJacoby, aciCubePos[ici].fBeavers, aciCubePos[ici].bgv);
4646         else
4647             /* mark cube position as unavailable */
4648             aci[i].nCube = -1;
4649 
4650         i++;
4651 
4652 
4653     }                           /* loop cci */
4654 
4655 }
4656 
4657 
4658 /*
4659  * Get take, double, take, and too good points for match play.
4660  *
4661  * Input:
4662  *   pci: cubeinfo
4663  *   aarRates: gammon and backgammon rates (first index is player)
4664  *
4665  * Output:
4666  *   aaarPoints: the points
4667  *
4668  */
4669 
4670 extern void
4671 getMatchPoints(float aaarPoints[2][4][2],
4672                int afAutoRedouble[2], int afDead[2], const cubeinfo * pci, float aarRates[2][2])
4673 {
4674 
4675     SSE_ALIGN(float arOutput[NUM_OUTPUTS]);
4676     float arDP1[2], arDP2[2], arCP1[2], arCP2[2], arTG[2];
4677     float rDTW, rDTL, rNDW, rNDL, rDP, rRisk, rGain;
4678 
4679     int i, anNormScore[2];
4680 
4681     for (i = 0; i < 2; i++)
4682         anNormScore[i] = pci->nMatchTo - pci->anScore[i];
4683 
4684     /* get cash points */
4685 
4686     arOutput[OUTPUT_WIN] = 0.5f;
4687     arOutput[OUTPUT_WINGAMMON] = 0.5f * (aarRates[0][0] + aarRates[0][1]);
4688     arOutput[OUTPUT_WINBACKGAMMON] = 0.5f * aarRates[0][1];
4689     arOutput[OUTPUT_LOSEGAMMON] = 0.5f * (aarRates[1][0] + aarRates[1][1]);
4690     arOutput[OUTPUT_LOSEBACKGAMMON] = 0.5f * aarRates[1][1];
4691 
4692     GetPoints(arOutput, pci, arCP2);
4693 
4694     for (i = 0; i < 2; i++) {
4695 
4696         afAutoRedouble[i] = (anNormScore[i] - 2 * pci->nCube <= 0) && (anNormScore[!i] - 2 * pci->nCube > 0);
4697 
4698         afDead[i] = (anNormScore[!i] - 2 * pci->nCube <= 0);
4699 
4700         /* MWC for "double, take; win" */
4701 
4702         rDTW =
4703             (1.0f - aarRates[i][0] - aarRates[i][1]) *
4704             getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4705                   2 * pci->nCube, i, pci->fCrawford, aafMET, aafMETPostCrawford)
4706             + aarRates[i][0] *
4707             getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4708                   4 * pci->nCube, i, pci->fCrawford, aafMET, aafMETPostCrawford)
4709             + aarRates[i][1] *
4710             getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4711                   6 * pci->nCube, i, pci->fCrawford, aafMET, aafMETPostCrawford);
4712 
4713         /* MWC for "no double, take; win" */
4714 
4715         rNDW =
4716             (1.0f - aarRates[i][0] - aarRates[i][1]) *
4717             getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4718                   pci->nCube, i, pci->fCrawford, aafMET, aafMETPostCrawford)
4719             + aarRates[i][0] *
4720             getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4721                   2 * pci->nCube, i, pci->fCrawford, aafMET, aafMETPostCrawford)
4722             + aarRates[i][1] *
4723             getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4724                   3 * pci->nCube, i, pci->fCrawford, aafMET, aafMETPostCrawford);
4725 
4726         /* MWC for "Double, take; lose" */
4727 
4728         rDTL =
4729             (1.0f - aarRates[!i][0] - aarRates[!i][1]) *
4730             getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4731                   2 * pci->nCube, !i, pci->fCrawford, aafMET, aafMETPostCrawford)
4732             + aarRates[!i][0] *
4733             getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4734                   4 * pci->nCube, !i, pci->fCrawford, aafMET, aafMETPostCrawford)
4735             + aarRates[!i][1] *
4736             getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4737                   6 * pci->nCube, !i, pci->fCrawford, aafMET, aafMETPostCrawford);
4738 
4739         /* MWC for "No double; lose" */
4740 
4741         rNDL =
4742             (1.0f - aarRates[!i][0] - aarRates[!i][1]) *
4743             getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4744                   1 * pci->nCube, !i, pci->fCrawford, aafMET, aafMETPostCrawford)
4745             + aarRates[!i][0] *
4746             getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4747                   2 * pci->nCube, !i, pci->fCrawford, aafMET, aafMETPostCrawford)
4748             + aarRates[!i][1] *
4749             getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4750                   3 * pci->nCube, !i, pci->fCrawford, aafMET, aafMETPostCrawford);
4751 
4752         /* MWC for "Double, pass" */
4753 
4754         rDP =
4755             getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4756                   pci->nCube, i, pci->fCrawford, aafMET, aafMETPostCrawford);
4757 
4758         /* Double point */
4759 
4760         rRisk = rNDL - rDTL;
4761         rGain = rDTW - rNDW;
4762 
4763         arDP1[i] = rRisk / (rRisk + rGain);
4764         arDP2[i] = arDP1[i];
4765 
4766         /* Dead cube take point without redouble */
4767 
4768         rRisk = rDTW - rDP;
4769         rGain = rDP - rDTL;
4770 
4771         arCP1[i] = 1.0f - rRisk / (rRisk + rGain);
4772 
4773         /* find too good point */
4774 
4775         rRisk = rNDW - rNDL;
4776         rGain = rNDW - rDP;
4777 
4778         arTG[i] = rRisk / (rRisk + rGain);
4779 
4780         if (afAutoRedouble[i]) {
4781 
4782             /* With redouble */
4783 
4784             rDTW =
4785                 (1.0f - aarRates[i][0] - aarRates[i][1]) *
4786                 getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4787                       4 * pci->nCube, i, pci->fCrawford, aafMET, aafMETPostCrawford)
4788                 + aarRates[i][0] *
4789                 getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4790                       8 * pci->nCube, i, pci->fCrawford, aafMET, aafMETPostCrawford)
4791                 + aarRates[i][1] *
4792                 getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4793                       12 * pci->nCube, i, pci->fCrawford, aafMET, aafMETPostCrawford);
4794 
4795             rDTL =
4796                 (1.0f - aarRates[!i][0] - aarRates[!i][1]) *
4797                 getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4798                       4 * pci->nCube, !i, pci->fCrawford, aafMET, aafMETPostCrawford)
4799                 + aarRates[!i][0] *
4800                 getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4801                       8 * pci->nCube, !i, pci->fCrawford, aafMET, aafMETPostCrawford)
4802                 + aarRates[!i][1] *
4803                 getME(pci->anScore[0], pci->anScore[1], pci->nMatchTo, i,
4804                       12 * pci->nCube, !i, pci->fCrawford, aafMET, aafMETPostCrawford);
4805 
4806             rRisk = rDTW - rDP;
4807             rGain = rDP - rDTL;
4808 
4809             arCP2[i] = 1.0f - rRisk / (rRisk + rGain);
4810 
4811             /* Double point */
4812 
4813             rRisk = rNDL - rDTL;
4814             rGain = rDTW - rNDW;
4815 
4816             arDP2[i] = rRisk / (rRisk + rGain);
4817 
4818         }
4819 
4820     }
4821 
4822     /* save points */
4823 
4824     for (i = 0; i < 2; i++) {
4825 
4826         /* take point */
4827 
4828         aaarPoints[i][0][0] = 1.0f - arCP1[!i];
4829         aaarPoints[i][0][1] = 1.0f - arCP2[!i];
4830 
4831         /* double point */
4832 
4833         aaarPoints[i][1][0] = arDP1[i];
4834         aaarPoints[i][1][1] = arDP2[i];
4835 
4836         /* cash point */
4837 
4838         aaarPoints[i][2][0] = arCP1[i];
4839         aaarPoints[i][2][1] = arCP2[i];
4840 
4841         /* too good point */
4842 
4843         aaarPoints[i][3][0] = arTG[i];
4844 
4845         if (!afDead[i])
4846             aaarPoints[i][3][1] = arCP2[i];
4847 
4848     }
4849 
4850 }
4851 
4852 extern void
4853 getCubeDecisionOrdering(int aiOrder[3],
4854                         float arDouble[4], float aarOutput[2][NUM_ROLLOUT_OUTPUTS], const cubeinfo * pci)
4855 {
4856 
4857     cubedecision cd;
4858 
4859     /* Get cube decision */
4860 
4861     cd = FindBestCubeDecision(arDouble, aarOutput, pci);
4862 
4863     switch (cd) {
4864 
4865     case DOUBLE_TAKE:
4866     case DOUBLE_BEAVER:
4867     case REDOUBLE_TAKE:
4868 
4869         /*
4870          * Optimal     : Double, take
4871          * Best for me : Double, pass
4872          * Worst for me: No Double
4873          */
4874 
4875         aiOrder[0] = OUTPUT_TAKE;
4876         aiOrder[1] = OUTPUT_DROP;
4877         aiOrder[2] = OUTPUT_NODOUBLE;
4878 
4879         break;
4880 
4881     case DOUBLE_PASS:
4882     case REDOUBLE_PASS:
4883 
4884         /*
4885          * Optimal     : Double, pass
4886          * Best for me : Double, take
4887          * Worst for me: no double
4888          */
4889         aiOrder[0] = OUTPUT_DROP;
4890         aiOrder[1] = OUTPUT_TAKE;
4891         aiOrder[2] = OUTPUT_NODOUBLE;
4892 
4893         break;
4894 
4895     case NODOUBLE_TAKE:
4896     case NODOUBLE_BEAVER:
4897     case TOOGOOD_TAKE:
4898     case NO_REDOUBLE_TAKE:
4899     case NO_REDOUBLE_BEAVER:
4900     case TOOGOODRE_TAKE:
4901     case NODOUBLE_DEADCUBE:
4902     case NO_REDOUBLE_DEADCUBE:
4903     case OPTIONAL_DOUBLE_BEAVER:
4904     case OPTIONAL_DOUBLE_TAKE:
4905     case OPTIONAL_REDOUBLE_TAKE:
4906 
4907         /*
4908          * Optimal     : no double
4909          * Best for me : double, pass
4910          * Worst for me: double, take
4911          */
4912 
4913         aiOrder[0] = OUTPUT_NODOUBLE;
4914         aiOrder[1] = OUTPUT_DROP;
4915         aiOrder[2] = OUTPUT_TAKE;
4916 
4917         break;
4918 
4919     case TOOGOOD_PASS:
4920     case TOOGOODRE_PASS:
4921     case OPTIONAL_DOUBLE_PASS:
4922     case OPTIONAL_REDOUBLE_PASS:
4923 
4924         /*
4925          * Optimal     : no double
4926          * Best for me : double, take
4927          * Worst for me: double, pass
4928          */
4929 
4930         aiOrder[0] = OUTPUT_NODOUBLE;
4931         aiOrder[1] = OUTPUT_TAKE;
4932         aiOrder[2] = OUTPUT_DROP;
4933 
4934         break;
4935 
4936     default:
4937 
4938         g_assert_not_reached();
4939 
4940     }
4941 
4942 }
4943 
4944 
4945 
4946 extern float
4947 getPercent(const cubedecision cd, const float arDouble[])
4948 {
4949 
4950     switch (cd) {
4951 
4952     case DOUBLE_TAKE:
4953     case DOUBLE_BEAVER:
4954     case DOUBLE_PASS:
4955     case REDOUBLE_TAKE:
4956     case REDOUBLE_PASS:
4957     case NODOUBLE_DEADCUBE:
4958     case NO_REDOUBLE_DEADCUBE:
4959     case OPTIONAL_DOUBLE_BEAVER:
4960     case OPTIONAL_DOUBLE_TAKE:
4961     case OPTIONAL_REDOUBLE_TAKE:
4962     case OPTIONAL_DOUBLE_PASS:
4963     case OPTIONAL_REDOUBLE_PASS:
4964         /* correct cube action */
4965         return -1.0f;
4966 
4967     case TOOGOODRE_TAKE:
4968     case TOOGOOD_TAKE:
4969         /* never correct to double */
4970         return -1.0f;
4971 
4972     case NODOUBLE_TAKE:
4973     case NODOUBLE_BEAVER:
4974     case NO_REDOUBLE_TAKE:
4975     case NO_REDOUBLE_BEAVER:
4976 
4977         /* how many doubles should be dropped before it is correct to double */
4978 
4979         return (arDouble[OUTPUT_NODOUBLE] - arDouble[OUTPUT_TAKE]) / (arDouble[OUTPUT_DROP] - arDouble[OUTPUT_TAKE]);
4980 
4981     case TOOGOOD_PASS:
4982     case TOOGOODRE_PASS:
4983 
4984         /* how many doubles should be taken before it is correct to double */
4985         if (arDouble[OUTPUT_NODOUBLE] > arDouble[OUTPUT_TAKE])
4986             /* strange match play scenario
4987              * (see 3-ply eval on cAmgACAAGAAA/4HPkAUgzW8EBMA):
4988              * never correct to double! */
4989             return -1.0f;
4990         else
4991             return
4992                 (arDouble[OUTPUT_NODOUBLE] - arDouble[OUTPUT_DROP]) / (arDouble[OUTPUT_TAKE] - arDouble[OUTPUT_DROP]);
4993 
4994     default:
4995 
4996         g_assert_not_reached();
4997 
4998     }
4999 
5000     return -1.0f;
5001 }
5002 
5003 
5004 /*
5005  * Resort movelist and recalculate best score.
5006  *
5007  * Input:
5008  *   pml: movelist
5009  *
5010  * Output:
5011  *   pml: update movelist
5012  *   ai : the new ordering. Caller must allocate ai.
5013  *
5014  * FIXME: the construction of the ai-array is *very* ugly.
5015  *        We should probably write a substitute for qsort that
5016  *        updates ai on the fly.
5017  *
5018  */
5019 
5020 extern void
5021 RefreshMoveList(movelist * pml, int *ai)
5022 {
5023 
5024     unsigned int i, j;
5025     movelist ml;
5026 
5027     if (!pml->cMoves)
5028         return;
5029 
5030     if (ai)
5031         CopyMoveList(&ml, pml);
5032 
5033     qsort(pml->amMoves, pml->cMoves, sizeof(move), (cfunc) CompareMovesGeneral);
5034 
5035     pml->rBestScore = pml->amMoves[0].rScore;
5036 
5037     if (ai) {
5038         for (i = 0; i < pml->cMoves; i++) {
5039 
5040             for (j = 0; j < pml->cMoves; j++) {
5041 
5042                 if (!memcmp(ml.amMoves[j].anMove, pml->amMoves[i].anMove, 8 * sizeof(int)))
5043                     ai[j] = i;
5044 
5045             }
5046         }
5047 
5048         free(ml.amMoves);
5049 
5050     }
5051 
5052 
5053 }
5054 
5055 
5056 extern void
5057 CopyMoveList(movelist * pmlDest, const movelist * pmlSrc)
5058 {
5059 
5060     if (pmlDest == pmlSrc)
5061         return;
5062 
5063     pmlDest->cMoves = pmlSrc->cMoves;
5064     pmlDest->cMaxMoves = pmlSrc->cMaxMoves;
5065     pmlDest->cMaxPips = pmlSrc->cMaxPips;
5066     pmlDest->iMoveBest = pmlSrc->iMoveBest;
5067     pmlDest->rBestScore = pmlSrc->rBestScore;
5068 
5069     if (pmlSrc->cMoves) {
5070         pmlDest->amMoves = (move *) malloc(pmlSrc->cMoves * sizeof(move));
5071         memcpy(pmlDest->amMoves, pmlSrc->amMoves, pmlSrc->cMoves * sizeof(move));
5072     } else
5073         pmlDest->amMoves = NULL;
5074 
5075 }
5076 
5077 
5078 
5079 /*
5080  * is this a close cubedecision?
5081  *
5082  * Input:
5083  *   arDouble: equities for cube decisions
5084  *
5085  */
5086 
5087 extern int
5088 isCloseCubedecision(const float arDouble[])
5089 {
5090     const float rThr = 0.16f;
5091     float rDouble;
5092     rDouble = MIN(arDouble[OUTPUT_TAKE], 1.0f);
5093 
5094     /* Report if doubling is less than very bad (0.16) */
5095     if (arDouble[OUTPUT_OPTIMAL] - rDouble < rThr)
5096         return 1;
5097 
5098     return 0;
5099 
5100 }
5101 
5102 
5103 /*
5104  * is this a missed double?
5105  *
5106  * Input:
5107  *   arDouble: equities for cube decisions
5108  *   fDouble: did the player double
5109  *   pci: cubeinfo
5110  *
5111  */
5112 
5113 extern int
5114 isMissedDouble(float arDouble[], float aarOutput[2][NUM_ROLLOUT_OUTPUTS], const int fDouble, const cubeinfo * pci)
5115 {
5116 
5117     cubedecision cd = FindBestCubeDecision(arDouble, aarOutput, pci);
5118 
5119     switch (cd) {
5120 
5121     case DOUBLE_TAKE:
5122     case DOUBLE_PASS:
5123     case DOUBLE_BEAVER:
5124     case REDOUBLE_TAKE:
5125     case REDOUBLE_PASS:
5126 
5127         return !fDouble;
5128 
5129     default:
5130 
5131         return 0;
5132 
5133     }
5134 
5135 }
5136 
5137 
5138 static int
5139 MoveKey(const TanBoard anBoard, const int anMove[8], positionkey * pkey)
5140 {
5141     TanBoard anBoardMove;
5142 
5143     memcpy(anBoardMove, anBoard, sizeof(anBoardMove));
5144     ApplyMove(anBoardMove, anMove, FALSE);
5145     PositionKey((ConstTanBoard) anBoardMove, pkey);
5146 
5147     return 0;
5148 }
5149 
5150 
5151 extern unsigned int
5152 locateMove(const TanBoard anBoard, const int anMove[8], const movelist * pml)
5153 {
5154 
5155     unsigned int i;
5156     positionkey key1, key2;
5157 
5158     MoveKey(anBoard, anMove, &key1);
5159 
5160     for (i = 0; i < pml->cMoves; ++i) {
5161 
5162         MoveKey(anBoard, pml->amMoves[i].anMove, &key2);
5163 
5164         if (EqualKeys(key2, key1))
5165             return i;
5166 
5167 
5168     }
5169 
5170     return 0;
5171 
5172 }
5173 
5174 
5175 extern int
5176 equal_movefilter(const int i, const movefilter amf1[MAX_FILTER_PLIES], const movefilter amf2[MAX_FILTER_PLIES])
5177 {
5178 
5179     int j;
5180 
5181     for (j = 0; j <= i; ++j) {
5182         if (amf1[j].Accept != amf2[j].Accept)
5183             return 0;
5184         if (amf1[j].Accept < 0)
5185             continue;
5186         if (amf1[j].Extra != amf2[j].Extra)
5187             return 0;
5188         if (!amf1[j].Extra)
5189             continue;
5190         if (amf1[j].Threshold != amf2[j].Threshold)
5191             return 0;
5192 
5193     }
5194 
5195     return 1;
5196 
5197 }
5198 
5199 
5200 extern int
5201 equal_movefilters(movefilter aamf1[MAX_FILTER_PLIES][MAX_FILTER_PLIES],
5202                   movefilter aamf2[MAX_FILTER_PLIES][MAX_FILTER_PLIES])
5203 {
5204 
5205     int i;
5206 
5207     for (i = 0; i < MAX_FILTER_PLIES; ++i)
5208         if (!equal_movefilter(i, aamf1[i], aamf2[i]))
5209             return 0;
5210 
5211     return 1;
5212 
5213 }
5214 
5215 
5216 /*
5217  * Categorise double into normal, beaver, or raccoon.
5218  *
5219  * The function is called before ApplyMoveRecord:
5220  *
5221  * fDoubled = FALSE:
5222  *
5223  *    the previous moverecord was not a MOVE_DOUBLE,
5224  *    hence this is a normal double.
5225  *
5226  * fDoubled = TRUE:
5227  *
5228  *    The previous moverecord was a MOVE_DOUBLE
5229  *    so it's either a beaver or a raccoon
5230  *
5231  *    Beaver: fTurn != fMove (the previous doubler was the player on roll
5232  *                            so the redouble must be a beaver)
5233  *    Raccoon: fTurn == fMove and/or fCubeOwner != fMove
5234  *
5235  *
5236  */
5237 
5238 extern doubletype
5239 DoubleType(const int fDoubled, const int fMove, const int fTurn)
5240 {
5241 
5242     if (fDoubled) {
5243 
5244         /* beaver or raccoon */
5245 
5246         if (fTurn != fMove)
5247             /* beaver */
5248             return DT_BEAVER;
5249         else
5250             /* raccoon */
5251             return DT_RACCOON;
5252 
5253     }
5254     return DT_NORMAL;
5255 }
5256 
5257 #else
5258 
5259 #define FindnSaveBestMoves FindnSaveBestMovesWithLocking
5260 #define FindBestMove FindBestMoveWithLocking
5261 #define EvaluatePosition EvaluatePositionWithLocking
5262 #define ScoreMove ScoreMoveWithLocking
5263 #define GeneralCubeDecisionE GeneralCubeDecisionEWithLocking
5264 #define GeneralEvaluationE GeneralEvaluationEWithLocking
5265 #define EvaluatePositionCache EvaluatePositionCacheWithLocking
5266 #define FindBestMovePlied FindBestMovePliedWithLocking
5267 #define GeneralEvaluationEPlied GeneralEvaluationEPliedWithLocking
5268 #define EvaluatePositionCubeful3 EvaluatePositionCubeful3WithLocking
5269 #define ScoreMoves ScoreMovesWithLocking
5270 #define ScoreMovesPruned ScoreMovesPrunedWithLocking
5271 #define FindBestMoveInEval FindBestMoveInEvalWithLocking
5272 #define GeneralEvaluationEPliedCubeful GeneralEvaluationEPliedCubefulWithLocking
5273 #define EvaluatePositionCubeful4 EvaluatePositionCubeful4WithLocking
5274 #define CacheAdd CacheAddWithLocking
5275 #define CacheLookup CacheLookupWithLocking
5276 
5277 static int EvaluatePositionCache(NNState * nnStates, const TanBoard anBoard, float arOutput[],
5278                                  cubeinfo * const pci, const evalcontext * pecx, int nPlies, positionclass pc);
5279 
5280 static int FindBestMovePlied(int anMove[8], int nDice0, int nDice1,
5281                              TanBoard anBoard, const cubeinfo * pci,
5282                              const evalcontext * pec, int nPlies, movefilter aamf[MAX_FILTER_PLIES][MAX_FILTER_PLIES]);
5283 
5284 #endif
5285 
5286 static int GeneralEvaluationEPlied(NNState * nnStates, float arOutput[NUM_ROLLOUT_OUTPUTS],
5287                                    const TanBoard anBoard, cubeinfo * const pci, const evalcontext * pec, int nPlies);
5288 static int EvaluatePositionCubeful3(NNState * nnStates, const TanBoard anBoard, float arOutput[NUM_OUTPUTS],
5289                                     float arCubeful[], const cubeinfo aciCubePos[], int cci, cubeinfo * const pciMove,
5290                                     const evalcontext * pec, int nPlies, int fTop);
5291 
5292 /* Functions that have both locking and non-locking versions below here */
5293 
5294 static int ScoreMoves(movelist * pml, const cubeinfo * pci, const evalcontext * pec, int nPlies);
5295 static int ScoreMovesPruned(movelist * pml, const cubeinfo * pci, const evalcontext * pec, unsigned int *bmovesi, unsigned int prune_moves);
5296 /*
5297    The pruning nets select the best MIN_PRUNE_MOVES +
5298    floor(log2(number of legal moves)) moves instead of 10 as they used
5299    to do.  A value of 5 for MIN_PRUNE_MOVES brings a small speed-up
5300    and, according to the Depreli benchmark, an insignificant strength
5301    improvement.  Using a lower value causes a measurable degradation
5302    of play. Using a higher one doesn't significantly improve it.
5303 */
5304 #define MIN_PRUNE_MOVES 5
5305 #define MAX_PRUNE_MOVES (MIN_PRUNE_MOVES + 11)
5306 
5307 static SIMD_AVX_STACKALIGN void
5308 FindBestMoveInEval(NNState * nnStates, int const nDice0, int const nDice1, const TanBoard anBoardIn,
5309                    TanBoard anBoardOut, cubeinfo * const pci, const evalcontext * pec)
5310 {
5311     unsigned int i;
5312     movelist ml;
5313     positionclass evalClass = CLASS_OVER;
5314     unsigned int bmovesi[MAX_PRUNE_MOVES];
5315     unsigned int prune_moves;
5316 
5317     GenerateMoves(&ml, anBoardIn, nDice0, nDice1, FALSE);
5318 
5319     if (ml.cMoves == 0) {
5320         /* no legal moves */
5321         return;
5322     }
5323 
5324     if (ml.cMoves == 1) {
5325         /* forced move */
5326         ml.iMoveBest = 0;
5327         PositionFromKey(anBoardOut, &ml.amMoves[ml.iMoveBest].key);
5328         return;
5329     }
5330 
5331     /* LogCube() is floor(log2()) */
5332     prune_moves = MIN_PRUNE_MOVES + LogCube(ml.cMoves);
5333 
5334     if (ml.cMoves <= prune_moves) {
5335         ScoreMoves(&ml, pci, pec, 0);
5336         PositionFromKey(anBoardOut, &ml.amMoves[ml.iMoveBest].key);
5337         return;
5338     }
5339 
5340     pci->fMove = !pci->fMove;
5341 
5342     for (i = 0; i < ml.cMoves; i++) {
5343         positionclass pc;
5344         SSE_ALIGN(float arInput[NUM_PRUNING_INPUTS]);
5345         SSE_ALIGN(float arOutput[NUM_OUTPUTS]);
5346         evalcache ec;
5347         uint32_t l;
5348         /* declared volatile to avoid wrong compiler optimization
5349          * on some gcc systems. Remove with great care. */
5350         move *const volatile pm = &ml.amMoves[i];
5351 
5352         PositionFromKeySwapped(anBoardOut, &pm->key);
5353 
5354         pc = ClassifyPosition((ConstTanBoard) anBoardOut, VARIATION_STANDARD);
5355         if (i == 0) {
5356             if (pc < CLASS_RACE)
5357                 break;
5358             evalClass = pc;
5359         } else if (pc != evalClass)
5360             break;
5361 
5362         CopyKey(pm->key, ec.key);
5363         ec.nEvalContext = 0;
5364         if ((l = CacheLookup(&cpEval, &ec, arOutput, NULL)) != CACHEHIT) {
5365             baseInputs((ConstTanBoard) anBoardOut, arInput);
5366             {
5367                 neuralnet *nets[] = { &nnpRace, &nnpCrashed, &nnpContact };
5368                 neuralnet *n = nets[pc - CLASS_RACE];
5369 #if defined(USE_SIMD_INSTRUCTIONS)
5370                 (void) nnStates;        /* silence compiler warning */
5371                 NeuralNetEvaluateSSE(n, arInput, arOutput, NULL);
5372 #else
5373                 if (nnStates)
5374                     nnStates[pc - CLASS_RACE].state = (i == 0) ? NNSTATE_INCREMENTAL : NNSTATE_DONE;
5375                 NeuralNetEvaluate(n, arInput, arOutput, nnStates);
5376 #endif
5377                 if (pc == CLASS_RACE)
5378                     /* special evaluation of backgammons
5379                      * overrides net output */
5380                     EvalRaceBG((ConstTanBoard) anBoardOut, arOutput, VARIATION_STANDARD);
5381 
5382                 SanityCheck((ConstTanBoard) anBoardOut, arOutput);
5383             }
5384             memcpy(ec.ar, arOutput, sizeof(float) * NUM_OUTPUTS);
5385             ec.ar[5] = 0.f;
5386             CacheAdd(&cpEval, &ec, l);
5387         }
5388         pm->rScore = UtilityME(arOutput, pci);
5389         if (i < prune_moves) {
5390             bmovesi[i] = i;
5391             if (pm->rScore > ml.amMoves[bmovesi[0]].rScore) {
5392                 bmovesi[i] = bmovesi[0];
5393                 bmovesi[0] = i;
5394             }
5395         } else if (pm->rScore < ml.amMoves[bmovesi[0]].rScore) {
5396             unsigned int m = 0, k;
5397             bmovesi[0] = i;
5398             for (k = 1; k < prune_moves; ++k) {
5399                 if (ml.amMoves[bmovesi[k]].rScore > ml.amMoves[bmovesi[m]].rScore) {
5400                     m = k;
5401                 }
5402             }
5403             bmovesi[0] = bmovesi[m];
5404             bmovesi[m] = i;
5405         }
5406     }
5407 
5408     pci->fMove = !pci->fMove;
5409 
5410     if (i == ml.cMoves)
5411         ScoreMovesPruned(&ml, pci, pec, bmovesi, prune_moves);
5412     else
5413         ScoreMoves(&ml, pci, pec, 0);
5414 
5415     PositionFromKey(anBoardOut, &ml.amMoves[ml.iMoveBest].key);
5416 }
5417 
5418 static int
5419 EvaluatePositionFull(NNState * nnStates, const TanBoard anBoard, float arOutput[],
5420                      cubeinfo * const pci, const evalcontext * pec, unsigned int nPlies, positionclass pc)
5421 {
5422     int i, n0, n1;
5423     SSE_ALIGN(float arVariationOutput[NUM_OUTPUTS]);
5424     float rTemp;
5425     int w;
5426 
5427     if (pc > CLASS_PERFECT && nPlies > 0) {
5428         /* internal node; recurse */
5429 
5430         TanBoard anBoardNew;
5431         /* int anMove[ 8 ]; */
5432         cubeinfo ciOpp;
5433         int const usePrune = pec->fUsePrune && pec->rNoise == 0.0f && pci->bgv == VARIATION_STANDARD;
5434 
5435         for (i = 0; i < NUM_OUTPUTS; i++)
5436             arOutput[i] = 0.0;
5437 
5438         /* loop over rolls */
5439 
5440         for (n0 = 1; n0 <= 6; n0++) {
5441             for (n1 = 1; n1 <= n0; n1++) {
5442                 w = (n0 == n1) ? 1 : 2;
5443 
5444                 for (i = 0; i < 25; i++) {
5445                     anBoardNew[0][i] = anBoard[0][i];
5446                     anBoardNew[1][i] = anBoard[1][i];
5447                 }
5448 
5449                 if (fInterrupt) {
5450                     errno = EINTR;
5451                     return -1;
5452                 }
5453 
5454                 if (usePrune) {
5455                     FindBestMoveInEval(nnStates, n0, n1, anBoard, anBoardNew, pci, pec);
5456                 } else {
5457 
5458                     FindBestMovePlied(NULL, n0, n1, anBoardNew, pci, pec, 0, defaultFilters);
5459                 }
5460 
5461                 SwapSides(anBoardNew);
5462 
5463                 SetCubeInfo(&ciOpp, pci->nCube, pci->fCubeOwner, !pci->fMove,
5464                             pci->nMatchTo, pci->anScore, pci->fCrawford, pci->fJacoby, pci->fBeavers, pci->bgv);
5465 
5466                 /* Evaluate at 0-ply */
5467                 if (EvaluatePositionCache(nnStates, (ConstTanBoard) anBoardNew, arVariationOutput,
5468                                           &ciOpp, pec, nPlies - 1,
5469                                           ClassifyPosition((ConstTanBoard) anBoardNew, ciOpp.bgv)))
5470                     return -1;
5471 
5472                 for (i = 0; i < NUM_OUTPUTS; i++)
5473                     arOutput[i] += w * arVariationOutput[i];
5474             }
5475 
5476         }
5477 
5478         /* normalize */
5479         for (i = 0; i < NUM_OUTPUTS; i++)
5480             arOutput[i] /= 36;
5481 
5482         /* flop eval */
5483         arOutput[OUTPUT_WIN] = 1.0f - arOutput[OUTPUT_WIN];
5484 
5485         rTemp = arOutput[OUTPUT_WINGAMMON];
5486         arOutput[OUTPUT_WINGAMMON] = arOutput[OUTPUT_LOSEGAMMON];
5487         arOutput[OUTPUT_LOSEGAMMON] = rTemp;
5488 
5489         rTemp = arOutput[OUTPUT_WINBACKGAMMON];
5490         arOutput[OUTPUT_WINBACKGAMMON] = arOutput[OUTPUT_LOSEBACKGAMMON];
5491         arOutput[OUTPUT_LOSEBACKGAMMON] = rTemp;
5492 
5493     } else {
5494         /* at leaf node; use static evaluation */
5495 
5496         if (acef[pc] (anBoard, arOutput, pci->bgv, nnStates))
5497             return -1;
5498 
5499         if (pec->rNoise > 0.0f && pc != CLASS_OVER) {
5500             for (i = 0; i < NUM_OUTPUTS; i++) {
5501                 arOutput[i] += Noise(pec, anBoard, i);
5502                 arOutput[i] = MAX(arOutput[i], 0.0f);
5503                 arOutput[i] = MIN(arOutput[i], 1.0f);
5504             }
5505         }
5506 
5507         if (pc > CLASS_GOOD || pec->rNoise > 0.0f)
5508             /* no sanity check needed for accurate evaluations */
5509             SanityCheck(anBoard, arOutput);
5510     }
5511 
5512     return 0;
5513 }
5514 
5515 
5516 static int
5517 EvaluatePositionCache(NNState * nnStates, const TanBoard anBoard, float arOutput[],
5518                       cubeinfo * const pci, const evalcontext * pecx, int nPlies, positionclass pc)
5519 {
5520     evalcache ec;
5521     uint32_t l;
5522     /* This should be a part of the code that is called in all
5523      * time-consuming operations at a relatively steady rate, so is a
5524      * good choice for a callback function. */
5525     if (!cCache || pecx->rNoise != 0.0f) {      /* non-deterministic noisy evaluations; cannot cache */
5526         return EvaluatePositionFull(nnStates, anBoard, arOutput, pci, pecx, nPlies, pc);
5527     }
5528 
5529     PositionKey(anBoard, &ec.key);
5530 
5531     ec.nEvalContext = EvalKey(pecx, nPlies, pci, FALSE);
5532     if ((l = CacheLookup(&cEval, &ec, arOutput, NULL)) == CACHEHIT) {
5533         return 0;
5534     }
5535 
5536     if (EvaluatePositionFull(nnStates, anBoard, arOutput, pci, pecx, nPlies, pc))
5537         return -1;
5538 
5539     memcpy(ec.ar, arOutput, sizeof(float) * NUM_OUTPUTS);
5540     ec.ar[5] = 0.f;
5541     CacheAdd(&cEval, &ec, l);
5542     return 0;
5543 }
5544 
5545 extern int
5546 EvaluatePosition(NNState * nnStates, const TanBoard anBoard, float arOutput[],
5547                  cubeinfo * const pci, const evalcontext * pec)
5548 {
5549 
5550     positionclass pc = ClassifyPosition(anBoard, pci->bgv);
5551 
5552     return EvaluatePositionCache(nnStates, anBoard, arOutput, pci, pec ? pec : &ecBasic, pec ? pec->nPlies : 0, pc);
5553 }
5554 
5555 
5556 extern int
5557 ScoreMove(NNState * nnStates, move * pm, const cubeinfo * pci, const evalcontext * pec, int nPlies)
5558 {
5559     TanBoard anBoardTemp;
5560     SSE_ALIGN(float arEval[NUM_ROLLOUT_OUTPUTS]);
5561     cubeinfo ci;
5562 
5563     PositionFromKeySwapped(anBoardTemp, &pm->key);
5564 
5565     /* swap fMove in cubeinfo */
5566     memcpy(&ci, pci, sizeof(ci));
5567     ci.fMove = !ci.fMove;
5568 
5569     if (GeneralEvaluationEPlied(nnStates, arEval, (ConstTanBoard) anBoardTemp, &ci, pec, nPlies))
5570         return -1;
5571 
5572     InvertEvaluationR(arEval, &ci);
5573 
5574     if (ci.nMatchTo)
5575         arEval[OUTPUT_CUBEFUL_EQUITY] = mwc2eq(arEval[OUTPUT_CUBEFUL_EQUITY], pci);
5576 
5577     /* Save evaluations */
5578     memcpy(pm->arEvalMove, arEval, NUM_ROLLOUT_OUTPUTS * sizeof(float));
5579 
5580     /* Save evaluation setup */
5581     pm->esMove.et = EVAL_EVAL;
5582     pm->esMove.ec = *pec;
5583     pm->esMove.ec.nPlies = nPlies;
5584 
5585     /* Score for move:
5586      * rScore is the primary score (cubeful/cubeless)
5587      * rScore2 is the secondary score (cubeless) */
5588     pm->rScore = (pec->fCubeful) ? arEval[OUTPUT_CUBEFUL_EQUITY] : arEval[OUTPUT_EQUITY];
5589     pm->rScore2 = arEval[OUTPUT_EQUITY];
5590 
5591     return 0;
5592 }
5593 
5594 static int
5595 ScoreMoves(movelist * pml, const cubeinfo * pci, const evalcontext * pec, int nPlies)
5596 {
5597     unsigned int i;
5598     int r = 0;                  /* return value */
5599     NNState *nnStates = MT_Get_nnState();
5600 
5601     pml->rBestScore = -99999.9f;
5602 
5603     if (nPlies == 0) {
5604         /* start incremental evaluations */
5605         nnStates[0].state = nnStates[1].state = nnStates[2].state = NNSTATE_INCREMENTAL;
5606     }
5607 
5608 
5609     for (i = 0; i < pml->cMoves; i++) {
5610         if (ScoreMove(nnStates, pml->amMoves + i, pci, pec, nPlies) < 0) {
5611             r = -1;
5612             break;
5613         }
5614 
5615         if ((pml->amMoves[i].rScore > pml->rBestScore) || ((pml->amMoves[i].rScore == pml->rBestScore)
5616                                                            && (pml->amMoves[i].rScore2 >
5617                                                                pml->amMoves[pml->iMoveBest].rScore2))) {
5618             pml->iMoveBest = i;
5619             pml->rBestScore = pml->amMoves[i].rScore;
5620         }
5621     }
5622 
5623     if (nPlies == 0) {
5624         /* reset to none */
5625 
5626         nnStates[0].state = nnStates[1].state = nnStates[2].state = NNSTATE_NONE;
5627     }
5628 
5629     return r;
5630 }
5631 
5632 static int
5633 ScoreMovesPruned(movelist * pml, const cubeinfo * pci, const evalcontext * pec, unsigned int *bmovesi, unsigned int prune_moves)
5634 {
5635     unsigned int i, j;
5636     int r = 0;                  /* return value */
5637     NNState *nnStates = MT_Get_nnState();
5638 
5639     pml->rBestScore = -99999.9f;
5640 
5641     /* start incremental evaluations */
5642     nnStates[0].state = nnStates[1].state = nnStates[2].state = NNSTATE_INCREMENTAL;
5643 
5644     for (j = 0; j < prune_moves; j++) {
5645 
5646         i = bmovesi[j];
5647 
5648         if (ScoreMove(nnStates, pml->amMoves + i, pci, pec, 0) < 0) {
5649             r = -1;
5650             break;
5651         }
5652 
5653         if ((pml->amMoves[i].rScore > pml->rBestScore) || ((pml->amMoves[i].rScore == pml->rBestScore)
5654                                                            && (pml->amMoves[i].rScore2 >
5655                                                                pml->amMoves[pml->iMoveBest].rScore2))) {
5656             pml->iMoveBest = i;
5657             pml->rBestScore = pml->amMoves[i].rScore;
5658         }
5659     }
5660 
5661     nnStates[0].state = nnStates[1].state = nnStates[2].state = NNSTATE_NONE;
5662 
5663     return r;
5664 }
5665 
5666 static movefilter NullFilter = { 0, 0, 0.0 };
5667 
5668 static int
5669 FindBestMovePlied(int anMove[8], int nDice0, int nDice1,
5670                   TanBoard anBoard,
5671                   const cubeinfo * pci, const evalcontext * pec, int nPlies,
5672                   movefilter aamf[MAX_FILTER_PLIES][MAX_FILTER_PLIES])
5673 {
5674 
5675     evalcontext ec;
5676     movelist ml;
5677     unsigned int i;
5678 
5679     memcpy(&ec, pec, sizeof(evalcontext));
5680     ec.nPlies = nPlies;
5681 
5682     if (anMove)
5683         for (i = 0; i < 8; ++i)
5684             anMove[i] = -1;
5685 
5686     if (FindnSaveBestMoves(&ml, nDice0, nDice1, (ConstTanBoard) anBoard, NULL, 0.0f, pci, &ec, aamf) < 0) {
5687         free(ml.amMoves);
5688         return -1;
5689     }
5690 
5691     if (anMove) {
5692         for (i = 0; i < ml.cMaxMoves * 2; i++)
5693             anMove[i] = ml.amMoves[ml.iMoveBest].anMove[i];
5694     }
5695 
5696     if (ml.cMoves)
5697         PositionFromKey(anBoard, &ml.amMoves[ml.iMoveBest].key);
5698 
5699     free(ml.amMoves);
5700 
5701     return ml.cMaxMoves * 2;
5702 }
5703 
5704 
5705 extern
5706     int
5707 FindBestMove(int anMove[8], int nDice0, int nDice1,
5708              TanBoard anBoard, const cubeinfo * pci, evalcontext * pec,
5709              movefilter aamf[MAX_FILTER_PLIES][MAX_FILTER_PLIES])
5710 {
5711 
5712     return FindBestMovePlied(anMove, nDice0, nDice1, anBoard, pci, pec ? pec : &ecBasic, pec ? pec->nPlies : 0, aamf);
5713 }
5714 
5715 extern int
5716 FindnSaveBestMoves(movelist * pml, int nDice0, int nDice1, const TanBoard anBoard, positionkey * keyMove, const
5717                    float rThr, const cubeinfo * pci, const evalcontext * pec,
5718                    movefilter aamf[MAX_FILTER_PLIES][MAX_FILTER_PLIES])
5719 {
5720 
5721     /* Find best moves.
5722      * Ensure that keyMove is evaluated at the deepest ply. */
5723 
5724     unsigned int i;
5725     unsigned int nMoves, iPly;
5726     move *pm;
5727     movefilter *mFilters;
5728     unsigned int nMaxPly = 0;
5729     unsigned int cOldMoves;
5730 
5731     /* Find all moves -- note that pml contains internal pointers to static
5732      * data, so we can't call GenerateMoves again (or anything that calls
5733      * it, such as ScoreMoves at more than 0 plies) until we have saved
5734      * the moves we want to keep in amCandidates. */
5735     GenerateMoves(pml, anBoard, nDice0, nDice1, FALSE);
5736 
5737     if (pml->cMoves == 0) {
5738         /* no legal moves */
5739         pml->amMoves = NULL;
5740         return 0;
5741     }
5742 
5743     /* Save moves */
5744     pm = (move *) malloc(pml->cMoves * sizeof(move));
5745     memcpy(pm, pml->amMoves, pml->cMoves * sizeof(move));
5746     pml->amMoves = pm;
5747     nMoves = pml->cMoves;
5748 
5749     mFilters = (pec->nPlies > 0 && pec->nPlies <= MAX_FILTER_PLIES) ?
5750         aamf[pec->nPlies - 1] : aamf[MAX_FILTER_PLIES - 1];
5751 
5752     for (iPly = 0; iPly < pec->nPlies; iPly++) {
5753 
5754         movefilter *mFilter = (iPly < MAX_FILTER_PLIES) ? &mFilters[iPly] : &NullFilter;
5755 
5756         unsigned int k;
5757 
5758         if (mFilter->Accept < 0) {
5759             continue;
5760         }
5761 
5762         if (ScoreMoves(pml, pci, pec, iPly) < 0) {
5763             free(pm);
5764             pml->cMoves = 0;
5765             pml->amMoves = NULL;
5766             return -1;
5767         }
5768 
5769         qsort(pml->amMoves, pml->cMoves, sizeof(move), (cfunc) CompareMoves);
5770         pml->iMoveBest = 0;
5771 
5772         k = pml->cMoves;
5773         /* we check for mFilter->Accept < 0 above */
5774         pml->cMoves = MIN((unsigned int) mFilter->Accept, pml->cMoves);
5775 
5776         {
5777             unsigned int limit = MIN(k, pml->cMoves + mFilter->Extra);
5778 
5779             for ( /**/; pml->cMoves < limit; ++pml->cMoves) {
5780                 if (pml->amMoves[pml->cMoves].rScore < pml->amMoves[0].rScore - mFilter->Threshold) {
5781                     break;
5782                 }
5783             }
5784         }
5785 
5786         nMaxPly = iPly;
5787 
5788         if (pml->cMoves == 1 && mFilter->Accept != 1)
5789             /* if there is only one move to evaluate there is no need to continue */
5790             goto finished;
5791 
5792 
5793     }
5794 
5795     /* evaluate moves on top ply */
5796 
5797     if (ScoreMoves(pml, pci, pec, pec->nPlies) < 0) {
5798         free(pm);
5799         pml->cMoves = 0;
5800         pml->amMoves = NULL;
5801         return -1;
5802     }
5803 
5804     nMaxPly = pec->nPlies;
5805 
5806     /* Resort the moves, in case the new evaluation reordered them. */
5807     qsort(pml->amMoves, pml->cMoves, sizeof(move), (cfunc) CompareMoves);
5808     pml->iMoveBest = 0;
5809 
5810     /* set the proper size of the movelist */
5811 
5812   finished:
5813 
5814     cOldMoves = pml->cMoves;
5815     pml->cMoves = nMoves;
5816 
5817     /* Make sure that keyMove and top move are both
5818      * evaluated at the deepest ply. */
5819     if (keyMove) {
5820 
5821         int fResort = FALSE;
5822 
5823         for (i = 0; i < pml->cMoves; i++)
5824             if (EqualKeys((*keyMove), pml->amMoves[i].key)) {
5825 
5826                 /* ensure top move is evaluted at deepest ply */
5827 
5828                 if (pml->amMoves[i].esMove.ec.nPlies < nMaxPly) {
5829                     ScoreMove(NULL, pml->amMoves + i, pci, pec, nMaxPly);
5830                     fResort = TRUE;
5831                 }
5832 
5833                 if ((fabsf(pml->amMoves[i].rScore - pml->amMoves[0].rScore) > rThr) && (nMaxPly < pec->nPlies)) {
5834 
5835                     /* this is en error/blunder: re-analyse at top-ply */
5836 
5837                     ScoreMove(NULL, pml->amMoves, pci, pec, pec->nPlies);
5838                     ScoreMove(NULL, pml->amMoves + i, pci, pec, pec->nPlies);
5839                     cOldMoves = 1;      /* only one move scored at deepest ply */
5840                     fResort = TRUE;
5841 
5842                 }
5843 
5844                 /* move it up to the other moves evaluated on nMaxPly */
5845 
5846                 if (fResort && pec->nPlies) {
5847                     move m;
5848                     unsigned int j;
5849 
5850                     memcpy(&m, pml->amMoves + i, sizeof m);
5851 
5852                     for (j = i - 1; j >= cOldMoves; --j)
5853                         memcpy(pml->amMoves + j + 1, pml->amMoves + j, sizeof(move));
5854 
5855                     memcpy(pml->amMoves + cOldMoves, &m, sizeof(m));
5856 
5857                     /* reorder moves evaluated on nMaxPly */
5858 
5859                     qsort(pml->amMoves, cOldMoves + 1, sizeof(move), (cfunc) CompareMoves);
5860 
5861                 }
5862                 break;
5863             }
5864     }
5865 
5866     return 0;
5867 
5868 }
5869 
5870 extern int
5871 GeneralCubeDecisionE(float aarOutput[2][NUM_ROLLOUT_OUTPUTS],
5872                      const TanBoard anBoard,
5873                      cubeinfo * const pci, const evalcontext * pec, const evalsetup * UNUSED(pes))
5874 {
5875 
5876     SSE_ALIGN(float arOutput[NUM_OUTPUTS]);
5877     cubeinfo aciCubePos[2];
5878     float arCubeful[2];
5879     int i, j;
5880 
5881 
5882     /* Setup cube for "no double" and "double, take" */
5883 
5884     memcpy(&aciCubePos[0], pci, sizeof(cubeinfo));
5885     memcpy(&aciCubePos[1], pci, sizeof(cubeinfo));
5886     aciCubePos[1].fCubeOwner = !aciCubePos[1].fMove;
5887     aciCubePos[1].nCube *= 2;
5888 
5889     if (EvaluatePositionCubeful3(NULL, anBoard, arOutput, arCubeful, aciCubePos, 2, pci, pec, pec->nPlies, TRUE))
5890         return -1;
5891 
5892 
5893     /* Scale double-take equity */
5894     if (!pci->nMatchTo)
5895         arCubeful[1] *= 2.0f;
5896 
5897     for (i = 0; i < 2; i++) {
5898 
5899         /* copy cubeless winning chances */
5900         for (j = 0; j < NUM_OUTPUTS; j++)
5901             aarOutput[i][j] = arOutput[j];
5902 
5903         /* equity */
5904 
5905         aarOutput[i][OUTPUT_EQUITY] = UtilityME(arOutput, &aciCubePos[i]);
5906 
5907         aarOutput[i][OUTPUT_CUBEFUL_EQUITY] = arCubeful[i];
5908 
5909     }
5910 
5911     return 0;
5912 
5913 }
5914 
5915 extern int
5916 GeneralEvaluationE(float arOutput[NUM_ROLLOUT_OUTPUTS],
5917                    const TanBoard anBoard, cubeinfo * const pci, const evalcontext * pec)
5918 {
5919 
5920     return GeneralEvaluationEPlied(NULL, arOutput, anBoard, pci, pec, pec->nPlies);
5921 
5922 }
5923 
5924 
5925 static int
5926 GeneralEvaluationEPliedCubeful(NNState * nnStates, float arOutput[NUM_ROLLOUT_OUTPUTS],
5927                                const TanBoard anBoard, cubeinfo * const pci, const evalcontext * pec, int nPlies)
5928 {
5929 
5930     float rCubeful;
5931 
5932     if (EvaluatePositionCubeful3(nnStates, anBoard, arOutput, &rCubeful, pci, 1, pci, pec, nPlies, FALSE))
5933         return -1;
5934 
5935     arOutput[OUTPUT_EQUITY] = UtilityME(arOutput, pci);
5936     arOutput[OUTPUT_CUBEFUL_EQUITY] = rCubeful;
5937 
5938     return 0;
5939 
5940 }
5941 
5942 extern int
5943 GeneralEvaluationEPlied(NNState * nnStates, float arOutput[NUM_ROLLOUT_OUTPUTS],
5944                         const TanBoard anBoard, cubeinfo * const pci, const evalcontext * pec, int nPlies)
5945 {
5946 
5947     if (pec->fCubeful) {
5948 
5949         if (GeneralEvaluationEPliedCubeful(nnStates, arOutput, anBoard, pci, pec, nPlies))
5950             return -1;
5951 
5952     } else {
5953         if (EvaluatePositionCache(nnStates, anBoard, arOutput, pci, pec, nPlies, ClassifyPosition(anBoard, pci->bgv)))
5954             return -1;
5955 
5956         arOutput[OUTPUT_EQUITY] = UtilityME(arOutput, pci);
5957         arOutput[OUTPUT_CUBEFUL_EQUITY] = 0.0f;
5958 
5959     }
5960 
5961     return 0;
5962 
5963 }
5964 
5965 static int
5966 EvaluatePositionCubeful4(NNState * nnStates, const TanBoard anBoard,
5967                          float arOutput[NUM_OUTPUTS],
5968                          float arCubeful[],
5969                          const cubeinfo aciCubePos[], int cci,
5970                          cubeinfo * const pciMove, const evalcontext * pec, unsigned int nPlies, int fTop)
5971 {
5972 
5973 
5974     /* calculate cubeful equity */
5975 
5976     int i, ici;
5977     positionclass pc;
5978     float r;
5979     SSE_ALIGN(float ar[NUM_OUTPUTS]);
5980     float arEquity[4];
5981     float rCubeX;
5982 
5983     cubeinfo ciMoveOpp;
5984 
5985     float *arCf = (float *) g_alloca(2 * cci * sizeof(float));
5986     float *arCfTemp = (float *) g_alloca(2 * cci * sizeof(float));
5987     cubeinfo *aci = (cubeinfo *) g_alloca(2 * cci * sizeof(cubeinfo));
5988 
5989     int w;
5990     int n0, n1;
5991 
5992     pc = ClassifyPosition(anBoard, pciMove->bgv);
5993 
5994     if (pc > CLASS_OVER && nPlies > 0 && !(pc <= CLASS_PERFECT && !pciMove->nMatchTo)) {
5995         /* internal node; recurse */
5996 
5997         TanBoard anBoardNew;
5998 
5999         int const usePrune = pec->fUsePrune && pec->rNoise == 0.0f && pciMove->bgv == VARIATION_STANDARD;
6000 
6001         for (i = 0; i < NUM_OUTPUTS; i++)
6002             arOutput[i] = 0.0;
6003 
6004         for (i = 0; i < 2 * cci; i++)
6005             arCf[i] = 0.0;
6006 
6007         /* construct next level cube positions */
6008 
6009         MakeCubePos(aciCubePos, cci, fTop, aci, TRUE);
6010 
6011         /* loop over rolls */
6012 
6013         for (n0 = 1; n0 <= 6; n0++) {
6014             for (n1 = 1; n1 <= n0; n1++) {
6015                 w = (n0 == n1) ? 1 : 2;
6016 
6017                 for (i = 0; i < 25; i++) {
6018                     anBoardNew[0][i] = anBoard[0][i];
6019                     anBoardNew[1][i] = anBoard[1][i];
6020                 }
6021 
6022                 if (fInterrupt) {
6023                     errno = EINTR;
6024                     return -1;
6025                 }
6026 
6027                 if (usePrune) {
6028                     FindBestMoveInEval(nnStates, n0, n1, anBoard, anBoardNew, pciMove, pec);
6029                 } else {
6030 
6031                     FindBestMovePlied(NULL, n0, n1, anBoardNew, pciMove, pec, 0, defaultFilters);
6032                 }
6033 
6034                 SwapSides(anBoardNew);
6035 
6036                 SetCubeInfo(&ciMoveOpp,
6037                             pciMove->nCube, pciMove->fCubeOwner,
6038                             !pciMove->fMove, pciMove->nMatchTo,
6039                             pciMove->anScore, pciMove->fCrawford, pciMove->fJacoby, pciMove->fBeavers, pciMove->bgv);
6040 
6041                 /* Evaluate at 0-ply */
6042                 if (EvaluatePositionCubeful3(nnStates, (ConstTanBoard) anBoardNew,
6043                                              ar, arCfTemp, aci, 2 * cci, &ciMoveOpp, pec, nPlies - 1, FALSE))
6044                     return -1;
6045 
6046                 /* Sum up cubeless winning chances and cubeful equities */
6047 
6048                 for (i = 0; i < NUM_OUTPUTS; i++)
6049                     arOutput[i] += w * ar[i];
6050                 for (i = 0; i < 2 * cci; i++)
6051                     arCf[i] += w * arCfTemp[i];
6052 
6053             }
6054 
6055         }
6056 
6057         /* Flip evals */
6058 #define sumW 36
6059 
6060         arOutput[OUTPUT_WIN] = 1.0f - arOutput[OUTPUT_WIN] / sumW;
6061 
6062         r = arOutput[OUTPUT_WINGAMMON] / sumW;
6063         arOutput[OUTPUT_WINGAMMON] = arOutput[OUTPUT_LOSEGAMMON] / sumW;
6064         arOutput[OUTPUT_LOSEGAMMON] = r;
6065 
6066         r = arOutput[OUTPUT_WINBACKGAMMON] / sumW;
6067         arOutput[OUTPUT_WINBACKGAMMON] = arOutput[OUTPUT_LOSEBACKGAMMON] / sumW;
6068         arOutput[OUTPUT_LOSEBACKGAMMON] = r;
6069 
6070         for (i = 0; i < 2 * cci; i++)
6071             if (pciMove->nMatchTo)
6072                 arCf[i] = 1.0f - arCf[i] / sumW;
6073             else
6074                 arCf[i] = -arCf[i] / sumW;
6075 #undef sumW
6076 
6077         /* invert fMove */
6078         /* Remember than fMove was inverted in the call to MakeCubePos */
6079 
6080         for (i = 0; i < 2 * cci; i++)
6081             aci[i].fMove = !aci[i].fMove;
6082 
6083         /* get cubeful equities */
6084 
6085         GetECF3(arCubeful, cci, arCf, aci);
6086 
6087     } else {
6088         /* at leaf node; use static evaluation */
6089 
6090         if (pc == CLASS_HYPERGAMMON1 || pc == CLASS_HYPERGAMMON2 || pc == CLASS_HYPERGAMMON3) {
6091 
6092             bearoffcontext *pbc = apbcHyper[pc - CLASS_HYPERGAMMON1];
6093             unsigned int nUs, nThem, iPos;
6094             unsigned int n;
6095 
6096             if (!pbc)
6097                 return -1;
6098 
6099             nUs = PositionBearoff(anBoard[1], pbc->nPoints, pbc->nChequers);
6100             nThem = PositionBearoff(anBoard[0], pbc->nPoints, pbc->nChequers);
6101             n = Combination(pbc->nPoints + pbc->nChequers, pbc->nPoints);
6102             iPos = nUs * n + nThem;
6103 
6104             if (BearoffHyper(apbcHyper[pc - CLASS_HYPERGAMMON1], iPos, arOutput, arEquity))
6105                 return -1;
6106 
6107         } else if (pc > CLASS_OVER && pc <= CLASS_PERFECT /* && ! pciMove->nMatchTo */ ) {
6108 
6109             if (EvaluatePerfectCubeful(anBoard, arEquity, pciMove->bgv)) {
6110                 return -1;
6111             }
6112 
6113             arOutput[OUTPUT_WIN] = (arEquity[0] + 1.0f) / 2.0f;
6114             arOutput[OUTPUT_WINGAMMON] = arOutput[OUTPUT_WINBACKGAMMON] = arOutput[OUTPUT_LOSEGAMMON] =
6115                 arOutput[OUTPUT_LOSEBACKGAMMON] = 0.0;
6116 
6117         } else {
6118 
6119             /* evaluate with neural net */
6120 
6121             if (EvaluatePosition(nnStates, anBoard, arOutput, pciMove, NULL))
6122                 return -1;
6123 
6124             if (pec->rNoise > 0.0f && pc != CLASS_OVER) {
6125                 for (i = 0; i < NUM_OUTPUTS; i++) {
6126                     arOutput[i] += Noise(pec, anBoard, i);
6127                     arOutput[i] = MAX(arOutput[i], 0.0f);
6128                     arOutput[i] = MIN(arOutput[i], 1.0f);
6129                 }
6130             }
6131 
6132             if (pc > CLASS_GOOD || pec->rNoise > 0.0f)
6133                 /* no sanity check needed for accurate evaluations */
6134                 SanityCheck(anBoard, arOutput);
6135 
6136         }
6137 
6138         /* Calculate cube efficiency */
6139 
6140         rCubeX = EvalEfficiency(anBoard, pc);
6141 
6142         /* Build all possible cube positions */
6143 
6144         MakeCubePos(aciCubePos, cci, fTop, aci, FALSE);
6145 
6146         /* Calculate cubeful equity for each possible cube position */
6147 
6148         for (ici = 0; ici < 2 * cci; ici++)
6149             if (aci[ici].nCube > 0) {
6150                 /* cube available */
6151 
6152                 if (!aci[ici].nMatchTo) {
6153 
6154                     /* money play */
6155 
6156                     switch (pc) {
6157                     case CLASS_HYPERGAMMON1:
6158                     case CLASS_HYPERGAMMON2:
6159                     case CLASS_HYPERGAMMON3:
6160                         /* exact bearoff equities & contact */
6161 
6162                         arCf[ici] = CFHYPER(arEquity, &aci[ici]);
6163                         break;
6164 
6165                     case CLASS_BEAROFF2:
6166                     case CLASS_BEAROFF_TS:
6167                         /* exact bearoff equities */
6168 
6169                         arCf[ici] = CFMONEY(arEquity, &aci[ici]);
6170                         break;
6171 
6172                     case CLASS_OVER:
6173                     case CLASS_RACE:
6174                     case CLASS_CRASHED:
6175                     case CLASS_CONTACT:
6176                     case CLASS_BEAROFF1:
6177                     case CLASS_BEAROFF_OS:
6178                         /* approximate using Janowski's formulae */
6179 
6180                         arCf[ici] = Cl2CfMoney(arOutput, &aci[ici], rCubeX);
6181                         break;
6182 
6183                     }
6184 
6185                 } else {
6186 
6187                     float rCl, rCf, rCfMoney;
6188                     float X = rCubeX;
6189                     cubeinfo ciMoney;
6190 
6191                     /* match play */
6192 
6193                     switch (pc) {
6194                     case CLASS_HYPERGAMMON1:
6195                     case CLASS_HYPERGAMMON2:
6196                     case CLASS_HYPERGAMMON3:
6197                         /* use exact money equities to guess cube efficiency */
6198 
6199                         SetCubeInfoMoney(&ciMoney, 1, aci[ici].fCubeOwner, aci[ici].fMove, FALSE, FALSE, aci[ici].bgv);
6200 
6201                         rCl = Utility(arOutput, &ciMoney);
6202                         rCubeX = 1.0;
6203                         rCf = Cl2CfMoney(arOutput, &ciMoney, rCubeX);
6204                         rCfMoney = CFHYPER(arEquity, &ciMoney);
6205 
6206                         if (fabsf(rCl - rCf) > 0.0001f)
6207                             rCubeX = (rCfMoney - rCl) / (rCf - rCl);
6208 
6209                         arCf[ici] = Cl2CfMatch(arOutput, &aci[ici], rCubeX);
6210 
6211                         rCubeX = X;
6212 
6213                         break;
6214 
6215                     case CLASS_BEAROFF2:
6216                     case CLASS_BEAROFF_TS:
6217                         /* use exact money equities to guess cube efficiency */
6218 
6219                         SetCubeInfoMoney(&ciMoney, 1, aci[ici].fCubeOwner, aci[ici].fMove, FALSE, FALSE, aci[ici].bgv);
6220 
6221                         rCl = arEquity[0];
6222                         rCubeX = 1.0;
6223                         rCf = Cl2CfMoney(arOutput, &ciMoney, rCubeX);
6224                         rCfMoney = CFMONEY(arEquity, &ciMoney);
6225 
6226                         if (fabsf(rCl - rCf) > 0.0001f)
6227                             rCubeX = (rCfMoney - rCl) / (rCf - rCl);
6228                         else
6229                             rCubeX = X;
6230 
6231                         /* fabs(...) > 0.0001 above is not enough. We still get some
6232                          * nutty values for rCubeX and need more sanity checking */
6233 
6234                         if (rCubeX < 0.0f)
6235                             rCubeX = 0.0f;
6236                         if (rCubeX > X)
6237                             rCubeX = X;
6238 
6239                         arCf[ici] = Cl2CfMatch(arOutput, &aci[ici], rCubeX);
6240 
6241                         rCubeX = X;
6242 
6243                         break;
6244 
6245                     case CLASS_OVER:
6246                     case CLASS_RACE:
6247                     case CLASS_CRASHED:
6248                     case CLASS_CONTACT:
6249                     case CLASS_BEAROFF1:
6250                     case CLASS_BEAROFF_OS:
6251                         /* approximate using Joern's generalisation of
6252                          * Janowski's formulae */
6253 
6254                         arCf[ici] = Cl2CfMatch(arOutput, &aci[ici], rCubeX);
6255                         break;
6256 
6257                     }
6258 
6259                 }
6260 
6261             }
6262 
6263 
6264         /* find optimal of "no double" and "double" */
6265 
6266         GetECF3(arCubeful, cci, arCf, aci);
6267 
6268     }
6269 
6270     return 0;
6271 
6272 }
6273 
6274 /* EvaluatePositionCubeful3 is now just a wrapper for ....Cubeful4, which
6275  * first checks the cache, and then calls ...Cubeful3 */
6276 
6277 extern int
6278 EvaluatePositionCubeful3(NNState * nnStates, const TanBoard anBoard,
6279                          float arOutput[NUM_OUTPUTS],
6280                          float arCubeful[],
6281                          const cubeinfo aciCubePos[], int cci,
6282                          cubeinfo * const pciMove, const evalcontext * pec, int nPlies, int fTop)
6283 {
6284 
6285     int ici;
6286     int fAll = TRUE;
6287     evalcache ec;
6288 
6289     if (!cCache || pec->rNoise != 0.0f)
6290         /* non-deterministic evaluation; never cache */
6291     {
6292         return EvaluatePositionCubeful4(nnStates, anBoard, arOutput, arCubeful,
6293                                         aciCubePos, cci, pciMove, pec, nPlies, fTop);
6294     }
6295 
6296     PositionKey(anBoard, &ec.key);
6297 
6298     /* check cache for existence for earlier calculation */
6299 
6300     fAll = !fTop;               /* FIXME: fTop should be a part of EvalKey */
6301 
6302     for (ici = 0; ici < cci && fAll; ++ici) {
6303 
6304         if (aciCubePos[ici].nCube < 0) {
6305             continue;
6306         }
6307 
6308         ec.nEvalContext = EvalKey(pec, nPlies, &aciCubePos[ici], TRUE);
6309 
6310         if (CacheLookup(&cEval, &ec, arOutput, arCubeful + ici) != CACHEHIT) {
6311             fAll = FALSE;
6312         }
6313     }
6314 
6315     /* get equities */
6316 
6317     if (!fAll) {
6318 
6319         /* cache miss */
6320         if (EvaluatePositionCubeful4(nnStates, anBoard, arOutput, arCubeful,
6321                                      aciCubePos, cci, pciMove, pec, nPlies, fTop))
6322             return -1;
6323 
6324         /* add to cache */
6325 
6326         if (!fTop) {
6327 
6328             for (ici = 0; ici < cci; ++ici) {
6329                 if (aciCubePos[ici].nCube < 0)
6330                     continue;
6331 
6332                 memcpy(ec.ar, arOutput, sizeof(float) * NUM_OUTPUTS);
6333                 ec.ar[5] = arCubeful[ici];      /* Cubeful equity stored in slot 5 */
6334                 ec.nEvalContext = EvalKey(pec, nPlies, &aciCubePos[ici], TRUE);
6335 
6336                 CacheAdd(&cEval, &ec, GetHashKey(cEval.hashMask, &ec));
6337 
6338             }
6339         }
6340     }
6341 
6342     return 0;
6343 
6344 }
6345