
// The code below was pulled out of pokerSupport.cc and is the code relating to
// getting hints to show to the player.  The only function missing from here is
// computePoints() which returns how much the current board layout is worth in
// chips.

// There were two different versions of the hint code and both are included here with
// the shipping version contained within the #if 1 code block.

// The return value is a string with a list of from-to sliding locations.  The format of the
// return string is slightly different for each search method - look at the comment in the
// code where the return string is created for the exact format.


// Point2I is being used for "from" and "to" and the meanings of x,y are:
// x is col
// y is row


// 
#define MAX_HINT_SEARCH_DEPTH 8
static U32 hintSearchDepth = 3;


#if 1

// This is the shipping version and will give the best move where the player clicks on and
// slides just one card - allowing the card to turn corners.

static Point2I hiPath[MAX_HINT_SEARCH_DEPTH];


bool followTrail(U8 * deck, int baseRow, int baseCol, U16 depth, bool checkRow, U32 *bestPoints, S16 *bestDepth)
{
   // We haven't found a new best yet
   bool newBest = false;

   // Now searching one level deeper
   depth++;

   // Note - it is assumed that we will check the column if we don't check the row
   if (!checkRow)
   {
      // Shift cards 5 times to check all permutations on this column
      for (int shift=0;shift<5;shift++)
      {
         // Shift column of five cards by one
         U8 firstCard = deck[baseCol];
         for (int i=0;i<4;i++)
            deck[baseCol+i*5] = deck[baseCol+i*5+5];
         deck[baseCol+20] = firstCard;

         // Our last shift is just restoring the board to its entry state and so it doesn't need to be checked
         if (shift != 4)
         {
            // Get the points for this board layout
            U32 newPoints = computePoints(deck);

            // If we have new best points then save this point
            if (newPoints > *bestPoints)
            {
               newBest = true;
               *bestPoints = newPoints;
               *bestDepth = depth;
               hiPath[depth].x = baseCol;
               hiPath[depth].y = (baseRow+4-shift)%5;
            }

            // Recursively search more combinations from this base layout up to our search depth
            if (depth < hintSearchDepth)
            {
               if (followTrail(deck, (baseRow+4-shift)%5,baseCol, depth, true, bestPoints, bestDepth))
               {
                  newBest = true;
                  hiPath[depth].x = baseCol;
                  hiPath[depth].y = (baseRow+4-shift)%5;
               }
            }
         }
      }
   }
   else
   {
      // Shift cards 5 times to check all permutations on this row
      for (int shift=0;shift<5;shift++)
      {
         // Shift row of five cards by one
         U8 firstCard = deck[baseRow*5];
         for (int i=0;i<4;i++)
            deck[baseRow*5+i] = deck[baseRow*5+i+1];
         deck[baseRow*5+4] = firstCard;

         // Our last shift is just restoring the board to its entry state and so it doesn't need to be checked
         if (shift != 4)
         {
            // Get the points for this board layout
            U32 newPoints = computePoints(deck);

            // If we have new best points then save this point
            if (newPoints > *bestPoints)
            {
               newBest = true;
               *bestPoints = newPoints;
               *bestDepth = depth;
               hiPath[depth].x = (baseCol+4-shift)%5;
               hiPath[depth].y = baseRow;
            }

            // Recursively search more combinations from this base layout up to our search depth
            if (depth < hintSearchDepth)
            {
               if (followTrail(deck, baseRow,(baseCol+4-shift)%5, depth, false, bestPoints, bestDepth))
               {
                  newBest = true;
                  hiPath[depth].x = (baseCol+4-shift)%5;
                  hiPath[depth].y = baseRow;
               }
            }
         }
      }
   }

   return newBest;
}


const char * getHint(U32 searchDepth)
{
   hintSearchDepth = searchDepth;

   // Grab our deck from the script
   U8 deck[25];
   char tempStr[64];
   for (int i=0;i<25;i++)
   {
      dSprintf(tempStr,64,"$deck%i",i);
      U8 temp8 = Con::getIntVariable(tempStr);
      deck[i] = temp8;
   }

   // Start out with no best move
   hiPath[0] = Point2I(0,5);
   U32 bestPoints = computePoints(deck);
   U32 startPoints = bestPoints;
   S16 bestDepth = 0;

   // Permutate all five rows
   for (int row=0;row<5;row++)
   {
      // Permutate all five colunns
      for (int col=0;col<5;col++)
      {
         // Now follow a trail starting from this point checking its row
         if (followTrail(deck, row,col, 0, true, &bestPoints, &bestDepth))
         {
            hiPath[0].x = col;
            hiPath[0].y = row;
         }

         // Now follow a trail starting from this point checking its column
         if (followTrail(deck, row,col, 0, false, &bestPoints, &bestDepth))
         {
            hiPath[0].x = col;
            hiPath[0].y = row;
         }
      }
   }

#ifdef TORQUE_DEBUG
   Con::printf(" *** FINAL SEARCH RESULTS *** ");
   Con::printf(" startPoints: %i  bestPoints: %i   bestDepth: %i ",startPoints,bestPoints,bestDepth);
#endif

   // Return string format is "<pathRow0> <pathCol0> <pathRow1> <pathCol1> etc."
   char temp[512];
   temp[0] = 0;

   char temp1[128];
   for (int i=0;i<=bestDepth;i++)
   {
      dSprintf(temp1,128,"%i %i ",hiPath[i].y,hiPath[i].x);
      dStrcat(temp,temp1);
   }

   // Create a return buffer from the console and return result in it
   char* returnString = Con::getReturnBuffer( dStrlen( temp ) + 1 );
   dStrcpy( returnString, temp );
   return( returnString ); 
}


