bug-gnubg
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Bug-gnubg] Playing with Nis' algorithm for rollout dice


From: Joseph Heled
Subject: Re: [Bug-gnubg] Playing with Nis' algorithm for rollout dice
Date: Mon, 04 Nov 2002 06:14:04 +1300
User-agent: Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.1) Gecko/20020829


Jim Segrave wrote:

Notes:
It uses random() and expects srandom() to be called when setting up
for a rollout. rand()/srand() would work, but don't give as nice a
psuedo distribution. I don't know if random()/srandom() are standard
library functions.

The GNUbg randomizer is settable, see dice.h. You should use it to determine what to call.

-Joseph



It uses a caller supplied struct, so if someone thinks of a reason to
run multiple copies of this simultaneously, it will work.




------------------------------------------------------------------------

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <time.h>

#define ARRAY_LEN   (36 * 36 * 36)

typedef struct diceroll_s {
  int             fReady;           /* TRUE if initialised */
  int             fInitial;         /* TRUE if rollout of initial position */
  int             count;            /* used for getting first roll */
  unsigned short  first[36];        /* sequence of turn 0 dice */
  int             last;             /* wrap when we get to here */
  int             indices[36];      /* indices for 2nd & later rolls */
  unsigned short  rolls[ARRAY_LEN]; /* packed sequence of turns 1..3 */
  void   (*randfunc)(int *);        /* ptr to function returning a roll  */
} diceroll;

static void SwapRolls (unsigned short *a, unsigned short *b) {
  unsigned short c = *a;
*a = *b; *b = c;
}

/* set up the first rolls array and randomise it. Then set up the indices
 * for second/third/fourth rolls and randomise them as well
 */
static void SetFirstRolls (diceroll *pDiceRoll) { int i, save, swap;
  unsigned short *p = pDiceRoll->first;
  int            *ind = pDiceRoll->indices;

  for (i = 0; i < 36; ++i) {
      *p++ = i;
      /* the indices will point to a sub-array. The -1 is a
       *     setup for the first call to GetRoll
       */
      *ind++ = i *  1296 - 1;
  }
p = pDiceRoll->first; for (i = 0; i < 36; ++i) SwapRolls (p + i, p + (random () % 36));

  /* Randomise the indices */
  for (i = 0; i < 36; ++i) {
    swap = random () % 36;
    save = pDiceRoll->indices[i];
    pDiceRoll->indices[i] = pDiceRoll->indices[swap];
    pDiceRoll->indices[swap] = save;
  }

}

static void RandomiseLayers (diceroll *pDiceRoll) {

  int             layer, i, j, k, swap;
  unsigned short *p;
  int             seq_len, arr_len, no_sets;
for (layer = 3, seq_len = ARRAY_LEN,
         arr_len = ARRAY_LEN / 36,
         no_sets = 1;
       layer > 0;
--layer, seq_len /= 36,
         arr_len /= 36,
         no_sets *= 36) {

    /* treat the set of rolls as no_sets sequences of
     * 36 arrays of length arr_len. Within each set of arrays
     * the rolls for lower layers (earlier rolls) are repeated
     * in the same order in each array. The roll for this layer
     * is 11 in the first array, 12 in the second, etc.
     * We can swap corresponding elements in any of the arrays of
     * one set without breaking anything, thereby randomising the
     * rolls for this layer
     */
    for (i = 0; i < no_sets; ++i) {
      /* find the start of the set of 36 sequences */
      p = pDiceRoll->rolls + (i * seq_len);

      /* do every element within each array in the set */
      for (j = 0; j < arr_len; ++j) {
        /* for each of the 36 arrays in this set */
        for (k = 0; k < 36; ++k) {
          /* pick another array in the set */
          swap = random () % 36;

          /* just to ensure that we maintain the invariant that
           * we're only randomising higher layers
           */
          assert ((*(p + j + k * arr_len) % arr_len) ==
            (*(p + j + swap * arr_len) % arr_len));

          SwapRolls (p + j + k * arr_len, p + j + swap * arr_len);
        }
      }
    }
  }
}

/* Initialise/reinitialise the arrays */

static void InitRolls (diceroll *pDiceRoll, int nGames, int fInitial,
                       void   (*randfunc)(int *) ) {

  int                i;
  unsigned short    *p;

  pDiceRoll->fInitial = fInitial;
  pDiceRoll->randfunc = randfunc;
  pDiceRoll->last =  ARRAY_LEN;
  pDiceRoll->count = 0;

  /* scramble the initial rolls */
  SetFirstRolls (pDiceRoll);

  if (!pDiceRoll->fReady) {
    /* scramble rolls 1/2/3 only the first time */
for (p = pDiceRoll->rolls, i = 0; i < ARRAY_LEN; ++i) *p++ = i;

    RandomiseLayers (pDiceRoll);
    pDiceRoll->fReady = 1;
  }

}

