contest/
contest/Merc21/
contest/Merc21/log/
contest/Merc21/player/
/******************************************************************************
 Copyright 2007 Richard Woolcock.  All rights reserved.

 Redistribution and use in source and binary forms, with or without 
 modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice, 
   this list of conditions and the following disclaimer. 

 * Redistributions in binary form must reproduce the above copyright notice, 
   this list of conditions and the following disclaimer in the documentation 
   and/or other materials provided with the distribution. 

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 POSSIBILITY OF SUCH DAMAGE.
 ******************************************************************************/

/******************************************************************************
 Headers
 ******************************************************************************/

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

#include "merc.h"
#include "score.h"
#include "game.h"

/******************************************************************************
 Local literals
 ******************************************************************************/

typedef struct
{
   const char *pName;            /* The name of the game                 */
   game_t      Game;             /* The type of game being played        */
   score_t     Scoring;          /* The scoring system used by the game  */
   boolean_t   bSoloBet;         /* The players have separate bets       */
   boolean_t   bMinimumBet;      /* A minimum bet is required            */
   int         MaxPlayers;       /* Maximum mumber of players permitted  */
   int         InitialDraw;      /* Number of cards in the initial draw  */
   int         FullHand;         /* Number of cards in a full hand       */
   int         LastTurn;         /* The last game turn                   */
   int         BurnCards   [10]; /* Number of burn cards on turns 1-10   */
   int         SharedDraw  [10]; /* Number of shared cards on turns 1-10 */
   int         ViewHand    [10]; /* Can you view hand on turns 1-10?     */
   int         CanBet      [10]; /* Can you place bets on turns 1-10?    */
   int         CanFold     [10]; /* Can you fold on turns 1-10?          */
   int         CanDiscard  [10]; /* Can you discard cards on turns 1-10? */
   int         CanDraw     [10]; /* Can you draw cards on turns 1-10?    */
   int         CanStick    [10]; /* Can you stick on turns 1-10?         */
   int         CanDouble   [10]; /* Can you double down on turns 1-10?   */
} game_rules_t;

/******************************************************************************
 Local structures
 ******************************************************************************/

game_rules_t GameRulesTable[] = 
{
   {
      /* Name of the card game  */ "Draw Poker", 
      /* Enumerated value       */ eGameDrawPoker,
      /* Scoring system         */ eScorePoker, 
      /* Are solo bets made?    */ eFalse, 
      /* Is there a minimum bet?*/ eFalse, 
      /* Maximum players        */ 6, 
      /* Cards in initial draw  */ 5, 
      /* Cards in a full hand   */ 5, 
      /* The last turn is turn  */ 3, 
      /*                      Turn: 1 2 3 */
      /* Burn cards turns 1-10  */ {0,0,0}, 
      /* Shared draw turns 1-10 */ {0,0,0}, 
      /* Can you view hand?     */ {1,1,1}, 
      /* Can you place bets?    */ {1,0,1}, 
      /* Can you fold?          */ {1,1,1}, 
      /* Can you discard cards? */ {0,4,0}, 
      /* Can you draw cards?    */ {0,4,0}, 
      /* Can you stick?         */ {0,1,0}, 
      /* Can you double down?   */ {0,0,0}
   }, 
   {
      /* Name of the card game  */ "Texas Hold 'em", 
      /* Enumerated value       */ eGameTexasHoldem,
      /* Scoring system         */ eScorePoker, 
      /* Are solo bets made?    */ eFalse, 
      /* Is there a minimum bet?*/ eFalse, 
      /* Maximum players        */ 10, 
      /* Cards in initial draw  */ 2, 
      /* Cards in a full hand   */ 7, 
      /* The last turn is turn  */ 4, 
      /*                      Turn: 1 2 3 4 */
      /* Burn cards turns 1-10  */ {0,1,1,1}, 
      /* Shared draw turns 1-10 */ {0,3,1,1}, 
      /* Can you view hand?     */ {1,1,1,1}, 
      /* Can you place bets?    */ {1,1,1,1}, 
      /* Can you fold?          */ {1,1,1,1}, 
      /* Can you discard cards? */ {0,0,0,0}, 
      /* Can you draw cards?    */ {0,0,0,0}, 
      /* Can you stick?         */ {0,0,0,0}, 
      /* Can you double down?   */ {0,0,0,0}
   }, 
   {
      /* Name of the card game  */ "Blackjack", 
      /* Enumerated value       */ eGameBlackjack,
      /* Scoring system         */ eScoreBlackjack, 
      /* Are solo bets made?    */ eTrue, 
      /* Is there a minimum bet?*/ eTrue, 
      /* Maximum players        */ 10, 
      /* Cards in initial draw  */ 2, 
      /* Cards in a full hand   */ 5, 
      /* The last turn is turn  */ 2, 
      /*                      Turn: 1 2 */
      /* Burn cards turns 1-10  */ {0,0}, 
      /* Shared draw turns 1-10 */ {0,0}, 
      /* Can you view hand?     */ {0,1}, 
      /* Can you place bets?    */ {1,0}, 
      /* Can you fold?          */ {1,1}, 
      /* Can you discard cards? */ {0,0}, 
      /* Can you draw cards?    */ {0,1}, 
      /* Can you stick?         */ {0,1}, 
      /* Can you double down?   */ {0,1}
   }
};

