/* **************************************************************************************
   File: Cards.cpp
   By Mauro Mindoli 
   
   January 2003 ver. 1.0
   
   February 2003 : ver 2.0
   Corrected some bug and added new features to the code:
  
  @@ DONE 
   @ Management of hidden card , the first one of the human player. Now Pc does not see it!
   @ New method CPlayerNapoletano::GetEstimatedPoints() that makes some pseudo A.I.
   @ Corrected GameNapoletano::Play() to insert an estimation of the points.
   @ Changed the bet edit box, now it is a combo box.
   @ Bet is now only allowed after the first (hidden) card, like in the real rules.
   @ Implemented a simple error checking when there are not bmp to load (before it simply crashed!)
   @ Introduced two txt files to read for Info and for Rules in the help dialog and its menu.
   @ Solved a memory leak for a bug in the distructor ~CDeckNapoletano: it deleted the wrong deck
   @ Added sound effects!
   @ Management of the "jolly" i.e. the 10 of gold that can change its value 
     (0.5, 1, 2, ..., 7, but NOT 1.5, 4.5, ...), dinamically.

***************************************************************************************** */
#include "stdafx.h"
#include "cards.h"

int CGameCard::GetIdCard()
{
   return (mFam << 8) | mVal; 
}

CDeck::CDeck(int numCards, int numFams)  : mNumCards(numCards), mNumFams(numFams)
{
   mCurPos = 0;
}

void CDeck::ShuffleDeck()
{
   // the suffle is implemented by random swapping   
   srand(time( NULL ) );
   int randcard;
   for (int ncard = 0; ncard < mNumCards; ncard++)
   {
      randcard = rand()%mNumCards;         
      std::swap(shuffledDeck[ncard], shuffledDeck[randcard]);
   }

}

// The current card is taken from the Deck already shuffled by the mCurPos that is incremented
bool CDeck::GiveMeACard(const CGameCard* &card) 
{
   if (mCurPos >= mNumCards)
      return false;
   else
      card = shuffledDeck[mCurPos++];
   return true;
}

CDeckNapoletano::CDeckNapoletano() : CDeck(NCARDS, NFAMS)
{
   // init the Deck ordering it by values and familys
   int vCard;
   int vFam;
   mDeck.reserve(NCARDS);
   shuffledDeck.reserve(NCARDS);
   // we have to use mDeck to store the allocated pointers
   // to delete them in the distructor, and shuffledDeck
   // to play
   for (vFam = game::COPPE; vFam < game::END; vFam++)
      for (vCard = 1; vCard <= (mNumCards/mNumFams); vCard++)
      {
         CGameCardNapoletano *p = new CGameCardNapoletano(vFam, vCard);
         mDeck.push_back(p);
         shuffledDeck.push_back(p);
      }
}

CDeckNapoletano::~CDeckNapoletano()
{
   VecPCards::iterator itCurCard = mDeck.begin();
   for (; itCurCard != mDeck.end(); itCurCard++)
      delete (*itCurCard);
}

// we reshuffle the deck when cards are ended
// we cannot use the same original deck, because some card
// are still in play, so we have to use outCards: the vector 
// with all cards played
void CDeckNapoletano::Reshuffle(VecPCards &outCards)
{
   mNumCards = outCards.size(); // mNumCard is <= 40 now
   shuffledDeck.erase(shuffledDeck.begin(), shuffledDeck.end());
   // we copy outCards in the shuffled Vector to restart the game
   for (VecPCards::iterator it = outCards.begin(); it != outCards.end(); it++)
      shuffledDeck.push_back(*it);
   // mCurPos has to be reset!
   mCurPos = 0;
}

CPlayer::CPlayer(std::string name) : mPoints(0.0), mName(name){}

#include <math.h>
int CPlayerNapoletano::AddCard(CGameCard *pCard)
{
   int val, fam;
   pCard->GetFamVal(fam, val);
   
   // the card is the Jolly! Possible Jolly values are: 0.5, 1, 2, 3, 4, 5, 6, 7
   if ((fam == game::DENARI) && (val == 10))
   {
      // I want to check if I had a point with a decimal part or not:
      // if I have 3.5 points my jolly will be 4, if I have 4 points my jolly will be 3
      mJollyValue = (mPoints - floor(mPoints)) > 0 ? 7.5 - mPoints : 7.0 - mPoints;
      mPoints += mJollyValue;
      if (mJollyValue == 0.0)
         mJollyValue = mPoints; // Jolly was the first card
   }
   else // Jolly was already in hand
   if (mJollyValue)
   {
      mPoints = mPoints - mJollyValue + ((val > 7) ? 0.5 : val); // points without jolly
      if (mPoints < 7.5) // minimum Jolly value is 0.5
         mJollyValue = (mPoints - floor(mPoints)) > 0 ? 7.5 - mPoints : 7.0 - mPoints;
      mPoints += mJollyValue;
   }
   else // normal case
      mPoints += (val > 7) ? 0.5 : val;
   
   if (mPoints > 7.5)
      return game::GAME_LOST;
   else
      return game::GAME_OK;
}

