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