/******************************************************************************
 Local variables
 ******************************************************************************/

/* None */

/******************************************************************************
 Global functions
 ******************************************************************************/

/* Function: GameValidation
 *
 * This function ensures that the game table is properly aligned with the 
 * enum.  There's no clean and easy way to do this automatically in C, but 
 * its easy to make a mistake which could in turn give rise to some strange 
 * bugs - therefore this function performs the validation for you.
 *
 * Arguments: None.
 *
 * Returns: A boolean flag indicating whether validation was successful.
 */
boolean_t GameValidation( void )
{
   int i; /* Loop counter */

   /* Varify that the card structure is correctly aligned */
   for ( i = eGameNone+1; i < eGameMax; ++i )
   {
      if ( GameRulesTable[i].Game != i )
      {
         /* You may want to replace this with mud log/error message */
         printf( "GameValidation: Game '%s' is not aligned with its enum.\n\r",
            GameRulesTable[i].pName );

         return eFalse; /* The table is not correctly aligned */
      }
   }

   return eTrue; /* The table is correctly aligned */
}

/* Function: GameName
 *
 * This function returns the name of the game you're playing.
 *
 * Arguments: The game you're playing.
 *
 * Returns: The name of the game.
 */
const char *GameName( game_t aGame )
{
   const char *pResult = "Unknown";

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      pResult = GameRulesTable[aGame].pName;
   }
   else /* Invalid enum */
   {
      bug( "GameName: Unrecognised game_t enum '%d'.", aGame );
   }

   return pResult;
}

/* Function: GameIndex
 *
 * This function returns the index (enum) of the specified game.
 *
 * Arguments: The game name as a string.
 *
 * Returns: The enum (or 'GameNone' if there is no match).
 */
game_t GameIndex( const char *apGame )
{
   if ( apGame != NULL )
   {
      int i; /* Loop counter */

      /* Look for a game which matches the specified string */
      for ( i = eGameNone+1; i < eGameMax; ++i )
      {
         /* If successful, return the enum value */
         if ( !str_cmp(GameRulesTable[i].pName, apGame) )
            return i;
      }
   }

   /* Invalid name selected, so there's no game */
   return eGameNone;
}

/* Function: GameScoring
 *
 * This function returns the scoring system used by the game.  For example most 
 * poker games use a certain way of calculating your score, while blackjack 
 * uses a different way.
 *
 * Arguments: The game you're playing.
 *
 * Returns: The type of scoring system used.
 */