void CPlayerNapoletano::AddHand(CGameCard *pCard)
{
   mHand.push_back(pCard);
}

// ****************************************
// * Artificial Intelligence Method!!!(?) *
// ****************************************
double CPlayerNapoletano::GetEstimatedPoints() const
{
   double valEstimated = 0.0;
   // there is only the hidden card
   // so the Pc estimate the hidden card
   // basing (psychology!) on the average of the old hidden cards
   if (mHand.size() <= 1)
      valEstimated = mEstimatedCard;
   else
   // Pc must know (it's a rule) when we have 7.5 points
   if (mPoints == 7.5)
      valEstimated = 7.5;
   else
   {
      int fam, valHidden;
      // this is the hidden card, and pc must not know about it!
      mHand[0]->GetFamVal(fam, valHidden);
      // this is the value of the points on the table (without hidden card)
      double valTable = mPoints - ((valHidden > 7) ? 0.5 : valHidden);
      // this is the value that Pc estimates

      // there are 12 cards on the total of 40 with 0.5 points, so mEstimatedCard must know it!
      srand(time( NULL ) );
      int randEstim;
      randEstim = rand()%40;         
      if (randEstim < 12)
         valEstimated = 0.5 + valTable;
      else
         valEstimated = mEstimatedCard + valTable;
      // if on the table there are 6.5 points
      // the Pc knows exactly the real points!
      // (if we had 7.5 points we had to declare it for the rule!)
      if (valTable == 6.5)
         valEstimated = mPoints;
      else
      // if points are 7.5 the rule says that we have to declare it
      // so the Pc will know exactly the points.
      // And if Pc estimated more than 7.5 points, we have to correct it!
      if (valEstimated > 7.0)
         valEstimated = 7.0;
   }
   return valEstimated;
}
 
void CPlayerNapoletano::ResetHand()
{
   if (!mHand.empty())
   {
      static unsigned int count = 1;   
      int fam, valHidden;
      mHand[0]->GetFamVal(fam, valHidden);
      count++;
      // average estimation of hidden card
      mEstimatedCard = ((count-1)*mEstimatedCard + valHidden) / count;
      mJollyValue = 0.0;
      mPoints = 0.0;
      mHand.erase(mHand.begin(), mHand.end());
   }
}

int GameNapoletano::Try(CPlayer *pp)
{
   int gameResult = game::GAME_OK;
   if (!dn.GiveMeACard((const CGameCard* &)pCard))
   {
      Reshuffle();
      dn.GiveMeACard((const CGameCard* &)pCard);
   }
   gameResult = pp->AddCard((CGameCard *)pCard);
   pp->AddHand((CGameCard *)pCard);
   return gameResult;
}

GameNapoletano::GameNapoletano()
{
   pMe = new CPlayerNapoletano("Maurus");
   pPc = new CPlayerNapoletano("Pc");
   Init();
}

void GameNapoletano::Init()
{
   dn.ShuffleDeck();
   gameResult = game::GAME_OK;
   pCard = NULL;
}

void GameNapoletano::Reshuffle()
{
   dn.Reshuffle(mOutCards);
   mOutCards.erase(mOutCards.begin(), mOutCards.end());
   Init();
}

void GameNapoletano::ResetPoints()
{
   pPc->SetPoints(0.0);
   pMe->SetPoints(0.0);
}

int GameNapoletano::Play(int who)
{
   if (who == game::ME)
      gameResult = Try(pMe);
   else
   {
      gameResult = Try(pPc);
      if (gameResult != game::GAME_LOST)
      {
         // Pc has 7.5, so it wins!
         if (pPc->GetPoints() > 7.0) 
               gameResult = game::GAME_WON;
         else
         // Pc estimates to have more than Me
         if (pPc->GetPoints() >= pMe->GetEstimatedPoints())
         {
            // this is the real result!
            if (pPc->GetPoints() >= pMe->GetPoints())
               gameResult = game::GAME_WON;
            else
               gameResult = game::GAME_LOST;
         }
      }
   }
   if ((gameResult == game::GAME_LOST) || (gameResult == game::GAME_WON))
   {
      // we save all the cards in hand, to the mOutCards vector
      // because we need to know them, when we have to reshuffle the deck
      for(VecPCards::iterator it1 = pMe->mHand.begin(); it1 != pMe->mHand.end(); it1++)
         mOutCards.push_back(*it1);     
      for(VecPCards::iterator it2 = pPc->mHand.begin(); it2 != pPc->mHand.end(); it2++)
         mOutCards.push_back(*it2);     
      pMe->ResetHand();
      pPc->ResetHand();
   }
   return gameResult;
}


