summaryrefslogtreecommitdiff
path: root/osr.c
diff options
context:
space:
mode:
authorRuss Allbery <rra@debian.org>2008-02-16 10:41:04 -0800
committerRuss Allbery <rra@debian.org>2008-02-16 10:41:04 -0800
commit4258edd6a0fcb6cd568413984eff5731829897b2 (patch)
treef215b95d6b53e3b8af77a642e80afa9aa1e7cbaf /osr.c
Imported upstream version 0.16~20080216
Diffstat (limited to 'osr.c')
-rw-r--r--osr.c908
1 files changed, 908 insertions, 0 deletions
diff --git a/osr.c b/osr.c
new file mode 100644
index 0000000..4d294c4
--- /dev/null
+++ b/osr.c
@@ -0,0 +1,908 @@
+
+/*
+ * osr.c
+ *
+ * by Jørn Thyssen <jthyssen@dk.ibm.com>, 2002.
+ * (after inspiration from osr.cc from fibs2html <fibs2html.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 3 or later of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id: osr.c,v 1.29 2008/02/07 22:29:36 Superfly_Jon Exp $
+ */
+
+#include <stdio.h>
+#include <glib.h>
+#include <string.h>
+
+
+#include "config.h"
+
+#include "backgammon.h"
+#include "positionid.h"
+#include "osr.h"
+#include "mt19937ar.h"
+
+#define MAX_PROBS 32
+#define MAX_GAMMON_PROBS 15
+
+static unsigned long mt[ N ];
+static int mti = N + 1;
+
+static int
+OSRQuasiRandomDice( const int iTurn, const int iGame, const int cGames,
+ int anDice[ 2 ] ) {
+
+ if( !iTurn && !( cGames % 36 ) ) {
+ anDice[ 0 ] = ( iGame % 6 ) + 1;
+ anDice[ 1 ] = ( ( iGame / 6 ) % 6 ) + 1;
+ return 0;
+ } else if( iTurn == 1 && !( cGames % 1296 ) ) {
+ anDice[ 0 ] = ( ( iGame / 36 ) % 6 ) + 1;
+ anDice[ 1 ] = ( ( iGame / 216 ) % 6 ) + 1;
+ return 0;
+ } else {
+ anDice[ 0 ] = ( genrand_int32(&mti,mt) % 6 ) + 1;
+ anDice[ 1 ] = ( genrand_int32(&mti,mt) % 6 ) + 1;
+ return ( anDice[ 0 ] > 0 && anDice[ 1 ] > 0 );
+ }
+}
+
+/* Fill aaProb with one sided bearoff probabilities for position with */
+/* bearoff id n. */
+
+static inline void getBearoffProbs(const unsigned int n, unsigned short int aaProb[32])
+{
+ g_assert( pbc1 );
+ if (BearoffDist ( pbc1, n, NULL, NULL, NULL, aaProb, NULL ))
+ puts("BearoffDist failed?");
+}
+
+
+static int
+isCrossOver ( const int from, const int to ) {
+ return ( from / 6 ) != ( to / 6 );
+}
+
+
+/*
+ * Find (and move) best move in one side rollout.
+ *
+ * Input
+ * anBoard: the board (reversed compared to normal convention)
+ * anDice: the roll (dice 1 <> dice 2)
+ * pnOut: current number of chequers outside home quadrant
+ *
+ * Output:
+ * anBoard: the updated board after the move
+ * pnOut: the number of chequers outside home quadrant after move
+ *
+ */
+
+static unsigned int chequersout(const unsigned int anBoard[25])
+{
+ unsigned int i, n = 0;
+
+ for (i = 6; i < 25; i++)
+ n += anBoard[i];
+
+ return n;
+}
+
+static int checkboard(const unsigned int anBoard[25])
+{
+ unsigned int i, c = 0;
+
+ for (i = 0; i < 25; i++)
+ c += anBoard[ i ];
+
+ return (c == 15);
+}
+
+static void FindBestMoveOSR2 ( unsigned int anBoard[ 25 ], const int anDice[ 2 ], unsigned int *pnOut )
+{
+ int ifar, inear, iboth;
+ int iused = 0;
+ int i, j, lc;
+ int found;
+
+ ifar = 5 + anDice[ 0 ];
+ inear = 5 + anDice[ 1 ];
+
+ if ( anBoard[ ifar ] && anBoard[ inear ] ) {
+
+ /* two chequers move exactly into the home quadrant */
+
+ /* move chequers */
+
+ --anBoard[ ifar ];
+ --anBoard[ inear ];
+ g_assert ( checkboard ( anBoard ) );
+ anBoard[ 5 ] += 2;
+
+ g_assert(*pnOut >= 2);
+ *pnOut -= 2;
+ g_assert ( *pnOut == chequersout ( anBoard ) );
+
+ return;
+ }
+
+ iboth = 5 + ( anDice[ 0 ] + anDice[ 1 ] );
+
+ if ( anBoard[ iboth ] ) {
+
+ /* one chequer move exactly into the home quadrant */
+
+ /* move chequer */
+
+ --anBoard[ iboth ];
+ ++anBoard[ 5 ];
+ g_assert ( checkboard ( anBoard ) );
+
+ g_assert ( *pnOut > 0 );
+ --*pnOut;
+ g_assert ( *pnOut == chequersout ( anBoard ) );
+
+ return;
+
+ }
+
+
+ /* loop through dice */
+
+ for ( i = 0; i < 2 && *pnOut; ++i ) {
+
+ /* check for exact cross over */
+
+ if ( anBoard[ 5 + anDice[ i ] ] ) {
+
+ /* move chequer */
+
+ --anBoard[ 5 + anDice[ i ] ];
+ ++anBoard[ 5 ];
+ g_assert ( checkboard ( anBoard ) );
+
+ g_assert ( *pnOut > 0 );
+ --*pnOut;
+ g_assert ( *pnOut == chequersout ( anBoard ) );
+
+ ++iused;
+
+ /* next die */
+
+ continue;
+
+ }
+
+ /* find chequer furthest away */
+
+ lc = 24;
+ while ( lc > 5 && ! anBoard[ lc ] )
+ --lc;
+
+ /* try to make cross over from the back */
+
+ found = FALSE;
+ for ( j = lc; j - anDice[ i ] >5; --j ) {
+
+ if ( anBoard[ j ] && isCrossOver ( j, j - anDice[ i ] ) ) {
+
+ /* move chequer */
+
+ --anBoard[ j ];
+ ++anBoard[ j - anDice[ i ] ];
+ g_assert ( checkboard ( anBoard ) );
+ ++iused;
+ found = TRUE;
+ /* FIXME: increment lc if needed */
+
+ break;
+
+ }
+
+ }
+
+
+ if ( ! found ) {
+
+ /* no move with cross-over was found */
+
+ /* move chequer from the rear */
+
+ for ( j = lc; j > 5; --j ) {
+
+ if ( anBoard[ j ] ) {
+
+ --anBoard[ j ];
+ ++anBoard[ j - anDice[ i ] ];
+ g_assert ( checkboard ( anBoard ) );
+ ++iused;
+
+ if ( j - anDice[ i ] < 6 )
+ { /* we've moved inside home quadrant */
+ g_assert ( *pnOut > 0 );
+ --*pnOut;
+ }
+
+ g_assert ( *pnOut == chequersout ( anBoard ) );
+
+ break;
+
+ }
+
+ }
+
+
+ }
+
+ }
+
+ if ( ! *pnOut && iused < 2 ) {
+
+ /* die 2 still left, and all chequers inside home quadrant */
+
+ if ( anBoard[ anDice[ 1 ] - 1 ] ) {
+ /* bear-off */
+ --anBoard[ anDice[ 1 ] - 1 ];
+ g_assert ( checkboard ( anBoard ) );
+ return;
+ }
+
+ /* try filling rearest empty space */
+
+ for ( j = 5; j - anDice[ 1 ] > -1; --j ) {
+
+ if ( anBoard[ j ] && ! anBoard[ j - anDice[ 1 ] ] ) {
+ /* empty space found */
+
+ --anBoard[ j ];
+ ++anBoard[ j - anDice[ 1 ] ];
+
+ g_assert ( checkboard ( anBoard ) );
+ return;
+
+ }
+ }
+
+ /* move chequer from the rear */
+
+ for ( j = 5; j > -1; --j ) {
+
+ if ( anBoard[ j ] ) {
+
+ /* move chequer from the rear */
+
+ --anBoard[ j ];
+
+ if ( j - anDice[ 1 ] > -1 )
+ /* add chequer to point */
+ ++anBoard[ j - anDice[ 1 ] ];
+ /* else */
+ /* bearoff */
+
+ g_assert ( checkboard ( anBoard ) );
+ return;
+
+ }
+
+ }
+
+ }
+
+ g_assert ( iused == 2 );
+ return;
+
+}
+
+/*
+ * Find (and move) best move in one side rollout.
+ *
+ * Input
+ * anBoard: the board
+ * anDice: the roll (dice 1 = dice 2)
+ * pnOut: current number of chequers outside home quadrant
+ *
+ * Output:
+ * anBoard: the updated board after the move
+ * pnOut: the number of chequers outside home quadrant after move
+ *
+ */
+
+static void FindBestMoveOSR4(unsigned int anBoard[ 25 ], const int nDice, unsigned int *pnOut)
+{
+ int nd = 4;
+ int i, n = 0;
+ int first, any;
+ int lc;
+
+ /* check for exact bear-ins */
+
+ while ( nd > 0 && *pnOut > 0 && anBoard[ 5 + nDice ] )
+ {
+ --anBoard[ 5 + nDice ];
+ ++anBoard[ 5 ];
+ g_assert ( checkboard ( anBoard ) );
+
+ --nd;
+ g_assert ( *pnOut > 0 );
+ --*pnOut;
+ g_assert ( *pnOut == chequersout ( anBoard ) );
+
+ }
+
+ if (*pnOut > 0)
+ {
+ /* check for 4, 3, or 2 chequers move exactly into home quadrant */
+
+ for ( n = nd; n > 1; --n ) {
+
+ i = 5 + n * nDice;
+
+ if ( i < 25 && anBoard[ i ] ) {
+
+ --anBoard[ i ];
+ ++anBoard[ 5 ];
+ g_assert ( checkboard ( anBoard ) );
+
+ nd -= n;
+ g_assert (*pnOut > 0);
+ --*pnOut;
+ g_assert ( *pnOut == chequersout ( anBoard ) );
+
+ n =nd; /* restart loop */
+ }
+ }
+ }
+
+ if ( *pnOut > 0 && nd > 0 )
+ {
+ first = TRUE;
+
+ /* find rearest chequer */
+
+ lc = 24;
+ while ( lc > 5 && ! anBoard[ lc ] )
+ --lc;
+
+ /* try to make cross over from the back */
+
+ for ( i = lc; i > 5; --i ) {
+
+ if ( anBoard[ i ] ) {
+
+ if ( isCrossOver ( i, i - nDice ) && ( first || (i - nDice) > 5 ) ) {
+
+ /* move chequers */
+
+ while ( anBoard[ i ] && nd && *pnOut )
+ {
+ --anBoard[ i ];
+ ++anBoard[ i - nDice ];
+ g_assert ( checkboard ( anBoard ) );
+
+ if ( i - nDice < 6 )
+ { /* we move into homeland */
+ g_assert ( *pnOut > 0 );
+ --*pnOut;
+ }
+
+
+ g_assert ( *pnOut == chequersout ( anBoard ) );
+
+ --nd;
+ }
+
+ if ( ! *pnOut || ! nd )
+ break;
+
+ /* did we move all chequers from that point */
+
+ first = ! anBoard[ i ];
+ }
+ }
+ }
+
+ /* move chequers from the rear */
+
+ while ( *pnOut && nd )
+ {
+ for ( i = lc; i > 5; --i )
+ {
+ if( anBoard[ i ] )
+ {
+ while ( anBoard[ i ] && nd && *pnOut )
+ {
+ --anBoard[ i ];
+ ++anBoard[ i - nDice ];
+ g_assert ( checkboard ( anBoard ) );
+
+ if ( i - nDice < 6 )
+ {
+ g_assert ( *pnOut > 0 );
+ --*pnOut;
+ }
+
+ g_assert ( *pnOut == chequersout ( anBoard ) );
+
+ --nd;
+ }
+
+ if ( ! n || ! *pnOut )
+ break;
+ }
+ }
+ }
+ }
+
+ if (!*pnOut )
+ { /* all chequers inside home quadrant */
+ while ( nd )
+ {
+ if ( anBoard[ nDice - 1 ] ) {
+ /* perfect bear-off */
+ --anBoard[ nDice - 1 ];
+ --nd;
+ g_assert ( checkboard ( anBoard ) );
+ continue;
+ }
+
+ if ( nd >= 2 && nDice <= 3 && anBoard[ 2 *nDice - 1 ] > 0 ) {
+ /* bear double 1s, 2s, and 3s off, e.g., 4/2/0 */
+ --anBoard[ 2 * nDice - 1 ];
+ nd -= 2;
+ g_assert ( checkboard ( anBoard ) );
+ continue;
+ }
+
+ if ( nd >= 3 && nDice <= 2 && anBoard[ 3 * nDice - 1 ] > 0 ) {
+ /* bear double 1s off from 3 point (3/2/1/0) or
+ double 2s off from 6 point (6/4/2/0) */
+ --anBoard[ 3 * nDice - 1 ];
+ nd -= 3;
+ g_assert ( checkboard ( anBoard ) );
+ continue;
+ }
+
+ if ( nd >= 4 && nDice <= 1 && anBoard[ 4 * nDice - 1] > 0 ) {
+ /* hmmm, this should not be possible... */
+ /* bear off double 1s: 4/3/2/1/0 */
+ --anBoard[ 4 * nDice - 1 ];
+ g_assert ( checkboard ( anBoard ) );
+ nd -= 4;
+ }
+
+ any = FALSE;
+
+ /* move chequers from rear */
+
+ /* FIXME: fill gaps? */
+
+ for ( i = 5; nd && i > -1; --i ) {
+
+ while ( anBoard[ i ] && nd ) {
+
+ any = TRUE;
+
+ --anBoard[ i ];
+ --nd;
+
+ if ( i - nDice > -1 )
+ ++anBoard[ i - nDice ];
+ g_assert ( checkboard ( anBoard ) );
+
+ }
+
+ }
+
+ if ( ! any )
+ /* no more chequers left */
+ nd = 0;
+ }
+ }
+}
+
+
+/*
+ * Find (and move) best move in one sided rollout.
+ *
+ * Input
+ * anBoard: the board
+ * anDice: the roll (dice 1 is assumed to be lower than dice 2)
+ * pnOut: current number of chequers outside home quadrant
+ *
+ * Output:
+ * anBoard: the updated board after the move
+ * pnOut: the number of chequers outside home quadrant after move
+ *
+ */
+
+static void FindBestMoveOSR(unsigned int anBoard[ 25 ], const int anDice[ 2 ], unsigned int *pnOut)
+{
+ if (anDice[0] != anDice[1])
+ FindBestMoveOSR2(anBoard, anDice, pnOut);
+ else
+ FindBestMoveOSR4(anBoard, anDice[0], pnOut);
+}
+
+/*
+ * osr: one sided rollout.
+ *
+ * Input:
+ * anBoard: the board (reversed compared to normal convention)
+ * iGame: game#
+ * nGames: # of games.
+ * nOut: current number of chequers outside home quadrant.
+ *
+ * Returns: number of rolls used to get all chequers inside home
+ * quadrant.
+ */
+
+static int osr(unsigned int anBoard[25], const int iGame, const int nGames, unsigned int nOut)
+{
+ int iTurn = 0;
+ int anDice[ 2 ];
+
+ /* loop until all chequers are in home quadrant */
+
+ while (nOut)
+ {
+ /* roll dice */
+ if ( OSRQuasiRandomDice ( iTurn, iGame, nGames, anDice ) < 0 )
+ return -1;
+
+ if ( anDice[ 0 ] < anDice[ 1 ] )
+ swap ( anDice, anDice + 1 );
+
+ /* find and move best move */
+ FindBestMoveOSR(anBoard, anDice, &nOut);
+
+ iTurn++;
+ }
+
+ return iTurn;
+}
+
+
+/*
+ * RollOSR: perform onesided rollout
+ *
+ * Input:
+ * nGames: number of simulations
+ * anBoard: the board
+ * nOut: number of chequers outside home quadrant
+ *
+ * Output:
+ * arProbs[ nMaxProbs ]: probabilities
+ * arGammonProbs[ nMaxGammonProbs ]: gammon probabilities
+ *
+ */
+
+static void
+rollOSR ( const int nGames, const unsigned int anBoard[ 25 ], const int nOut,
+ float arProbs[], const unsigned int nMaxProbs,
+ float arGammonProbs[], const unsigned int nMaxGammonProbs ) {
+
+ unsigned int an[ 25 ];
+ unsigned short int anProb[ 32 ];
+ unsigned int i;
+ int n, m;
+ int iGame;
+
+ int *anCounts = (int*) g_alloca(nMaxGammonProbs * sizeof(int));
+
+ memset(anCounts, 0, sizeof(int) * nMaxGammonProbs);
+
+ for ( i = 0; i < nMaxProbs; ++i )
+ arProbs[ i ] = 0.0f;
+
+ /* perform rollouts */
+
+ for ( iGame = 0; iGame < nGames; ++iGame ) {
+
+ memcpy ( an, anBoard, sizeof ( an ) );
+
+ /* do actual rollout */
+
+ n = osr ( an, iGame, nGames, nOut );
+
+ /* number of chequers in home quadrant */
+
+ m = 0;
+ for ( i = 0; i < 6; ++i )
+ m += an[ i ];
+
+ /* update counts */
+
+ ++anCounts[ MIN( m == 15 ? n + 1 : n, (int)nMaxGammonProbs - 1 ) ];
+
+ /* get prob. from bearoff1 */
+
+ getBearoffProbs ( PositionBearoff ( an, 6, 15 ), anProb );
+
+ for ( i = 0; i < 32; ++i )
+ arProbs[ MIN( n + i, nMaxProbs - 1 ) ] += anProb[ i ] / 65535.0f;
+
+ }
+
+ /* scale resulting probabilities */
+
+ for ( i = 0; i < (unsigned int)nMaxProbs; ++i ) {
+ arProbs[ i ] /= nGames;
+ /* printf ( "arProbs[%d]=%f\n", i, arProbs[ i ] ); */
+ }
+
+ /* calculate gammon probs.
+ (prob. of getting inside home quadrant in i rolls */
+
+ for ( i = 0; i < nMaxGammonProbs; ++i ) {
+ arGammonProbs[ i ] = 1.0f * anCounts[ i ] / nGames;
+ /* printf ( "arGammonProbs[%d]=%f\n", i, arGammonProbs[ i ] ); */
+ }
+
+}
+
+
+
+/*
+ * OSP: one sided probabilities
+ *
+ * Input:
+ * anBoard: one side of the board
+ * nGames: number of simulations
+ *
+ * Output:
+ * an: ???
+ * arProb: array of probabilities (ar[ i ] is the prob. that player
+ * bears off in i moves)
+ * arGammonProb: gammon probabilities
+ *
+ */
+
+static int
+osp ( const unsigned int anBoard[ 25 ], const int nGames,
+ unsigned int an[ 25 ], float arProbs[ MAX_PROBS ],
+ float arGammonProbs[ MAX_GAMMON_PROBS ] ) {
+
+ int i, n;
+ int nTotal, nOut;
+ unsigned short int anProb[ 32 ];
+
+ /* copy board into an, and find total number of chequers left,
+ and number of chequers outside home */
+
+ nTotal = nOut = 0;
+
+ memcpy( an, anBoard, 25 * sizeof( int ) );
+
+ for ( i = 0; i < 25; ++i ) {
+ /* total number of chequers left */
+ nTotal += anBoard[ i ];
+ if ( i > 5 )
+ nOut += anBoard[ i ];
+ }
+
+
+ if ( nOut > 0 )
+ /* chequers outside home: do one sided rollout */
+ rollOSR ( nGames, an, nOut,
+ arProbs, MAX_PROBS,
+ arGammonProbs, MAX_GAMMON_PROBS );
+ else {
+ /* chequers inde home: use BEAROFF2 */
+
+ /* no gammon possible */
+ for ( i = 0; i < MAX_GAMMON_PROBS; ++i )
+ arGammonProbs[ i ] = 0.0f;
+
+ if ( nTotal == 15 )
+ arGammonProbs[ 1 ] = 1.0f;
+ else
+ arGammonProbs[ 0 ] = 1.0f;
+
+ /* get probs from BEAROFF2 */
+
+ for ( i = 0; i < MAX_PROBS; ++i )
+ arProbs[ i ] = 0.0f;
+
+ getBearoffProbs ( PositionBearoff ( anBoard, 6, 15 ), anProb );
+
+ for ( i = 0; i < 32; ++i ) {
+ n = MIN( i, MAX_PROBS - 1 );
+ arProbs[ n ] += anProb[ i ] / 65535.0f;
+ /* printf ( "arProbs[%d]=%f\n", n, arProbs[n] ); */
+ }
+
+ }
+
+ return nTotal;
+
+}
+
+
+static float
+bgProb ( const unsigned int anBoard[ 25 ],
+ const int fOnRoll,
+ const int nTotal,
+ const float arProbs[],
+ const int nMaxProbs ) {
+
+ int nTotPipsHome = 0;
+ int i, j;
+ float r, s;
+ unsigned short int anProb[ 32 ];
+
+ /* total pips before out of opponent's home quadrant */
+
+ for ( i = 18; i < 25; ++i )
+ nTotPipsHome += anBoard[ i ] * ( i - 17 );
+
+ r = 0.0f;
+
+ if ( nTotPipsHome ) {
+
+ /* ( nTotal + 3 ) / 4 - 1: number of rolls before opponent is off. */
+ /* (nTotPipsHome + 2) / 3: numbers of rolls before I'm out of
+ opponent's home quadrant (with consequtive 2-1's) */
+
+ if ( ( nTotal + 3 ) / 4 - 1 <= ( nTotPipsHome + 2 ) / 3 ) {
+
+ /* backgammon is possible */
+
+ /* get "bear-off" prob (for getting out of opp.'s home quadr.) */
+
+ /* FIXME: this ignores chequers on the bar */
+
+ getBearoffProbs ( PositionBearoff ( anBoard + 18, 6, 15 ), anProb );
+
+ for ( i = 0; i < nMaxProbs; ++i ) {
+
+ if ( arProbs[ i ] > 0.0f ) {
+
+ s = 0.0f;
+
+ for ( j = i + ! fOnRoll; j < 32; ++j )
+ s += anProb[ j ] / 65535.0f;
+
+ r += s * arProbs[ i ];
+
+ }
+
+ }
+
+ }
+
+ }
+
+ return r;
+
+}
+
+
+/*
+ * Calculate race probabilities using one sided rollouts.
+ *
+ * Input:
+ * anBoard: the current board
+ * (assumed to be a race position without contact)
+ * nGames: the number of simulations to perform
+ *
+ * Output:
+ * arOutput: probabilities.
+ *
+ */
+
+extern void
+raceProbs ( const TanBoard anBoard, const int nGames,
+ float arOutput[ NUM_OUTPUTS ],
+ float arMu[ 2 ] ) {
+
+ TanBoard an;
+ float aarProbs[ 2 ][ MAX_PROBS ];
+ float aarGammonProbs[ 2 ][ MAX_PROBS ];
+ float arG[ 2 ], arBG[ 2 ];
+
+ int anTotal[ 2 ];
+
+ int i, j, k;
+
+ float w, s;
+
+ /* Seed set to ensure that OSR are reproducable */
+
+ init_genrand ( 0, &mti, mt );
+
+ for ( i = 0; i < 5; ++i )
+ arOutput[ i ] = 0.0f;
+
+ for ( i = 0; i < 2; ++i )
+ anTotal[ i ] =
+ osp ( anBoard[ i ], nGames, an[ i ],
+ aarProbs[ i ], aarGammonProbs[ i ] );
+
+ /* calculate OUTPUT_WIN */
+
+ w = 0;
+
+ for ( i = 0; i < MAX_PROBS; ++i ) {
+
+ /* calculate the prob. of the opponent using more than i rolls
+ to bear off */
+
+ s = 0.0f;
+ for ( j = i; j < MAX_PROBS; ++j )
+ s += aarProbs[ 0 ][ j ];
+
+ /* winning chance is: prob. of me bearing off in i rolls times
+ prob. the opponent doesn't bear off in i rolls */
+
+ w += aarProbs[ 1 ][ i ] * s;
+
+ }
+
+ arOutput[ OUTPUT_WIN ] = MIN( w, 1.0f );
+
+ /* calculate gammon and backgammon probs */
+
+ for ( i = 0; i < 2; ++i ) {
+
+ arG[ i ] = 0.0f;
+ arBG[ i ] = 0.0f;
+
+ if ( anTotal[ ! i ] == 15 ) {
+
+ /* gammon and backgammon possible */
+
+ for ( j = 0; j < MAX_GAMMON_PROBS; ++j ) {
+
+ /* chance of opponent having borne all chequers of
+ within j rolls */
+
+ s = 0.0f;
+ for ( k = 0; k < j + i; ++k )
+ s += aarProbs[ i ][ k ];
+
+ /* gammon chance */
+
+ arG[ i ] += aarGammonProbs[ ! i ][ j ] * s;
+
+ }
+
+ if ( arG[ i ] > 0.0f )
+ /* calculate backgammon probs */
+ arBG[ i ] = bgProb ( an[ ! i ], i, anTotal[ i ],
+ aarProbs[ i ], MAX_PROBS );
+
+ }
+
+ }
+
+ arOutput[ OUTPUT_WINGAMMON ] = MIN( arG[ 1 ], 1.0f );
+ arOutput[ OUTPUT_LOSEGAMMON ] = MIN( arG[ 0 ], 1.0f );
+ arOutput[ OUTPUT_WINBACKGAMMON ] = MIN( arBG[ 1 ], 1.0f );
+ arOutput[ OUTPUT_LOSEBACKGAMMON ] = MIN( arBG[ 0 ], 1.0f );
+
+ /* calculate average number of rolls to bear off */
+
+ if ( arMu ) {
+
+
+ for ( i = 0; i < 2; ++i ) {
+ arMu[ i ] = 0.0f;
+ for ( j = 0; j < MAX_PROBS; ++j )
+ arMu[ i ] += 1.0f * j * aarProbs[ i ][ j ];
+
+ }
+
+ }
+
+}