score_t GameScoring( game_t aGame )
{
   int Result = 0;

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      Result = GameRulesTable[aGame].Scoring;
   }
   else /* Invalid enum */
   {
      bug( "GameScoring: Unrecognised game_t enum '%d'.", aGame );
   }

   return Result;
}

/* Function: GameSoloBet
 *
 * This function indicates whether or not the players in this game have a 
 * solo bet with the dealer - for example blackjack, where each player is 
 * effectively playing a separate game against the dealer.
 *
 * Arguments: The game you're playing.
 *
 * Returns: Whether or not you make solo bets.
 */
boolean_t GameSoloBet( game_t aGame )
{
   boolean_t bResult = eFalse;

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      bResult = GameRulesTable[aGame].bSoloBet;
   }
   else /* Invalid enum */
   {
      bug( "GameSoloBet: Unrecognised game_t enum '%d'.", aGame );
   }

   return bResult;
}

/* Function: GameMinimumBet
 *
 * This function indicates whether or not the dealer has to make a minimum bet 
 * before the game can begin.
 *
 * Arguments: The game you're playing.
 *
 * Returns: Whether or not a minimum bet is required.
 */
boolean_t GameMinimumBet( game_t aGame )
{
   boolean_t bResult = eFalse;

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      bResult = GameRulesTable[aGame].bMinimumBet;
   }
   else /* Invalid enum */
   {
      bug( "GameMinimumBet: Unrecognised game_t enum '%d'.", aGame );
   }

   return bResult;
}

/* Function: GameAdvice
 *
 * This function returns a recommended action as a string, which is directly 
 * parsable as a command.  It is primarily designed for mobs, and is currently 
 * highly simplified - real AI tactics would require a lot more work.
 *
 * Arguments: The game you're playing, and your hand.
 *
 * Returns: A string containing a recommended action.
 */
char *GameAdvice( game_t aGame, hand_t *apHand )
{
   static char Buffer[256];
   Buffer[0] = '\0';

   if ( apHand != NULL )
   {
      const char *pCardList = NULL;

      switch ( aGame )
      {
         default:
            bug( "GameAdvice: Unrecognised game_t enum '%d'.", aGame );
            break;
         case eGameTexasHoldem:
            /* No advice for this game */
            break;
         case eGameDrawPoker:
            pCardList = HandShow(apHand, eFalse);
            if ( pCardList != NULL )
            {
               int Max = strlen(pCardList);
               int i; /* Loop counter */
               int CardIndex = 0, DiscardCount = 0;
               card_t Cards[5] = { eCardMax, eCardMax, eCardMax, eCardMax, eCardMax };
               boolean_t CanDiscard[5] = { eFalse, eFalse, eFalse, eFalse, eFalse };
               combination_t OldCombination = eCombinationHighCard;
               card_t KeepCard = eCardMax;

               /* Calculate the current poker score */
               (void)ScorePoker(apHand, &OldCombination);

               /* First we pull out the first 5 cards */
               for ( i = 0; i < Max && CardIndex < 5; i += 3, ++CardIndex )
                  Cards[CardIndex] = CardType(CardSuit(pCardList[i]), CardRank(pCardList[i+1]));

               /* Now we see which cards aren't needed */
               for ( i = 0; i < 5; ++i )
               {
                  combination_t NewCombination = eCombinationHighCard;

                  /* Remove the card from the hand */
                  (void)HandRemoveCard(apHand, Cards[i]);

                  /* Check the new score */
                  (void)ScorePoker(apHand, &NewCombination);

                  /* See if the card can be discarded without lowering score */
                  if ( NewCombination == OldCombination )
                  {
                     CanDiscard[i] = eTrue;
                     DiscardCount++;
                  }

                  /* Put the card back in the hand */
                  (void)HandAddCard(apHand, Cards[i]);
               }

               /* Can only discard 4 cards; if none are needed, keep highest */
               if ( DiscardCount == 5 )
               {
                  KeepCard = Cards[0];

                  /* Find the card with the highest score */
                  for ( i = 1; i < 5; ++i )
                  {
                     const char *pOldValue = CardValue(KeepCard);
                     const char *pNewValue = CardValue(Cards[i]);
                     rank_t OldRank = CardRank(pOldValue[1]);
                     rank_t NewRank = CardRank(pNewValue[1]);

                     if ( NewRank == eRankAce || NewRank > OldRank )
                        KeepCard = Cards[i];
                  }
               }

               /* Now we generate the recommendation list */
               for ( i = 0; i < 5; ++i )
               {
                  if ( CanDiscard[i] == eTrue && Cards[i] != KeepCard )
                  {
                     if ( Buffer[0] == '\0' )
                        strcpy( Buffer, "discard" );
                     strcat( Buffer, " " );
                     strcat( Buffer, CardValue(Cards[i]) );
                  }
               }
            }
            break;
         case eGameBlackjack:
            if ( ScoreBlackjack(apHand) < 17 )
               strcpy( Buffer, "draw" );
            else /* Already over 17, so stick */
               strcpy( Buffer, "stick" );
            break;
      }
   }
   else /* No hand was defined */
   {
      bug( "GameAdvice: apHand was NULL.", 0 );
   }

   return Buffer;
}

