/* * Copyright (C) 1998-2002 Gary Wong * Copyright (C) 2000-2025 the AUTHORS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef EVAL_H #define EVAL_H #include "dice.h" #include "bearoff.h" #include "neuralnet.h" #include "cache.h" #define EXP_LOCK_FUN(ret, name, ...) \ typedef ret (*f_##name)( __VA_ARGS__); \ extern f_##name name; \ extern ret name##NoLocking( __VA_ARGS__); \ extern ret name##WithLocking( __VA_ARGS__) #define WEIGHTS_VERSION "1.01" #define WEIGHTS_VERSION_BINARY 1.01f #define WEIGHTS_MAGIC_BINARY 472.3782f #define NUM_OUTPUTS 5 #define NUM_CUBEFUL_OUTPUTS 4 #define NUM_ROLLOUT_OUTPUTS 7 #define BETA_HIDDEN 0.1f #define BETA_OUTPUT 1.0f #define OUTPUT_WIN 0 #define OUTPUT_WINGAMMON 1 #define OUTPUT_WINBACKGAMMON 2 #define OUTPUT_LOSEGAMMON 3 #define OUTPUT_LOSEBACKGAMMON 4 #define OUTPUT_EQUITY 5 /* NB: neural nets do not output equity, only * rollouts do. */ #define OUTPUT_CUBEFUL_EQUITY 6 /* Cubeful evaluations */ typedef enum { OUTPUT_OPTIMAL = 0, OUTPUT_NODOUBLE, OUTPUT_TAKE, OUTPUT_DROP } CubefulOutputs; /* A trivial upper bound on the number of (complete or incomplete) * legal moves of a single roll: if all 15 chequers are spread out, * then there are 18 C 4 + 17 C 3 + 16 C 2 + 15 C 1 = 3875 * combinations in which a roll of 11 could be played (up to 4 choices from * 15 chequers, and a chequer may be chosen more than once). The true * bound will be lower than this (because there are only 26 points, * some plays of 15 chequers must "overlap" and map to the same * resulting position), but that would be more difficult to * compute. */ #define MAX_INCOMPLETE_MOVES 3875 #define MAX_MOVES 3060 typedef struct { int Accept; /* always allow this many moves. -1 means don't use this level */ int Extra; /* and add up to this many more */ float Threshold; /* if they are within this equity difference */ } movefilter; /* we'll have filters for 1..4 ply evaluation */ #define MAX_FILTER_PLIES 4 extern movefilter defaultFilters[MAX_FILTER_PLIES][MAX_FILTER_PLIES]; typedef struct { /* FIXME expand this... e.g. different settings for different position * classes */ unsigned int fCubeful:1; /* cubeful evaluation */ unsigned int nPlies:4; unsigned int fUsePrune:1; unsigned int fDeterministic:1; unsigned int :25; /* padding */ float rNoise; /* standard deviation */ } evalcontext; /* identifies the format of evaluation info in .sgf files * early (pre extending rollouts) had no version numbers * extendable rollouts have a version number of 1 * with pruning nets, we have two possibilities - reduction could still * be enabled (version = 2) or, given the speedup and performance * improvements, I assume we will drop reduction entirely. (ver = 3 or more) * When presented with an .sgf file, gnubg will attempt to work out what * data is present in the file based on the version number */ #define SGF_FORMAT_VER 3 typedef struct { evalcontext aecCube[2], aecChequer[2]; /* evaluation parameters */ evalcontext aecCubeLate[2], aecChequerLate[2]; /* ... for later moves */ evalcontext aecCubeTrunc, aecChequerTrunc; /* ... at truncation point */ movefilter aaamfChequer[2][MAX_FILTER_PLIES][MAX_FILTER_PLIES]; movefilter aaamfLate[2][MAX_FILTER_PLIES][MAX_FILTER_PLIES]; unsigned int fCubeful:1; /* Cubeful rollout */ unsigned int fVarRedn:1; /* variance reduction */ unsigned int fInitial:1; /* roll out as opening position */ unsigned int fRotate:1; /* quasi-random dice */ unsigned int fTruncBearoff2:1; /* cubeless rollout: trunc at BEAROFF2 */ unsigned int fTruncBearoffOS:1; /* cubeless rollout: trunc at BEAROFF_OS */ unsigned int fLateEvals:1; /* enable different evals for later moves */ unsigned int fDoTruncate:1; /* enable truncated rollouts */ unsigned int fStopOnSTD:1; /* stop when std's are small enough */ unsigned int fStopOnJsd:1; unsigned int fStopMoveOnJsd:1; /* stop multi-line rollout when jsd * is small enough */ unsigned short nTruncate; /* truncation */ unsigned int nTrials; /* number of rollouts */ unsigned short nLate; /* switch evaluations on move nLate of game */ rng rngRollout; unsigned long nSeed; unsigned int nMinimumGames; /* always do at least this many */ float rStdLimit; /* stop when std < this */ unsigned int nMinimumJsdGames; float rJsdLimit; unsigned int nGamesDone; float rStoppedOnJSD; int nSkip; } rolloutcontext; typedef struct { float rEquity; float rJSD; int nOrder; int nRank; } jsdinfo; typedef enum { EVAL_NONE, EVAL_EVAL, EVAL_ROLLOUT } evaltype; /* enumeration of variations of backgammon * (starting position and/or special rules) */ extern bgvariation bgvDefault; extern int anChequers[NUM_VARIATIONS]; extern const char *aszVariations[NUM_VARIATIONS]; extern const char *aszVariationCommands[NUM_VARIATIONS]; /* * Cubeinfo contains the information necessary for evaluation * of a position. * These structs are placed here so that the move struct can be defined */ typedef struct { /* * nCube: the current value of the cube, * fCubeOwner: the owner of the cube, * fMove: the player for which we are * calculating equity for, * fCrawford, fJacoby, fBeavers: optional rules in effect, * arGammonPrice: the gammon prices; * [ 0 ] = gammon price for player 0, * [ 1 ] = gammon price for player 1, * [ 2 ] = backgammon price for player 0, * [ 3 ] = backgammon price for player 1. * */ int nCube, fCubeOwner, fMove, nMatchTo, anScore[2], fCrawford, fJacoby, fBeavers; float arGammonPrice[4]; bgvariation bgv; } cubeinfo; typedef struct { evaltype et; evalcontext ec; rolloutcontext rc; } evalsetup; typedef enum { DOUBLE_TAKE, DOUBLE_PASS, NODOUBLE_TAKE, TOOGOOD_TAKE, TOOGOOD_PASS, DOUBLE_BEAVER, NODOUBLE_BEAVER, REDOUBLE_TAKE, REDOUBLE_PASS, NO_REDOUBLE_TAKE, TOOGOODRE_TAKE, TOOGOODRE_PASS, NO_REDOUBLE_BEAVER, NODOUBLE_DEADCUBE, /* cube is dead (match play only) */ NO_REDOUBLE_DEADCUBE, /* cube is dead (match play only) */ NOT_AVAILABLE, /* Cube not available */ OPTIONAL_DOUBLE_TAKE, OPTIONAL_REDOUBLE_TAKE, OPTIONAL_DOUBLE_BEAVER, OPTIONAL_DOUBLE_PASS, OPTIONAL_REDOUBLE_PASS } cubedecision; typedef enum { DT_NORMAL, DT_BEAVER, DT_RACCOON, NUM_DOUBLE_TYPES } doubletype; /* * TT_NA can happen if a single position was loaded from a sgf file. * To check for beavers, use "> TT_NORMAL", not "!= TT_NORMAL". */ typedef enum { TT_NA, TT_NORMAL, TT_BEAVER } taketype; extern const char *aszDoubleTypes[NUM_DOUBLE_TYPES]; /* * prefined settings */ #define NUM_SETTINGS 9 #define SETTINGS_4PLY 8 #define SETTINGS_3PLY 7 #define SETTINGS_2PLY 6 #define SETTINGS_1PLY 5 #define SETTINGS_0PLY 4 #define SETTINGS_ADVANCED 3 #define SETTINGS_INTERMEDIATE 2 #define SETTINGS_NOVICE 1 #define SETTINGS_BEGINNER 0 extern evalcontext aecSettings[NUM_SETTINGS]; extern evalcontext ecBasic; extern int aiSettingsMoveFilter[NUM_SETTINGS]; extern const char *aszSettings[NUM_SETTINGS]; #define NUM_MOVEFILTER_SETTINGS 2 extern const char *aszMoveFilterSettings[NUM_MOVEFILTER_SETTINGS]; extern movefilter aaamfMoveFilterSettings[NUM_MOVEFILTER_SETTINGS][MAX_FILTER_PLIES][MAX_FILTER_PLIES]; typedef enum { CMARK_NONE, CMARK_ROLLOUT } CMark; typedef struct { int anMove[8]; positionkey key; unsigned int cMoves, cPips; /* scores for this move */ float rScore, rScore2; /* evaluation for this move */ float arEvalMove[NUM_ROLLOUT_OUTPUTS]; float arEvalStdDev[NUM_ROLLOUT_OUTPUTS]; evalsetup esMove; CMark cmark; } move; extern int fInterrupt; extern cubeinfo ciCubeless; extern const char *aszEvalType[(int) EVAL_ROLLOUT + 1]; extern bearoffcontext *pbc1; extern bearoffcontext *pbc2; extern bearoffcontext *pbcOS; extern bearoffcontext *pbcTS; extern bearoffcontext *apbcHyper[3]; typedef struct { unsigned int cMoves; /* and current move when building list */ unsigned int cMaxMoves, cMaxPips; int iMoveBest; float rBestScore; move *amMoves; } movelist; /* cube efficiencies */ extern float rOSCubeX; extern float rRaceFactorX[2]; extern float rRaceCoefficientX[2]; extern float rRaceMax[2]; extern float rRaceMin[2]; extern float rCrashedX[2]; extern float rContactX[2]; /* position classes */ typedef enum { CLASS_OVER = 0, /* Game already finished */ CLASS_HYPERGAMMON1, /* hypergammon with 1 chequers */ CLASS_HYPERGAMMON2, /* hypergammon with 2 chequers */ CLASS_HYPERGAMMON3, /* hypergammon with 3 chequers */ CLASS_BEAROFF2, /* Two-sided bearoff database (in memory) */ CLASS_BEAROFF_TS, /* Two-sided bearoff database (on disk) */ CLASS_BEAROFF1, /* One-sided bearoff database (in memory) */ CLASS_BEAROFF_OS, /* One-sided bearoff database /on disk) */ CLASS_RACE, /* Race neural network */ CLASS_CRASHED, /* Contact, one side has less than 7 active checkers */ CLASS_CONTACT /* Contact neural network */ } positionclass; #define N_CLASSES (CLASS_CONTACT + 1) #define CLASS_PERFECT CLASS_BEAROFF_TS #define CLASS_GOOD CLASS_BEAROFF_OS /* Good enough to not need SanityCheck */ typedef int (*classevalfunc) (const TanBoard anBoard, float arOutput[], const bgvariation bgv, NNState * nnStates); extern classevalfunc acef[N_CLASSES]; /* Evaluation cache size is 2^SIZE entries */ #define CACHE_SIZE_DEFAULT 19 #define CACHE_SIZE_GUIMAX 23 #define CFMONEY(arEquity,pci) \ ( ( (pci)->fCubeOwner == -1 ) ? arEquity[ 2 ] : \ ( ( (pci)->fCubeOwner == (pci)->fMove ) ? arEquity[ 1 ] : arEquity[ 3 ] ) ) #define CFHYPER(arEquity,pci) \ ( ( (pci)->fCubeOwner == -1 ) ? \ ( ( (pci)->fJacoby ) ? arEquity[ 2 ] : arEquity[ 1 ] ) : \ ( ( (pci)->fCubeOwner == (pci)->fMove ) ? arEquity[ 0 ] : arEquity[ 3 ] ) ) extern void EvalInitialise(char *szWeights, char *szWeightsBinary, int fNoBearoff, void (*pfProgress) (unsigned int)); extern int EvalShutdown(void); extern void EvalStatus(char *szOutput); extern int EvalNewWeights(int nSize); extern int EvalSave(const char *szWeights); EXP_LOCK_FUN(int, EvaluatePosition, NNState * nnStates, const TanBoard anBoard, float arOutput[], cubeinfo * const pci, const evalcontext * pec); extern void InvertEvaluationR(float ar[NUM_ROLLOUT_OUTPUTS], const cubeinfo * pci); extern void InvertEvaluation(float ar[NUM_OUTPUTS]); EXP_LOCK_FUN(int, FindBestMove, int anMove[8], int nDice0, int nDice1, TanBoard anBoard, const cubeinfo * pci, evalcontext * pec, movefilter aamf[MAX_FILTER_PLIES][MAX_FILTER_PLIES]); EXP_LOCK_FUN(int, FindnSaveBestMoves, movelist * pml, int nDice0, int nDice1, const TanBoard anBoard, positionkey * keyMove, int fAnalyse, const float rThr, const cubeinfo * pci, const evalcontext * pec, movefilter aamf[MAX_FILTER_PLIES][MAX_FILTER_PLIES]); extern void PipCount(const TanBoard anBoard, unsigned int anPips[2]); extern int ThorpCount(const TanBoard anBoard, int *pnLeader, float *adjusted, int *pnTrailer); extern int KeithCount(const TanBoard anBoard, int pn[2]); extern int IsightCount(const TanBoard anBoard, int pn[2]); extern int DumpPosition(const TanBoard anBoard, char *szOutput, const evalcontext * pec, cubeinfo * pci, int fOutputMWC, int fOutputWinPC, int fOutputInvert, const char *szMatchID); extern void SwapSides(TanBoard anBoard); extern int GameStatus(const TanBoard anBoard, bgvariation bgv); extern void EvalCacheFlush(void); extern int EvalCacheResize(unsigned int cNew); extern int EvalCacheStats(unsigned int *pcUsed, unsigned int *pcLookup, unsigned int *pcHit); extern double GetEvalCacheSize(void); void SetEvalCacheSize(unsigned int size); extern unsigned int GetEvalCacheEntries(void); extern int GetCacheMB(int size); extern evalCache cEval; extern evalCache cpEval; extern unsigned int cCache; extern int GenerateMoves(movelist * pml, const TanBoard anBoard, int n0, int n1, int fPartial); extern int ApplySubMove(TanBoard anBoard, int iSrc, int nRoll, int fCheckLegal); extern int ApplyMove(TanBoard anBoard, const int anMove[8], int fCheckLegal); extern positionclass ClassifyPosition(const TanBoard anBoard, bgvariation bgv); /* internal use only */ extern void EvalRaceBG(const TanBoard anBoard, float arOutput[], bgvariation bgv); extern float Utility(float ar[NUM_OUTPUTS], const cubeinfo * pci); extern float UtilityME(float ar[NUM_OUTPUTS], const cubeinfo * pci); extern int SetCubeInfoMoney(cubeinfo * pci, int nCube, int fCubeOwner, int fMove, int fJacoby, int fBeavers, bgvariation bgv); extern int SetCubeInfo(cubeinfo * pci, int nCube, int fCubeOwner, int fMove, int nMatchTo, const int anScore[2], int fCrawford, int fJacoby, int fBeavers, bgvariation bgv); extern void swap_us(unsigned int *p0, unsigned int *p1); extern void swap(int *p0, int *p1); extern void SanityCheck(const TanBoard anBoard, float arOutput[]); extern int EvalOver(const TanBoard anBoard, float arOutput[], bgvariation bgv, NNState * nnStates); extern float KleinmanCount(int nPipOnRoll, int nPipNotOnRoll); extern int GetDPEq(int *pfCube, float *prDPEq, const cubeinfo * pci); extern float mwc2eq(float rMwc, const cubeinfo * pci); extern float eq2mwc(float rEq, const cubeinfo * pci); extern float se_mwc2eq(float rMwc, const cubeinfo * pci); extern float se_eq2mwc(float rEq, const cubeinfo * pci); extern char *FormatEval(char *sz, evalsetup * pes); extern cubedecision FindCubeDecision(float arDouble[], float aarOutput[][NUM_ROLLOUT_OUTPUTS], const cubeinfo * pci); EXP_LOCK_FUN(int, GeneralCubeDecisionE, float aarOutput[2][NUM_ROLLOUT_OUTPUTS], const TanBoard anBoard, cubeinfo * const pci, const evalcontext * pec, const evalsetup * pes); EXP_LOCK_FUN(int, GeneralEvaluationE, float arOutput[NUM_ROLLOUT_OUTPUTS], const TanBoard anBoard, cubeinfo * const pci, const evalcontext * pec); extern int cmp_evalsetup(const evalsetup * pes1, const evalsetup * pes2); extern int cmp_evalcontext(const evalcontext * pec1, const evalcontext * pec2); extern char *GetCubeRecommendation(cubedecision cd); extern cubedecision FindBestCubeDecision(float arDouble[], float aarOutput[2][NUM_ROLLOUT_OUTPUTS], const cubeinfo * pci); extern int getCurrentGammonRates(float aarRates[2][2], float arOutput[], const TanBoard anBoard, cubeinfo * pci, const evalcontext * pec); extern void getMoneyPoints(float aaarPoints[2][7][2], int fJacoby, int fBeavers, float aarRates[2][2]); extern void getMatchPoints(float aaarPoints[2][4][2], int afAutoRedouble[2], int afDead[2], const cubeinfo * pci, float aarRates[2][2]); extern void getCubeDecisionOrdering(int aiOrder[3], float arDouble[4], float aarOutput[2][NUM_ROLLOUT_OUTPUTS], const cubeinfo * pci); extern float getPercent(cubedecision cd, const float arDouble[]); extern void RefreshMoveList(movelist * pml, int *ai); EXP_LOCK_FUN(int, ScoreMove, NNState * nnStates, move * pm, const cubeinfo * pci, const evalcontext * pec, int nPlies); extern void CopyMoveList(movelist * pmlDest, const movelist * pmlSrc); extern int isCloseCubedecision(const float arDouble[]); extern int isMissedDouble(float arDouble[], float aarOutput[2][NUM_ROLLOUT_OUTPUTS], int fDouble, const cubeinfo * pci); extern unsigned int locateMove(const TanBoard anBoard, const int anMove[8], const movelist * pml); extern int equal_movefilter(int i, const movefilter amf1[MAX_FILTER_PLIES], const movefilter amf2[MAX_FILTER_PLIES]); extern int equal_movefilters(movefilter aamf1[MAX_FILTER_PLIES][MAX_FILTER_PLIES], movefilter aamf2[MAX_FILTER_PLIES][MAX_FILTER_PLIES]); extern doubletype DoubleType(int fDoubled, int fMove, int fTurn); extern int PerfectCubeful(bearoffcontext * pbc, const TanBoard anBoard, float arEquity[]); extern void baseInputs(const TanBoard anBoard, float arInput[]); extern int CompareMoves(const move * pm0, const move * pm1); extern float EvalEfficiency(const TanBoard anBoard, positionclass pc, int ply); extern float Cl2CfMoney(float arOutput[NUM_OUTPUTS], cubeinfo * pci, float rCubeX); extern float Cl2CfMatch(float arOutput[NUM_OUTPUTS], cubeinfo * pci, float rCubeX); extern float Noise(const evalcontext * pec, const TanBoard anBoard, int iOutput); extern int EvalKey(const evalcontext * pec, int nPlies, const cubeinfo * pci, int fCubefulEquity); extern void MakeCubePos(const cubeinfo aciCubePos[], int cci, int fTop, cubeinfo aci[], int fInvert); extern void GetECF3(float arCubeful[], int cci, float arCf[], cubeinfo aci[]); extern int EvaluatePerfectCubeful(const TanBoard anBoard, float arEquity[], bgvariation bgv); extern neuralnet nnContact, nnRace, nnCrashed; extern neuralnet nnpContact, nnpRace, nnpCrashed; #endif