static void BearOff(int nId, unsigned int nPoints, unsigned short int aOutProb[64], const int fGammon, xhash * ph, bearoffcontext * pbc, const int fCompress, FILE * pfOutput, FILE * pfTmp) { #if !defined(G_DISABLE_ASSERT) int iBest; #endif int iMode, j, anRoll[2], aProb[64]; unsigned int i; TanBoard anBoard, anBoardTemp; movelist ml; int k; unsigned int us; unsigned int usBest; unsigned short int *pusj; unsigned short int ausj[64]; unsigned short int ausBest[32]; unsigned int usGammonBest; unsigned short int ausGammonBest[32]; unsigned int nBack; /* get board for given position */ PositionFromBearoff(anBoard[1], nId, nPoints, 15); /* initialise probabilities */ for (i = 0; i < 64; i++) aProb[i] = aOutProb[i] = 0; /* all chequers off is easy :-) */ if (!nId) { aOutProb[0] = 0xFFFF; aOutProb[32] = 0xFFFF; return; } /* initialise the remainder of the board */ for (i = nPoints; i < 25; i++) anBoard[1][i] = 0; for (i = 0; i < 25; i++) anBoard[0][i] = 0; nBack = 0; for (i = 0, k = 0; i < nPoints; ++i) { if (anBoard[1][i]) nBack = i; k += anBoard[1][i]; } /* look for position in existing bearoff file */ if (pbc && nBack < pbc->nPoints) { unsigned int nPosID = PositionBearoff(anBoard[1], pbc->nPoints, pbc->nChequers); BearoffDist(pbc, nPosID, NULL, NULL, NULL, aOutProb, aOutProb + 32); return; } if (k < 15) aProb[32] = 0xFFFF * 36; for (anRoll[0] = 1; anRoll[0] <= 6; anRoll[0]++) for (anRoll[1] = 1; anRoll[1] <= anRoll[0]; anRoll[1]++) { GenerateMoves(&ml, (ConstTanBoard) anBoard, anRoll[0], anRoll[1], FALSE); usBest = 0xFFFFFFFF; #if !defined(G_DISABLE_ASSERT) iBest = -1; #endif usGammonBest = 0xFFFFFFFF; for (i = 0; i < ml.cMoves; i++) { PositionFromKey(anBoardTemp, &ml.amMoves[i].key); j = PositionBearoff(anBoardTemp[1], nPoints, 15); g_assert(j >= 0); g_assert(j < nId); if (!j) { memset(pusj = ausj, 0, fGammon ? 128 : 64); pusj[0] = 0xFFFF; pusj[32] = 0xFFFF; } else if (!(pusj = XhashLookup(ph, j))) { /* look up in file generated so far */ pusj = ausj; OSLookup(j, nPoints, pusj, fGammon, fCompress, pfOutput, pfTmp); XhashAdd(ph, j, pusj, fGammon ? 128 : 64); } /* find best move to win */ if ((us = RollsOS(pusj)) < usBest) { #if !defined(G_DISABLE_ASSERT) iBest = j; #endif usBest = us; memcpy(ausBest, pusj, 64); } /* find best move to save gammon */ if (fGammon && ((us = RollsOS(pusj + 32)) < usGammonBest)) { usGammonBest = us; memcpy(ausGammonBest, pusj + 32, 64); } } g_assert(iBest >= 0); /* g_assert( iGammonBest >= 0 ); */ if (anRoll[0] == anRoll[1]) { for (i = 0; i < 31; i++) { aProb[i + 1] += ausBest[i]; if (k == 15 && fGammon) aProb[32 + i + 1] += ausGammonBest[i]; } } else { for (i = 0; i < 31; i++) { aProb[i + 1] += 2 * ausBest[i]; if (k == 15 && fGammon) aProb[32 + i + 1] += 2 * ausGammonBest[i]; } } } for (i = 0, j = 0, iMode = 0; i < 32; i++) { j += (aOutProb[i] = (unsigned short) ((aProb[i] + 18) / 36)); if (aOutProb[i] > aOutProb[iMode]) iMode = i; } aOutProb[iMode] -= (j - 0xFFFF); /* gammon probs */ if (fGammon) { for (i = 0, j = 0, iMode = 0; i < 32; i++) { j += (aOutProb[32 + i] = (unsigned short) ((aProb[32 + i] + 18) / 36)); if (aOutProb[32 + i] > aOutProb[32 + iMode]) iMode = i; } aOutProb[32 + iMode] -= (j - 0xFFFF); } }
static void NDBearoff(const int iPos, const unsigned int nPoints, float ar[4], xhash * ph, bearoffcontext * pbc) { int d0, d1; movelist ml; TanBoard anBoard, anBoardTemp; int ii, j, k; unsigned int i; #if !defined(G_DISABLE_ASSERT) int iBest; int iGammonBest; #endif float rBest; float rMean; float rVarSum, rGammonVarSum; float *prj; float arj[4] = { 0.0, 0.0, 0.0, 0.0 }; float arBest[4] = { 0.0, 0.0, 0.0, 0.0 }; float arGammonBest[4] = { 0.0, 0.0, 0.0, 0.0 }; float rGammonBest; for (i = 0; i < 4; ++i) ar[i] = 0.0f; if (!iPos) return; PositionFromBearoff(anBoard[1], iPos, nPoints, 15); for (i = nPoints; i < 25; i++) anBoard[1][i] = 0; /* * look for position in existing bearoff file */ if (pbc) { for (ii = 24; ii >= 0 && !anBoard[1][ii]; --ii); if (ii < (int) pbc->nPoints) { unsigned int nPosID = PositionBearoff(anBoard[1], pbc->nPoints, pbc->nChequers); BearoffDist(pbc, nPosID, NULL, NULL, ar, NULL, NULL); return; } } memset(anBoard[0], 0, 25 * sizeof(int)); for (i = 0, k = 0; i < nPoints; ++i) k += anBoard[1][i]; /* loop over rolls */ rVarSum = rGammonVarSum = 0.0f; for (d0 = 1; d0 <= 6; ++d0) for (d1 = 1; d1 <= d0; ++d1) { GenerateMoves(&ml, (ConstTanBoard) anBoard, d0, d1, FALSE); rBest = 1e10; rGammonBest = 1e10; #if !defined(G_DISABLE_ASSERT) iBest = -1; iGammonBest = -1; #endif for (i = 0; i < ml.cMoves; ++i) { PositionFromKey(anBoardTemp, &ml.amMoves[i].key); j = PositionBearoff(anBoardTemp[1], nPoints, 15); if (!(prj = XhashLookup(ph, j))) { prj = arj; NDBearoff(j, nPoints, prj, ph, pbc); XhashAdd(ph, j, prj, 16); } /* find best move to win */ if (prj[0] < rBest) { #if !defined(G_DISABLE_ASSERT) iBest = j; #endif rBest = prj[0]; memcpy(arBest, prj, 4 * sizeof(float)); } /* find best move to save gammon */ if (prj[2] < rGammonBest) { #if !defined(G_DISABLE_ASSERT) iGammonBest = j; #endif rGammonBest = prj[2]; memcpy(arGammonBest, prj, 4 * sizeof(float)); } } g_assert(iBest >= 0); g_assert(iGammonBest >= 0); rMean = 1.0f + arBest[0]; ar[0] += (d0 == d1) ? rMean : 2.0f * rMean; rMean = arBest[1] * arBest[1] + rMean * rMean; rVarSum += (d0 == d1) ? rMean : 2.0f * rMean; if (k == 15) { rMean = 1.0f + arGammonBest[2]; ar[2] += (d0 == d1) ? rMean : 2.0f * rMean; rMean = arGammonBest[3] * arGammonBest[3] + rMean * rMean; rGammonVarSum += (d0 == d1) ? rMean : 2.0f * rMean; } } ar[0] /= 36.0f; ar[1] = sqrt(rVarSum / 36.0f - ar[0] * ar[0]); ar[2] /= 36.0f; ar[3] = sqrt(rGammonVarSum / 36.0f - ar[2] * ar[2]); }
static void BearOff2(int nUs, int nThem, const int nTSP, const int nTSC, short int asiEquity[4], const int n, const int fCubeful, xhash * ph, bearoffcontext * pbc, FILE * pfTmp) { int j, anRoll[2]; unsigned int i; TanBoard anBoard, anBoardTemp; movelist ml; int aiBest[4]; int asiBest[4]; int aiTotal[4]; short int k; short int *psij; short int asij[4]; const short int EQUITY_P1 = 0x7FFF; const short int EQUITY_M1 = ~EQUITY_P1; if (!nUs) { /* we have won */ asiEquity[0] = asiEquity[1] = asiEquity[2] = asiEquity[3] = EQUITY_P1; return; } if (!nThem) { /* we have lost */ asiEquity[0] = asiEquity[1] = asiEquity[2] = asiEquity[3] = EQUITY_M1; return; } PositionFromBearoff(anBoard[0], nThem, nTSP, nTSC); PositionFromBearoff(anBoard[1], nUs, nTSP, nTSC); for (i = nTSP; i < 25; i++) anBoard[1][i] = anBoard[0][i] = 0; /* look for position in bearoff file */ if (pbc && isBearoff(pbc, (ConstTanBoard) anBoard)) { unsigned short int nUsL = PositionBearoff(anBoard[1], pbc->nPoints, pbc->nChequers); unsigned short int nThemL = PositionBearoff(anBoard[0], pbc->nPoints, pbc->nChequers); int nL = Combination(pbc->nPoints + pbc->nChequers, pbc->nPoints); unsigned int iPos = nUsL * nL + nThemL; unsigned short int aus[4]; BearoffCubeful(pbc, iPos, NULL, aus); for (i = 0; i < 4; ++i) asiEquity[i] = aus[i] - 0x8000; return; } aiTotal[0] = aiTotal[1] = aiTotal[2] = aiTotal[3] = 0; for (anRoll[0] = 1; anRoll[0] <= 6; anRoll[0]++) for (anRoll[1] = 1; anRoll[1] <= anRoll[0]; anRoll[1]++) { GenerateMoves(&ml, (ConstTanBoard) anBoard, anRoll[0], anRoll[1], FALSE); asiBest[0] = asiBest[1] = asiBest[2] = asiBest[3] = -0xFFFF; aiBest[0] = aiBest[1] = aiBest[2] = aiBest[3] = -1; for (i = 0; i < ml.cMoves; i++) { PositionFromKey(anBoardTemp, &ml.amMoves[i].key); j = PositionBearoff(anBoardTemp[1], nTSP, nTSC); g_assert(j >= 0); g_assert(j < nUs); if (!nThem) { asij[0] = asij[1] = asij[2] = asij[3] = EQUITY_P1; } else if (!j) { asij[0] = asij[1] = asij[2] = asij[3] = EQUITY_M1; } if (!(psij = XhashLookup(ph, n * nThem + j))) { /* lookup in file */ psij = asij; TSLookup(nThem, j, nTSP, nTSC, psij, n, fCubeful, pfTmp); XhashAdd(ph, n * nThem + j, psij, fCubeful ? 8 : 2); } /* cubeless */ if (psij[0] < -asiBest[0]) { aiBest[0] = j; asiBest[0] = ~psij[0]; } if (fCubeful) { /* I own cube: * from opponent's view he doesn't own cube */ if (psij[3] < -asiBest[1]) { aiBest[1] = j; asiBest[1] = ~psij[3]; } /* Centered cube (so centered for opponent too) */ k = CubeEquity(psij[2], psij[3], EQUITY_P1); if (~k > asiBest[2]) { aiBest[2] = j; asiBest[2] = ~k; } /* Opponent owns cube: * from opponent's view he owns cube */ k = CubeEquity(psij[1], psij[3], EQUITY_P1); if (~k > asiBest[3]) { aiBest[3] = j; asiBest[3] = ~k; } } } g_assert(aiBest[0] >= 0); g_assert(!fCubeful || aiBest[1] >= 0); g_assert(!fCubeful || aiBest[2] >= 0); g_assert(!fCubeful || aiBest[3] >= 0); if (anRoll[0] == anRoll[1]) for (k = 0; k < (fCubeful ? 4 : 1); ++k) aiTotal[k] += asiBest[k]; else for (k = 0; k < (fCubeful ? 4 : 1); ++k) aiTotal[k] += 2 * asiBest[k]; } /* final equities */ for (k = 0; k < (fCubeful ? 4 : 1); ++k) asiEquity[k] = aiTotal[k] / 36; }
static void HyperEquity ( const int nUs, const int nThem, hyperequity *phe, const int nC, const hyperequity aheOld[], float arNorm[] ) { TanBoard anBoard; TanBoard anBoardTemp; movelist ml; int i, j; unsigned int k; int nUsNew, nThemNew; hyperequity heBest; hyperequity heOld; hyperequity heNew; int nPos = Combination ( 25 + nC, nC ); const hyperequity *phex; float r; hyperclass hc; /* save old hyper equity */ memcpy ( &heOld, phe, sizeof ( hyperequity ) ); /* generate board for position */ PositionFromBearoff ( anBoard[ 0 ], nThem, 25, nC ); PositionFromBearoff ( anBoard[ 1 ], nUs, 25, nC ); switch ( ( hc = ClassifyHyper ( anBoard ) ) ) { case HYPER_OVER: HyperOver ( (ConstTanBoard)anBoard, phe->arOutput, nC ); for ( k = 0; k < 5; ++k ) phe->arEquity[ k ] = Utility ( phe->arOutput, &ci ); /* special calc for Jacoby rule */ phe->arEquity[ EQUITY_CENTER_JACOBY ] = Utility ( phe->arOutput, &ciJacoby ); return; break; case HYPER_ILLEGAL: return; break; case HYPER_BEAROFF: case HYPER_CONTACT: for ( k = 0; k < NUM_OUTPUTS; ++k ) heNew.arOutput[ k ] = 0.0f; for ( k = 0; k < 5; ++k ) heNew.arEquity[ k ] = 0.0f; for ( i = 1; i <= 6; ++i ) for ( j = 1; j <= i; ++j ) { GenerateMoves ( &ml, (ConstTanBoard)anBoard, i, j, FALSE ); if ( ml.cMoves ) { /* at least one legal move: find the equity of the best move */ for ( k = 0; k < 5; ++k ) heBest.arEquity[ k ] = -10000.0f; for ( k = 0; k < ml.cMoves; ++k ) { PositionFromKey ( anBoardTemp, &ml.amMoves[ k ].key ); nUsNew = PositionBearoff( anBoardTemp[ 1 ], 25, nC ); nThemNew = PositionBearoff( anBoardTemp[ 0 ], 25, nC ); g_assert ( nUsNew >= 0 ); g_assert ( nUsNew < nUs ); g_assert ( nThemNew >= 0 ); /* cubeless */ phex = &aheOld[ nPos * nThemNew + nUsNew ]; r = - phex->arEquity[ EQUITY_CUBELESS ]; if ( r >= heBest.arEquity[ EQUITY_CUBELESS ] ) { memcpy ( heBest.arOutput, phex->arOutput, NUM_OUTPUTS * sizeof ( float ) ); InvertEvaluation ( heBest.arOutput ); heBest.arEquity[ EQUITY_CUBELESS ] = r; } /* I own cube: from opponent's view he doesn't own cube */ r = - phex->arEquity[ EQUITY_OPPONENT ]; if ( r >= heBest.arEquity[ EQUITY_OWNED ] ) heBest.arEquity[ EQUITY_OWNED ] = r; /* Centered cube, i.e., centered for opponent too */ r = - CubeEquity ( phex->arEquity[ EQUITY_CENTER ], phex->arEquity[ EQUITY_OPPONENT ], +1.0 ); if ( r >= heBest.arEquity[ EQUITY_CENTER ] ) heBest.arEquity[ EQUITY_CENTER ] = r; /* centered cube with Jacoby rule */ r = - CubeEquity ( phex->arEquity[ EQUITY_CENTER_JACOBY ], phex->arEquity[ EQUITY_OPPONENT ], +1.0 ); if ( r >= heBest.arEquity[ EQUITY_CENTER_JACOBY ] ) heBest.arEquity[ EQUITY_CENTER_JACOBY ] = r; /* Opponent owns cube: from opponent's view he owns cube */ r = - CubeEquity ( phex->arEquity[ EQUITY_OWNED ], phex->arEquity[ EQUITY_OPPONENT ], +1.0 ); if ( r >= heBest.arEquity[ EQUITY_OPPONENT ] ) heBest.arEquity[ EQUITY_OPPONENT ] = r; } } else { /* no legal moves: equity is minus the equity of the reverse position, which has the opponent on roll */ memcpy( &heBest, &aheOld[ nPos * nThem + nUs ], sizeof ( hyperequity ) ); InvertEvaluation ( heBest.arOutput ); for ( k = 0; k < 5; ++k ) heBest.arEquity[ k ] = - heBest.arEquity[ k ]; } /* sum up equities */ for ( k = 0; k < NUM_OUTPUTS; ++k ) heNew.arOutput[ k ] += ( i == j ) ? heBest.arOutput[ k ] : 2.0 * heBest.arOutput[ k ]; for ( k = 0; k < 5; ++k ) heNew.arEquity[ k ] += ( i == j ) ? heBest.arEquity[ k ] : 2.0 * heBest.arEquity[ k ]; } /* normalise */ for ( k = 0; k < NUM_OUTPUTS; ++k ) phe->arOutput[ k ] = heNew.arOutput[ k ] / 36.0; for ( k = 0; k < 5; ++k ) phe->arEquity[ k ] = heNew.arEquity[ k ] / 36.0; break; } /* calculate contribution to norm */ for ( k = 0; k < NUM_OUTPUTS; ++k ) { r = fabsf ( phe->arOutput[ k ] - heOld.arOutput[ k ] ); if ( r > arNorm[ k ] ) { arNorm[ k ] = r; aiNorm[ k ] = nUs * nPos + nThem; } } for ( k = 0; k < 5; ++k ) { r = fabsf( phe->arEquity[ k ] - heOld.arEquity[ k ] ); if ( r > arNorm[ 5 + k ] ) { arNorm[ 5 + k ] = r; aiNorm[ 5 + k ] = nUs * nPos + nThem; } } }