/* Function: GameMaxPlayers
 *
 * This function returns how many players can play this particular game.  If 
 * you set this number too high you may reach a point where there are simply 
 * not enough cards to go around.
 *
 * Arguments: The game you're playing.
 *
 * Returns: The number of cards to be drawn.
 */
int GameMaxPlayers( game_t aGame )
{
   int Result = 0;

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      Result = GameRulesTable[aGame].MaxPlayers;
   }
   else /* Invalid enum */
   {
      bug( "GameMaxPlayers: Unrecognised game_t enum '%d'.", aGame );
   }

   return Result;
}

/* Function: GameInitialDraw
 *
 * This function returns how many cards should be initially drawn - for example 
 * draw poker would be 5, blackjack would be 2, etc.
 *
 * Arguments: The game you're playing.
 *
 * Returns: The number of cards to be drawn.
 */
int GameInitialDraw( game_t aGame )
{
   int Result = 0;

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      Result = GameRulesTable[aGame].InitialDraw;
   }
   else /* Invalid enum */
   {
      bug( "GameInitialDraw: Unrecognised game_t enum '%d'.", aGame );
   }

   return Result;
}

/* Function: GameFullHand
 *
 * This function returns the maximum number of cards you're allowed to have in 
 * your hand at any one time.
 *
 * Arguments: The game you're playing.
 *
 * Returns: The number of cards in a full hand.
 */
int GameFullHand( game_t aGame )
{
   int Result = 0;

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      Result = GameRulesTable[aGame].FullHand;
   }
   else /* Invalid enum */
   {
      bug( "GameFullHand: Unrecognised game_t enum '%d'.", aGame );
   }

   return Result;
}

/* Function: GameLastTurn
 *
 * This function returns how many turns there are in the game.  For open ended 
 * games, return -1.
 *
 * Arguments: The game you're playing.
 *
 * Returns: The maximum number of turns.
 */
int GameLastTurn( game_t aGame )
{
   int Result = 0;

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      Result = GameRulesTable[aGame].LastTurn;
   }
   else /* Invalid enum */
   {
      bug( "GameLastTurn: Unrecognised game_t enum '%d'.", aGame );
   }

   return Result;
}

/* Function: GameBurnCards
 *
 * This function returns how many burn cards are discarded on the specified 
 * turn.  For example in Texas Hold 'em 1 card is burned before the flop, 1 
 * before the turn and 1 before the river.
 *
 * Arguments: The game you're playing.
 *
 * Returns: The maximum number of turns.
 */
int GameBurnCards( game_t aGame, int aTurn )
{
   int Result = 0;

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      if ( aTurn >= 1 && aTurn <= 10 )
      {
         Result = GameRulesTable[aGame].BurnCards[aTurn-1];
      }
      else /* Outside valid range */
      {
         bug( "GameBurnCards: Turn '%d' is not supported.", aTurn );
      }
   }
   else /* Invalid enum */
   {
      bug( "GameBurnCards: Unrecognised game_t enum '%d'.", aGame );
   }

   return Result;
}

