[comp.sources.games.bugs] Randomness problems with mille - or how it seemed to play weird

syd@dsinc.UUCP (Syd Weinstein) (02/13/88)

After playing mille for several weeks, I kept getting cards I thought
the odds of getting were all wrong.  I finally looked at card.c and
determined the problem.

Mille has a deck of 101 cards.  Lets take the example of the roll
card.  There are 14 roll cards.  As the first card is dealt, there is a
14/101 chance of it being a roll card.  The way the card.c file works,
this is correct as if a random number is between n and n+13, its a roll
card.  Then a count of roll cards is decremented and the game
continues.

However after this first card is dealt this algorithm is all wrong.

Now there are 13 roll cards, the odds of the next card being a roll
card are 13/100. (13 rolls left in 100 cards left).  The program used
the odds 14/100 as any random number in the range n..n+13 is still a
roll card and then it decremented a roll card if one was left.

This biases the deal heavily towards the roll cards and away from the
lesser cards.  It made it very difficult to get a hazard as the game
went on and even more difficult to get a remedy for that hazard.

I added a 'brute force' deal change to card.  This fix makes the random
selection be retried if that particular card number (0..100) has
already been used.  If so, I get another card.  This makes the odds
work out right as long as the number generator is really uniform.  In
this case it made the play much more like the real game and got rid of
those often occurring 10 play shutouts that were happening.

the patch in patch format is

Index: card.c
*** card.sav
--- card.c
**************
*** 47,52
  /*                                                                    */
  /**********************************************************************/
  
  /*********************************************************/
  /* initilaizes a card_set structure with the card counts */
  /*********************************************************/
--- 47,54 -----
  /*                                                                    */
  /**********************************************************************/
  
+ int deck_played[101];
+ 
  /*********************************************************/
  /* initilaizes a card_set structure with the card counts */
  /*********************************************************/
**************
*** 53,58
  initialize_card_set (cards)
  int cards[20];
  {
  cards[0]              = 101;    /* total cards */
  cards[extra_tank]     = 1;      /* safeties */
  cards[puncture_proof] = 1;
--- 55,62 -----
  initialize_card_set (cards)
  int cards[20];
  {
+ int	i, c, *pi;
+ 
  cards[0]              = 101;    /* total cards */
  cards[extra_tank]     = 1;      /* safeties */
  cards[puncture_proof] = 1;
**************
*** 73,79
  cards[miles_75]       = 10;
  cards[miles_50]       = 10;
  cards[miles_25]       = 10;
- }
  
  /********************************************/
  /* removes a card from the deck and returns */
--- 77,82 -----
  cards[miles_75]       = 10;
  cards[miles_50]       = 10;
  cards[miles_25]       = 10;
  
  pi = deck_played;
  for (c = 1; c < 20; c++)
**************
*** 75,80
  cards[miles_25]       = 10;
  }
  
  /********************************************/
  /* removes a card from the deck and returns */
  /* card if it could, 0 if it couldn't       */
--- 78,91 -----
  cards[miles_50]       = 10;
  cards[miles_25]       = 10;
  
+ pi = deck_played;
+ for (c = 1; c < 20; c++)
+ 	{
+ 	for (i = cards[c]; --i >= 0; pi++)
+ 		*pi = 1;
+ 	}
+ }
+ 
  /********************************************/
  /* removes a card from the deck and returns */
  /* card if it could, 0 if it couldn't       */
**************
*** 99,104
  int number;
  {
  number = number % 101;          /* 0 - 100 */
  if (number >= 28 && number <= 41)
      return(roll);
  if (number >= 59 && number <= 70)
--- 110,118 -----
  int number;
  {
  number = number % 101;          /* 0 - 100 */
+ if (!deck_played[number])
+ 	return(-1);
+ deck_played[number] = 0;
  if (number >= 28 && number <= 41)
      return(roll);
  if (number >= 59 && number <= 70)
**************
*** 149,155
  deal_card ()
  {
  int card;
- int more_cards;
  int i;
  more_cards = FALSE;
  for (i=0; i<20; i++)
--- 163,168 -----
  deal_card ()
  {
  int card;
  int i;
  if (deck[0])
      {
**************
*** 151,162
  int card;
  int more_cards;
  int i;
! more_cards = FALSE;
! for (i=0; i<20; i++)
!     if (deck[i] > 0)
!          more_cards = TRUE;
! if (more_cards)
!     while ((card = remove_card(&deck[0],get_card(rand()))) == 0)
           ;
  else
      card = 0;
--- 164,172 -----
  {
  int card;
  int i;
! if (deck[0])
!     {
!     while ((card = get_card(rand())) < 0)
           ;
      card = remove_card(&deck[0],card);
      }
**************
*** 158,163
  if (more_cards)
      while ((card = remove_card(&deck[0],get_card(rand()))) == 0)
           ;
  else
      card = 0;
  return(card);
--- 168,175 -----
      {
      while ((card = get_card(rand())) < 0)
           ;
+     card = remove_card(&deck[0],card);
+     }
  else
      card = 0;
  return(card);
-- 
=====================================================================
Sydney S. Weinstein, CDP, CCP
Datacomp Systems, Inc.				Voice: (215) 947-9900
{allegra,bellcore,bpa,vu-vlsi}!dsinc!syd	FAX:   (215) 938-0235