#else


// This version return the best possible set of moves where the player can first slide one
// card and then release it in its new location and then select another card and slide it.
// Repeating this process up to the depth of the search.  It can return better results than
// the above method, but also results in a less clean hint display on the screen so it is
// not being used in the shipping version of Puzzle Poker.

static Point2I hiTo[MAX_HINT_SEARCH_DEPTH];
static Point2I hiFrom[MAX_HINT_SEARCH_DEPTH];


bool getBestPoints(U8 * deck, U32 *bestPoints, S16 *bestDepth, S16 depth, int ignoreRow, int ignoreCol)
{
   // Start out assuming we don't find a new best
   bool newBest = false;

   // Depth of our recursive search
   depth++;

   // Permuatate all five rows
   for (int row=0;row<5;row++)
   {
      // Don't backtrack on a row we have already checked
      if (row != ignoreRow)
      {
         // Shift cards 5 times to check all permutations on this row
         for (int check=0;check<5;check++)
         {
            // Shift row of five cards by one
            U8 firstCard = deck[row*5];
            for (int shift=0;shift<4;shift++)
               deck[row*5+shift] = deck[row*5+shift+1];
            deck[row*5+4] = firstCard;

            // The last shift just returns us to where we were and so this layout doesn't need to be checked
            if (check != 4)
            {
               // Get the points for this board layout
               U32 newPoints = computePoints(deck);

               // If we have new best points or shorter path to equal points then save it
               if (newPoints > *bestPoints || (newPoints == *bestPoints && depth < *bestDepth))
               {
                  newBest = true;
                  *bestPoints = newPoints;
                  *bestDepth = depth;
                  hiTo[depth].x = 4-check;
                  hiTo[depth].y = row;
                  hiFrom[depth].x = 0;
                  hiFrom[depth].y = row;
               }

               // Recursively search more combinations from this base layout up to our search depth
               if (depth < hintSearchDepth)
               {
                  if (getBestPoints(deck,bestPoints,bestDepth,depth,row,5))
                  {
                     newBest = true;
                     hiTo[depth].x = 4-check;
                     hiTo[depth].y = row;
                     hiFrom[depth].x = 0;
                     hiFrom[depth].y = row;
                  }
               }
            }
         }
      }
   }

   // Permuatate all five columns
   for (int col=0;col<5;col++)
   {
      // Don't backtrack on a column we have already checked
      if (col != ignoreCol)
      {
         for (int check=0;check<5;check++)
         {
            U8 firstCard = deck[col];
            for (int shift=0;shift<4;shift++)
               deck[col+shift*5] = deck[col+shift*5+5];
            deck[col+20] = firstCard;

            if (check != 4)
            {
               // Get the points for this board layout
               U32 newPoints = computePoints(deck);

               // If we have new best points or shorter path to equal points then save it
               if (newPoints > *bestPoints || (newPoints == *bestPoints && depth < *bestDepth))
               {
                  newBest = true;
                  *bestPoints = newPoints;
                  *bestDepth = depth;
                  hiTo[depth].x = col;
                  hiTo[depth].y = 4-check;
                  hiFrom[depth].x = col;
                  hiFrom[depth].y = 0;
               }

               // Recursively search more combinations from this base layout up to our search depth
               if (depth < hintSearchDepth)
               {
                  if (getBestPoints(deck,bestPoints,bestDepth,depth,5,col))
                  {
                     newBest = true;
                     hiTo[depth].x = col;
                     hiTo[depth].y = 4-check;
                     hiFrom[depth].x = col;
                     hiFrom[depth].y = 0;
                  }
               }
            }
         }
      }
   }

   return newBest;
}


const char * getHint(U32 searchDepth)
{
   // Grab our deck from the script
   U8 deck[25];
   char tempStr[64];
   for (int i=0;i<25;i++)
   {
      dSprintf(tempStr,64,"$deck%i",i);
      U8 temp8 = Con::getIntVariable(tempStr);
      deck[i] = temp8;
   }

   // Start out with no best move
   hiFrom[0] = Point2I(0,5);
   hiTo[0] = Point2I(0,0);

   // Also start out with the current state of our board
   U32 bestPoints = computePoints(deck);
   U32 startPoints = bestPoints;
   S16 bestDepth = -1;
   getBestPoints(deck,&bestPoints,&bestDepth,-1,5,5);

   // Return string format is "<bestFromRow> <bestFromCol> <bestToRow> <bestToCol><tab>"
   char temp[512];
   temp[0] = 0;

   if (hiFrom[0].y == 5)
   {
      dSprintf(temp,128,"%i %i %i %i\t",hiFrom[0].y,hiFrom[0].x,hiTo[0].y,hiTo[0].x);
   }
   else
   {
      char temp1[128];
      for (int i=0;i<=bestDepth;i++)
      {
         dSprintf(temp1,128,"%i %i %i %i\t",hiFrom[i].y,hiFrom[i].x,hiTo[i].y,hiTo[i].x);
         dStrcat(temp,temp1);
      }
   }

   // Create a return buffer from the console and return result in it
   char* returnString = Con::getReturnBuffer( dStrlen( temp ) + 1 );
   dStrcpy( returnString, temp );
   return( returnString ); 
}

#endif




// -------------------------------------------------

ConsoleFunctionGroupBegin( PokerSupport, "Poker support functions.");


ConsoleFunction(psGetHint, const char *, 2, 3, "psGetHint(searchDepth)")
{
   argc;
   U32 searchDepth = dAtoi(argv[1]);
   return getHint(searchDepth);
}

ConsoleFunctionGroupEnd( PokerSupport );