/* Function: GameSharedDraw
 *
 * This function returns how many shared cards are drawn on the specified 
 * turn.  For example Texas Hold 'em has a 3 card flop on turn 2, a turn (or 
 * 'fourth street') on turn 3 and a river (or 'fifth street') on turn 4.
 *
 * Arguments: The game you're playing.
 *
 * Returns: The maximum number of turns.
 */
int GameSharedDraw( game_t aGame, int aTurn )
{
   int Result = 0;

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      if ( aTurn >= 1 && aTurn <= 10 )
      {
         Result = GameRulesTable[aGame].SharedDraw[aTurn-1];
      }
      else /* Outside valid range */
      {
         bug( "GameSharedDraw: Turn '%d' is not supported.", aTurn );
      }
   }
   else /* Invalid enum */
   {
      bug( "GameSharedDraw: Unrecognised game_t enum '%d'.", aGame );
   }

   return Result;
}

/* Function: GameViewHand
 *
 * This function returns whether the 'hand' view command is permitted.
 *
 * Arguments: The game you're playing.
 *
 * Returns: Whether or not you can use this option in this game.
 */
int GameViewHand( game_t aGame, int aTurn )
{
   int Result = 0;

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      if ( aTurn >= 1 && aTurn <= 10 )
      {
         Result = GameRulesTable[aGame].ViewHand[aTurn-1];
      }
      else if ( aTurn == 0 )
      {
         /* Special case '0' adds them up */
         int i; /* Loop counter */
         for ( i = 0; i < 10; ++i )
            Result += GameRulesTable[aGame].ViewHand[i];
      }
      else /* Outside valid range */
      {
         bug( "GameViewHand: Turn '%d' is not supported.", aTurn );
      }
   }
   else /* Invalid enum */
   {
      bug( "GameViewHand: Unrecognised game_t enum '%d'.", aGame );
   }

   return Result;
}

/* Function: GameCanBet
 *
 * This function returns whether the 'bet' command is permitted.
 *
 * Arguments: The game you're playing.
 *
 * Returns: Whether or not you can use this option in this game.
 */
int GameCanBet( game_t aGame, int aTurn )
{
   int Result = 0;

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      if ( aTurn >= 1 && aTurn <= 10 )
      {
         Result = GameRulesTable[aGame].CanBet[aTurn-1];
      }
      else if ( aTurn == 0 )
      {
         /* Special case '0' adds them up */
         int i; /* Loop counter */
         for ( i = 0; i < 10; ++i )
            Result += GameRulesTable[aGame].CanBet[i];
      }
      else /* Outside valid range */
      {
         bug( "GameCanBet: Turn '%d' is not supported.", aTurn );
      }
   }
   else /* Invalid enum */
   {
      bug( "GameCanBet: Unrecognised game_t enum '%d'.", aGame );
   }

   return Result;
}

/* Function: GameCanDiscard
 *
 * This function returns whether the 'discard' command is permitted.
 *
 * Arguments: The game you're playing.
 *
 * Returns: Whether or not you can use this option in this game.
 */
int GameCanDiscard( game_t aGame, int aTurn )
{
   int Result = eFalse;

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      if ( aTurn >= 1 && aTurn <= 10 )
      {
         Result = GameRulesTable[aGame].CanDiscard[aTurn-1];
      }
      else if ( aTurn == 0 )
      {
         /* Special case '0' adds them up */
         int i; /* Loop counter */
         for ( i = 0; i < 10; ++i )
            Result += GameRulesTable[aGame].CanDiscard[i];
      }
      else /* Outside valid range */
      {
         bug( "GameCanDiscard: Turn '%d' is not supported.", aTurn );
      }
   }
   else /* Invalid enum */
   {
      bug( "GameCanDiscard: Unrecognised game_t enum '%d'.", aGame );
   }

   return Result;
}