static void GetRoll (diceroll *pDiceRoll, int nTurn, int *anDice) {

  int i, first;

while (1) { if ((nTurn == 0) && ((pDiceRoll->count % 36) == 0)) {
      /* update all the pointers for each 36 new games */
      for (i = 0; i < 36; ++i) {
        if (++pDiceRoll->indices[i] >= ARRAY_LEN)
          pDiceRoll->indices[i] -= ARRAY_LEN;
      }
    }

    first = i = pDiceRoll->first[(pDiceRoll->count) % 36];

    if (nTurn == 0) {
        ++pDiceRoll->count;
    }

    anDice[0] = 1 + i / 6;
    anDice[1] = 1 + (i % 6);
/* keep tring if it's a double and fInitial */
    if (!pDiceRoll->fInitial || (anDice[0] != anDice[1]) || (nTurn != 0))
      break;

  }

  if (nTurn == 0)
    return;

  /* use the supplied function for turns after the first 4 */
  if (nTurn > 3) {
    (pDiceRoll->randfunc) (anDice);
    return;
  }

  /* get an entry out of the array and return the appropriate dice */
  i = pDiceRoll->rolls[pDiceRoll->indices[first]];

#if DEBUGGING_ROLLS
  assert (i < ARRAY_LEN);
# endif

  switch (nTurn) {
  case 3:

#if DEBUGGING_ROLLS
    pDiceRoll->rolls[pDiceRoll->indices[first]] = 0xffff;
# endif

    i /= 36;
    /* fall through */
  case 2:
    i /= 36;
    /* fall through */
  case 1:
    i %= 36;
    anDice[0] = 1 + i / 6;
    anDice[1] = 1 + (i % 6);
    return;

  default:
    assert ( 1 == 0 );
  }
}

#ifdef TEST_ROLLS
/* dummy function to get random dice for later rolls
* in real life, some systems random() function may not be good enough, * so we'd use something better.
 */
static void RandRoll (int *aRoll) {
aRoll[0] = 1 + random () % 6;
  aRoll[1] = 1 + random () % 6;
}

static diceroll adRoll;

static void usage (char * program) { fprintf (stderr, "Usage: %s <trials> <0|1>\n"
           "   trials = 1..4 to test 1..4 plies of full rollouts\n"
           "          other values are exact counts\n"
           "   optional flag 0/1 for Initial position\n", program);
  exit (1);
}

int main (int argc, char **argv) {

  int   trials = 1296;
  int   i, t;
  int   iGame, iTurn;
  int   fInitial = 0;
  int   anDice[2];

  switch (argc) {
  case 3:
    /* set fInitial from command line */
    sscanf (argv[2], "%d", &fInitial);
    if ((fInitial < 0) || (fInitial > 1)) {
      usage (argv[0]);
    }
    /* fall through */

  case 2:
    /* get number of full rollouts or no. of games */
    sscanf (argv[1], "%d", &trials);
    if (trials < 1) {
      usage (argv[0]);
    }
    if (trials < 5) {
      t = 1;
      for (i = 0; i < trials; ++i) {
        t *= (fInitial && (i == 0)) ? 30 : 36;
      }
      trials = t;
    }
    break;

  case 1:
    break;

  default:
    usage (argv[0]);
  }

  srandom (1);

  InitRolls (&adRoll, trials, fInitial, RandRoll);

  iGame = 0;

  while (1) {
    /* work out what the first four rolls of a game will be */
    for (iTurn = 0; iTurn < 4; ++iTurn) {
      GetRoll (&adRoll, iTurn, anDice);

      /* ensure printout shows low, high */
      if (anDice[0] > anDice[1]) {
        i = anDice[1];
        anDice[1] = anDice[0];
        anDice[0] = i;
      }
      /* make it easy to grep out all the nth moves from the results */
      printf ("%d%d ", anDice[0], anDice[1]);

    }
    printf (": %d\n", iGame);

++iGame; if (--trials <= 0)
      break;
  }
return 0;
}
#endif



------------------------------------------------------------------------

#!/usr/bin/perl

$init = 36;

if ($#ARGV == 0) {
    $init = ($ARGV[0] =~ /1/) ? 30 : 36;
}
else {
    die "Usage: checkrand 0|1 for regular|initial rollouts\n";
}
while (<STDIN>)
{
    s/\s+$//;
    @r = split (/\s/);
    $game = $r[5];
    if (($game > 0) && (($game % $init) == 0))
    {
foreach $k (sort keys %roll1) {
            $n = $roll1{$k};
            if (($n != 1) && ($n != 2) )
            {
                print "$k $roll1{$k}  $n $game\n";
            }
$roll1{$k} = 0;
        }
    }
    if (($game > 0) && (($game % ($init * 36)) == 0))
    {
foreach $k (sort keys %roll2) {
            $n = $roll2{$k};
            if (($n != 1) && ($n != 2) && ($n != 4) )
            {
                print "$k $roll2{$k}  $n $game\n";
            }
$roll2{$k} = 0;
        }

if (($game % ($init * 1296)) == 0) { foreach $k (sort keys %roll3) {
                $n = $roll3{$k};
                if (($n != 1) && ($n != 2) && ($n != 4) && ($n != 8))
                {
                    print "$k $n $game\n";
}
                $roll3{$k} = 0;
            }
        }
    }
    $roll1{"$r[0]"} += 1;
    $roll2{"$r[0]$r[1]"} += 1;
    $roll3{"$r[0]$r[1]$r[2]"} += 1;
    $roll4{"$r[0]$r[1]$r[2]$r[3]"} += 1;
}

if (($game + 1) % ($init * 36) == 0) { foreach $k (sort keys %roll2) {
        $n = $roll2{$k};
        if (($n != 1) && ($n != 2) && ($n != 4))
        {
            print "$k $roll2{$k} $game\n";
        }
    }
}


if (($game + 1) % ($init * 1296) == 0)
{
foreach $k (sort keys %roll3) {
        $n = $roll3{$k};
        if (($n != 1) && ($n != 2) && ($n != 4) && ($n != 8))
        {
            print "$k $n $game\n";
} }
}

if (($game + 1) % ($init * 36 * 1296) == 0)
{
foreach $k (sort keys %roll4) {
        $n = $roll4{$k};
        if (($n != 1) && ($n != 2) && ($n != 4) && ($n != 8) && ($n != 16))
        {
            print "$k $roll4{$k} $game\n";
        }
    }
}
print "Done\n";





reply via email to

[Prev in Thread] Current Thread [Next in Thread]