/* Function: GameCanDraw
 *
 * This function returns whether the 'draw' command is permitted.
 *
 * Arguments: The game you're playing.
 *
 * Returns: Whether or not you can use this option in this game.
 */
int GameCanDraw( game_t aGame, int aTurn )
{
   int Result = eFalse;

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      if ( aTurn >= 1 && aTurn <= 10 )
      {
         Result = GameRulesTable[aGame].CanDraw[aTurn-1];
      }
      else if ( aTurn == 0 )
      {
         /* Special case '0' adds them up */
         int i; /* Loop counter */
         for ( i = 0; i < 10; ++i )
            Result += GameRulesTable[aGame].CanDraw[i];
      }
      else /* Outside valid range */
      {
         bug( "GameCanDraw: Turn '%d' is not supported.", aTurn );
      }
   }
   else /* Invalid enum */
   {
      bug( "GameCanDraw: Unrecognised game_t enum '%d'.", aGame );
   }

   return Result;
}

/* Function: GameCanFold
 *
 * This function returns whether the 'fold' command is permitted.
 *
 * Arguments: The game you're playing.
 *
 * Returns: Whether or not you can use this option in this game.
 */
int GameCanFold( game_t aGame, int aTurn )
{
   int Result = eFalse;

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      if ( aTurn >= 1 && aTurn <= 10 )
      {
         Result = GameRulesTable[aGame].CanFold[aTurn-1];
      }
      else if ( aTurn == 0 )
      {
         /* Special case '0' adds them up */
         int i; /* Loop counter */
         for ( i = 0; i < 10; ++i )
            Result += GameRulesTable[aGame].CanFold[i];
      }
      else /* Outside valid range */
      {
         bug( "GameCanFold: Turn '%d' is not supported.", aTurn );
      }
   }
   else /* Invalid enum */
   {
      bug( "GameCanFold: Unrecognised game_t enum '%d'.", aGame );
   }

   return Result;
}

/* Function: GameCanStick
 *
 * This function returns whether the 'stick' command is permitted.
 *
 * Arguments: The game you're playing.
 *
 * Returns: Whether or not you can use this option in this game.
 */
int GameCanStick( game_t aGame, int aTurn )
{
   int Result = eFalse;

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      if ( aTurn >= 1 && aTurn <= 10 )
      {
         Result = GameRulesTable[aGame].CanStick[aTurn-1];
      }
      else if ( aTurn == 0 )
      {
         /* Special case '0' adds them up */
         int i; /* Loop counter */
         for ( i = 0; i < 10; ++i )
            Result += GameRulesTable[aGame].CanStick[i];
      }
      else /* Outside valid range */
      {
         bug( "GameCanStick: Turn '%d' is not supported.", aTurn );
      }
   }
   else /* Invalid enum */
   {
      bug( "GameCanStick: Unrecognised game_t enum '%d'.", aGame );
   }

   return Result;
}

/* Function: GameCanDouble
 *
 * This function returns whether the 'double' command is permitted.
 *
 * Arguments: The game you're playing.
 *
 * Returns: Whether or not you can use this option in this game.
 */
int GameCanDouble( game_t aGame, int aTurn )
{
   int Result = eFalse;

   if ( aGame > eGameNone && aGame < eGameMax )
   {
      if ( aTurn >= 1 && aTurn <= 10 )
      {
         Result = GameRulesTable[aGame].CanDouble[aTurn-1];
      }
      else if ( aTurn == 0 )
      {
         /* Special case '0' adds them up */
         int i; /* Loop counter */
         for ( i = 0; i < 10; ++i )
            Result += GameRulesTable[aGame].CanDouble[i];
      }
      else /* Outside valid range */
      {
         bug( "GameCanDouble: Turn '%d' is not supported.", aTurn );
      }
   }
   else /* Invalid enum */
   {
      bug( "GameCanDouble: Unrecognised game_t enum '%d'.", aGame );
   }

   return Result;
}