/**********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *    
 *   Copyright (C) 2001-2006  by Diether Knof and Borg Enders
 *
 *   This program is free software; you can redistribute it and/or 
 *   modify it under the terms of the GNU General Public License as 
 *   published by the Free Software Foundation; either version 2 of 
 *   the License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details. 
 *   You can find this license in the file 'gpl.txt'.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
 *   MA  02111-1307  USA
 *
 *  Contact:
 *    Diether Knof dknof@gmx.de
 *    Borg Enders  borg@borgsoft.de
 *
 *********************************************************************/

#include "constants.h"

#include "cards_information.h"
#include "ai.h"

#include "../../party/rule.h"

/*
 * CardsInformation::OfPlayer contains the information an ai has about the
 * cards a specific player can / must / cannot have.
 * The class contains four counters
 * - played
 *   what cards the player has played
 * - must have
 *   which and how many cards the player must have
 *   (p.e. if he has announced 're' and not played a club queen, he must
 *    have one (but can have two) )
 * - can have
 *   which and how many cards the player can have at max
 *   (p.e. if another player has annouced 're' he can only have one club queen)
 * - does not have tcolor
 *   just remembers the colors the player cannot have anymore
 *   (because he could not serve).
 *   This should not really be necessary anymore, but I feel more sure with this
 *   code (should check someday)
 *
 * The easy part of the code is to add the information gotten by the game
 * (see CardsInformation).
 * Complicated is to update the information over the players.
 * - if the cards a player has to play == the number he must have
 *   the player can only have the cards he must have
 *   (must have --> can have)
 *   A bit more information is gained, like: 
 *     If the player has only one free card (the rest to play are 'must') he
 *     can only have at most one card from each card he must not have).
 * - if the cards a player has to play == the number he can have
 *   the player must have the cards he can have
 *   (can have --> must have)
 *   This is analogous to the previous case, so here also a bit more information
 *   can be gained (and yes, there was at least one case I needed this), p.e.
 *     The player has to play two cards
 *     and he can have one diamond jack and two heart jacks,
 *     he must have at least one heart jack.
 * - count data for a single card
 *   Sum up how many times the card was played and the other players must have
 *   it. Then the player can at max have the remaining number of cards.
 *   If p.e. a player has announced 're' he must have a club queen. So the other
 *     players can only have at max one club queen in their hands.
 *   And again this can be viewed from the opposite side:
 *   If for a card the sum of 'played' and 'can have' of the other players
 *   are less than two, then the player must have the remaining ones.
 *   A simple (and most common) example is that three players do not have
 *   spade, so the fourth player must have all remaining spade cards.
 */

/**
 ** constructor
 **
 ** @param	cards_information	corresponding cards information
 ** @param	playerno		number of the player of this information
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
CardsInformation::OfPlayer::OfPlayer(CardsInformation& cards_information,
				     unsigned const playerno) :
  cards_information_(&cards_information),
  playerno_(playerno),
  played_(),
  must_have_(),
  can_have_(),
  does_not_have_tcolor_()
{
  this->reset();
}

/**
 ** copy constructor
 **
 ** @param	of_player	object to be copied
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
CardsInformation::OfPlayer::OfPlayer(OfPlayer const& of_player) :
  cards_information_(of_player.cards_information_),
  playerno_(of_player.playerno_),
  played_(of_player.played_),
  must_have_(of_player.must_have_),
  can_have_(of_player.can_have_),
  does_not_have_tcolor_(of_player.does_not_have_tcolor_)
{ }

/**
 ** copy operator
 **
 ** @param	of_player	object to be copied
 **
 ** @return	object
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
CardsInformation::OfPlayer&
CardsInformation::OfPlayer::operator=(OfPlayer const& of_player)
{
  this->cards_information_ = of_player.cards_information_;
  this->playerno_ = of_player.playerno_;
  this->played_ = of_player.played_;
  this->must_have_ = of_player.must_have_;
  this->can_have_ = of_player.can_have_;
  this->does_not_have_tcolor_ = of_player.does_not_have_tcolor_;

  return *this;
} // CardsInformation::OfPlayer& CardsInformation::OfPlayer::operator=(OfPlayer of_player)

/**
 ** destructor
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
CardsInformation::OfPlayer::~OfPlayer()
{ }

/**
 ** resets all information
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::OfPlayer::reset()
{
  this->played_.clear();
  this->must_have_.clear();
  this->does_not_have_tcolor_.clear();
  for (vector<Card>::const_iterator
       c = this->game().rule().valid_cards().begin();
       c != this->game().rule().valid_cards().end();
       ++c)
    this->can_have_.set(*c, this->game().rule()(Rule::NUMBER_OF_SAME_CARDS));

  return ;
} // void CardsInformation::OfPlayer::reset()

/**
 ** writes 'of_player' in 'ostr'
 **
 ** @param	ostr		output stream
 ** @param	of_player	object to write into 'ostr'
 **
 ** @return	the output stream
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
ostream&
operator<<(ostream& ostr, CardsInformation::OfPlayer const& of_player)
{
  of_player.write(ostr);
  return ostr;
} // ostream& operator<<(ostream& ostr, CardsInformation::OfPlayer of_player)

/**
 ** writes 'of_player' in 'ostr'
 **
 ** @param	ostr		output stream
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::OfPlayer::write(ostream& ostr) const
{
  ostr << "player = " << this->playerno() << '\n';

  ostr << "{\n";
  ostr << "  played\n"
    << "  {\n"
    << this->played_
    << "  }\n";

  ostr << "  must_have\n"
    << "  {\n"
    << this->must_have_
    << "  }\n";

  ostr << "  can_have\n"
    << "  {\n"
    << this->can_have_
    << "  }\n";

  ostr << "  does_not_have_tcolor\n"
    << "  {\n";
  for (map<Card::TColor, bool>::const_iterator
       x = this->does_not_have_tcolor_.begin();
       x != this->does_not_have_tcolor_.end();
       ++x)
    if (x->second)
      ostr << "  " << x->first << '\n';
  ostr << "  }\n";
  ostr << "}\n";

  return ;
} // void CardsInformation::OfPlayer::write(ostream& ostr) const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the corresponding game
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
Game const&
CardsInformation::OfPlayer::game() const
{
  return this->cards_information().game();
} // Game CardsInformation::OfPlayer::game() const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the corresponding player
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
Player const&
CardsInformation::OfPlayer::player() const
{
  return this->game().player(this->playerno());
} // Player CardsInformation::OfPlayer::player() const

/**
 ** sets the corresponding cards information
 **
 ** @param	cards_information	new cards information
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::OfPlayer::set_cards_information(CardsInformation&
						  cards_information)
{
  this->cards_information_ = &cards_information;
  return ;
} // void CardsInformation::OfPlayer::set_cards_information(CardsInformation& cards_information)

/**
 ** -> result
 **
 ** @param	card	card
 **
 ** @return	how many of 'card' the player has played
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
unsigned
CardsInformation::OfPlayer::played(Card const& card) const
{
  return this->played_[card];
} // unsigned CardsInformation::OfPlayer::played(Card card) const

/**
 ** -> result
 **
 ** @param	tcolor	tcolor
 **
 ** @return	how many cards of the tcolor 'tcolor' the player has played
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
unsigned
CardsInformation::OfPlayer::played(Card::TColor const& tcolor) const
{
  unsigned n = 0;

  for (CardCounter::const_iterator c = this->played_.begin();
       c != played_.end();
       ++c)
    if ((tcolor == Card::TRUMP)
	? c->first.istrump(this->game())
	: ((c->first.color() == tcolor)
	   && !c->first.istrump(this->game())))
      n += c->second;

  return n;
} // unsigned CardsInformation::OfPlayer::played(Card::TColor tcolor) const

/**
 ** -> result
 **
 ** @param	card	card
 **
 ** @return	how many cards of 'card' the player must still have on the hand
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
unsigned
CardsInformation::OfPlayer::must_have(Card const& card) const
{
  return this->must_have_[card];
} // unsigned CardsInformation::OfPlayer::must_have(Card const& card) const

/**
 ** -> result
 **
 ** @param	card	card
 **
 ** @return	how many cards of 'card' the player can still have on the hand
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
unsigned
CardsInformation::OfPlayer::can_have(Card const& card) const
{
  return this->can_have_[card];
} // unsigned CardsInformation::OfPlayer::can_have(Card const& card) const

/**
 ** -> result
 **
 ** @param	card	card
 **
 ** @return	whether the player cannot have the card still on the hand
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
bool
CardsInformation::OfPlayer::cannot_have(Card const& card) const
{
  return (this->can_have(card) == 0);
} // bool CardsInformation::OfPlayer::cannot_have(Card const& card) const

/**
 ** -> result
 **
 ** @param	tcolor	tcolor to check
 **
 ** @return	whether the player does not have anymore the tcolor
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
bool
CardsInformation::OfPlayer::does_not_have(Card::TColor const& tcolor) const
{
  return this->does_not_have_tcolor_[tcolor];
} // bool CardsInformation::OfPlayer::does_not_have(Card::TColor tcolor) const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the possible hand of the player
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **
 ** @todo	check whether the cards the player must have are as many as the
 **		player can only have
 ** @todo	add the played cards
 **/
Hand
CardsInformation::OfPlayer::possible_hand() const
{
  DEBUG_ASSERTION((this->can_have_.cards_no()
		   >= this->player().cards_to_play()),
		  "CardsInformation::OfPlayer::possible_hand():\n"
		  "  can_have.cards_no() = " << this->can_have_.cards_no()
		  << " < " << this->player().cards_to_play()
		  << " = this->player().cards_to_play()");

  DEBUG_ASSERTION((this->can_have_.cards().size()
		   == this->can_have_.cards_no()),
		  "CardsInformation::OfPlayer::possible_hand():\n"
		  "  the number of cards is not valid");

  return Hand(this->player(),
	      this->can_have_.cards(), this->played_.cards());
} // Hand CardsInformation::OfPlayer::possible_hand() const

/**
 ** the player has played 'card'
 **
 ** @param	card	card that has been played
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::OfPlayer::card_played(HandCard const& card)
{
#ifdef DEBUG_ASSERT
  if (!this->can_have(card)) {
    cerr << "--\n"
      << "Error in 'CardsInformation::OfPlayer(" << this->playerno() << ")"
      << "::card_played(" << card << ")\n";
    cerr << '\n';
    cerr << "trick " << this->game().trick_current_no() << '\n';
    cerr << '\n';
    //cerr << this->cards_information() << endl;
    cerr << "Hand:\n" << this->player().hand();
    cerr << '\n';
    cerr << *this;
    cerr << endl;
  }
#endif
  DEBUG_ASSERTION(this->can_have(card),
		  "CardsInformation::OfPlayer::card_played(" << card << "):\n"
		  "  card cannot be on the hand.\n"
		  << "  information of player = " << this->cards_information().player().no() << '\n'
		  << "  player = " << this->playerno() << '\n'
		  << "  trick = " << this->game().trick_current_no() << '\n'
		  //<< "  Game:\n" << this->game()
		  //<< "  Information:\n" << this->cards_information()
		  //<< "  Hand:\n" << this->player().hand()
		 );

  this->played_.inc(card);
  if (this->must_have_[card])
    this->must_have_.dec(card);
  this->can_have_.dec(card);

  if (!this->cards_information().in_recalcing())
    this->update_remaining_cards();

  return ;
} // void CardsInformation::OfPlayer::card_played(HandCard const& card)

/**
 ** adds the information that the player must have the card 'no' times
 **
 ** @param	card	card that the player must have
 ** @param	no	how many times the player must have the card
 **			default: 1
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::OfPlayer::add_must_have(Card const& card, unsigned const no)
{
  if (no > this->can_have(card)) {
    DEBUG_ASSERTION(this->game().isvirtual(),
		    "CardsInformation::OFPlayer::add_must_have"
		    "(" << card << ", " << no << ")\n"
		    "  player = " << this->playerno() << "\n"
		    "  can_have(card) = " << this->can_have(card)
		    << " < " << no << " = no");
    DEBUG_THROW(InvalidGameException, InvalidGameException());
  }

  if (this->must_have_.min_set(card, no))
    this->cards_information().update_information(card);

  return ;
} // void CardsInformation::OfPlayer::add_must_have(Card card, unsigned no = 1)

/**
 ** adds the information that the player must have the cards
 **
 ** @param	cards	cards the player must have
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::OfPlayer::add_must_have(vector<Card> const& cards)
{
  map<Card, unsigned> number;
  for (vector<Card>::const_iterator c = cards.begin();
       c != cards.end();
       ++c)
    number[*c] += 1;

  //for (map<Card, unsigned>::const_iterator n = number.begin();
  for (map<Card, unsigned>::const_iterator n = number.begin();
       n != number.end();
       ++n)
    this->add_must_have(n->first, n->second);

  return ;
} // void CardsInformation::OfPlayer::add_must_have(vector<Card> cards)

/**
 ** adds the information that the player can have the card at max 'no' times
 **
 ** @param	card	card that the player must have
 ** @param	no	how many times the player can have the card at max
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::OfPlayer::add_can_have(Card const& card, unsigned const no)
{
  DEBUG_ASSERTION((no >= this->must_have(card)),
		  "CardsInformation::OfPlayer::add_can_have("
		  << card << ", " << no << "):\n"
		  "  no < " << this->must_have(card) << " = must have\n"
		  "  player = " << this->playerno());

  if (this->can_have_.max_set(card, no))
    this->cards_information().update_information(card);

  return ;
} // void CardsInformation::OfPlayer::add_can_have(Card card, unsigned no)

/**
 ** adds the information that the player can only have the cards
 **
 ** @param	cards	cards the player can only have
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::OfPlayer::add_can_only_have(vector<Card> const& cards)
{
  map<Card, unsigned> number;
  for (vector<Card>::const_iterator c = cards.begin();
       c != cards.end();
       ++c)
    number[*c] += 1;

  // remove all entries in 'can_have' which have the value '0'
  // count the number of cards the player can have
  list<Card> to_remove;
  for (CardCounter::const_iterator x = this->can_have_.begin();
       x != this->can_have_.end();
       ++x) {
    map<Card, unsigned>::const_iterator n = number.find(x->first);
    if (n == number.end()) {
      DEBUG_ASSERTION((this->must_have(x->first) == 0),
		      "CardsInformation::OfPlayer::add_can_only_have():\n"
		      "  player " << this->playerno() << "\n"
		      "  card '" << x->first << "':\n"
		      "    new number = 0 < "
		      << this->must_have(x->first) << " = must_have");
      to_remove.push_back(x->first);
      continue;
    }

    DEBUG_ASSERTION((n->second >= this->must_have(x->first)),
		    "CardsInformation::OfPlayer::add_can_only_have():\n"
		    "  card '" << x->first << "':\n"
		    "    new number = " << n->second << " < "
		    << this->must_have(x->first) << " = must_have");
  } // for (n \in number)

  // remove the cards with value '0' from the list
  if (!to_remove.empty())
    for (list<Card>::const_iterator c = to_remove.begin();
	 c != to_remove.end();
	 ++c) {
      DEBUG_ASSERTION((this->must_have(*c) == 0),
		      "CardsInformation::OfPlayer::add_can_only_have():\n"
		      "  card '" << *c << "':\n"
		      "    new number = 0 < "
		      << this->must_have(*c) << " = must_have");

      this->add_cannot_have(*c);
    }

  // set the maximal number of the cards
  for (map<Card, unsigned>::const_iterator n = number.begin();
       n != number.end();
       ++n)
    if (this->can_have_.max_set(n->first, n->second))
      this->cards_information().update_information(n->first);

  this->check_can_is_must();

  if (   (::game_status != GAMESTATUS::GAME_REDISTRIBUTE)
      && (this->can_have_.cards_no() < this->player().cards_to_play())) {
    DEBUG_ASSERTION(this->game().isvirtual(),
		    "CardsInformation::OFPlayer::add_can_only_have(cards)\n"
		    "  player = " << this->playerno() << "\n"
		    "  can_have.cards_no() = " << this->can_have_.cards_no()
		    << " < "
		    << this->player().cards_to_play() << " = cards to play");
    DEBUG_THROW(InvalidGameException, InvalidGameException());
  }

  return ;
} // void CardsInformation::OfPlayer::add_can_only_have(vector<Card> cards)

/**
 ** adds the information that the player cannot have the card
 **
 ** @param	card	card the player cannot have
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::OfPlayer::add_cannot_have(Card const& card)
{
  if (this->must_have(card)) {
    DEBUG_ASSERTION(this->game().isvirtual(),
		    "CardsInformation::OFPlayer::add_cannot_have"
		    "(" << card << ")\n"
		    "  player = " << this->playerno() << "\n"
		    "  must_have(card) = " << this->must_have(card) << " > 0");
    DEBUG_THROW(InvalidGameException, InvalidGameException());
  }

  if (this->can_have_.erase(card)) {
    DEBUG_ASSERTION((this->must_have(card) == 0),
		    "CardsInformation::OfPlayer::add_cannot_have("
		    << card << "):\n"
		    << "  must_have = " << this->must_have(card) << '\n'
		    << "  player = " << this->player().no());
    if (this->can_have_.cards_no() < this->player().cards_to_play()) {
      DEBUG_ASSERTION(this->game().isvirtual(),
		      "CardsInformation::OFPlayer::add_cannot_have"
		      "(" << card << ")\n"
		      "  player = " << this->playerno() << "\n"
		      "  can_have.cards_no() = " << this->can_have_.cards_no()
		      << " < "
		      << this->player().cards_to_play() << " = cards to play");
      DEBUG_THROW(InvalidGameException, InvalidGameException());
    }

#ifdef WORKAROUND
    // if only one player can have the card, he must have it (see 'update_card')
    { // check whether all cards of 'card' are either played,
      // one player has it or it can be forgotten
      unsigned distributed = this->cards_information().played(card);
      for (unsigned p = 0; p < this->game().playerno(); ++p)
	distributed += this->cards_information().of_player(p).can_have(card);
      distributed += this->cards_information().forgotten_cards_no();

      if (distributed < this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)) {
	DEBUG_ASSERTION(this->game().isvirtual(),
			"CardsInformation::OFPlayer::add_cannot_have"
			"(" << card << ")\n"
			"  player = " << this->playerno() << "\n"
			"  the card is not distributed");
	DEBUG_THROW(InvalidGameException, InvalidGameException());
      }


    } // check whether all cards of 'card' are somewhere
#endif
    this->cards_information().update_information(card);
  }

  return ;
} // void CardsInformation::OfPlayer::add_cannot_have(Card card)

/**
 ** adds the information that the player cannot have the cards
 **
 ** @param	cards	cards the player cannot have
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::OfPlayer::add_cannot_have(vector<Card> const& cards)
{
  for (vector<Card>::const_iterator c = cards.begin();
       c != cards.end();
       ++c)
    this->add_cannot_have(*c);

  return ;
} // void CardsInformation::OfPlayer::add_cannot_have(vector<Card> cards)

/**
 ** adds the information that the player cannot have the tcolor
 **
 ** @param	tcolor	tcolor the player cannot have
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::OfPlayer::add_does_not_have(Card::TColor const tcolor)
{
  this->does_not_have_tcolor_[tcolor] = true;

  // now erase the 'can_have' for all cards of 'tcolor'

  if (tcolor == Card::TRUMP) {
    for (vector<Card>::const_iterator
	 c = this->game().rule().valid_cards().begin();
	 c != this->game().rule().valid_cards().end();
	 ++c) {
      if (c->istrump(this->game()))
	this->add_cannot_have(*c);
    } // for (c \in valid_cards)
  } else {
    for (vector<Card::Value>::const_iterator
	 v = this->game().rule().valid_card_values().begin();
	 v != this->game().rule().valid_card_values().end();
	 ++v) {
      Card const card(tcolor, *v);
      if (!card.istrump(this->game()))
	this->add_cannot_have(card);
    } // for (v \in valid_card_values)
  } // if !(tcolor == Card::TRUMP)

  return ;
} // void CardsInformation::OfPlayer::add_does_not_have(Card::TColor tcolor)

/**
 ** updates the information of the card
 ** throws InvalidGameException
 **
 ** @param	card	card to update the information of
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::OfPlayer::update_information(Card const& card)
{
  if (this->cards_information().in_recalcing())
    return ;

#ifdef OUTDATED
  // 0.7.0
  if (!this->can_have_[card]) {
    // this check is necessary:
    //   a card has been played which is not 'must_have', then
    //   'can_have.cards_no()' is decreased but not 'must_have.cards_no()'
    this->check_can_is_must();
    this->check_must_is_can();
    return ;
  }
#endif

  // whether the information of the card must be updated
  bool update_information = false;
  // how many times this card was played
  unsigned const played = this->cards_information().played(card);
  // the number of forgotten cards
  unsigned const forgotten_cards
    = this->cards_information().forgotten_cards_no();
  // count how many of the card the other player must have
  unsigned must_have = 0;
  // count how many of the card the other player can have
  unsigned can_have = 0;
  for (unsigned p = 0; p < this->game().playerno(); ++p)
    if (p != this->playerno()) {
      must_have += this->cards_information().of_player(p).must_have(card);
      can_have += this->cards_information().of_player(p).can_have(card);
    }

#ifndef WORKAROUND
  // the cards distribution shall be sufficient
  // so that this case does not happen
  if ((this->cards_information().played(card) + must_have
       > this->game().rule()(Rule::NUMBER_OF_SAME_CARDS))
      && this->player().game().isvirtual())
    DEBUG_THROW(InvalidGameException, InvalidGameException());
#endif

#ifndef OUTDATED
  // see bug report 232744

  if (this->game().isvirtual()) {
    if (played + must_have > this->game().rule()(Rule::NUMBER_OF_SAME_CARDS))
      DEBUG_THROW(InvalidGameException, InvalidGameException());
  } else
#endif
    DEBUG_ASSERTION((played + must_have
		     <= this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)),
		    "CardsInformation::OfPlayer::update_information(" << card << "):\n"
		    "  played + must_have > max number:\n"
		    "  " << played
		    << " + " << must_have << " > "
		    << this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)
		   );
  // the number of cards this player can at most have
  unsigned const n = (this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)
		      - played
		      - must_have);

  if (n < this->must_have(card)) {
    DEBUG_ASSERTION(this->game().isvirtual(),
		    "CardsInformation::OFPlayer::update_information()\n"
		    "  player = " << this->playerno() << "\n"
		    "  card = " << card << "\n"
		    "  number of same cards - played - must_have total"
		    " < must_have(card):\n"
		    << this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)
		    << " - " << played
		    << " - " << must_have
		    << " < " << this->must_have(card));
    DEBUG_THROW(InvalidGameException, InvalidGameException());
  }

  if (this->can_have_.max_set(card, n))
    update_information = true;

  // check whether a player must have the remaining cards of 'card'
  if (can_have + played + this->must_have(card) + forgotten_cards
      < this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)) {
    this->add_must_have(card,
			this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)
			- can_have - played - forgotten_cards);
    update_information = true;
  }

  if (update_information) {
    this->cards_information().update_information(card);
  } else { // if !(update_information)
    this->check_can_is_must();
    this->check_must_is_can();
  } // if !(update_information)

  if (this->can_have_.cards_no()
      < this->player().cards_to_play()) {
    //COUT << this->cards_information() << endl;
    DEBUG_ASSERTION(this->game().isvirtual(),
		    "CardsInformation::OFPlayer::update_information()\n"
		    "  player = " << this->playerno() << "\n"
		    "  can_have.cards_no() = " << this->can_have_.cards_no()
		    << " < "
		    << this->player().cards_to_play() << " = cards to play");
    DEBUG_THROW(InvalidGameException, InvalidGameException());
  }

  if (this->must_have_.cards_no() > this->player().cards_to_play()) {
    DEBUG_ASSERTION(this->game().isvirtual(),
		    "CardsInformation::OFPlayer::update_information()\n"
		    "  player = " << this->cards_information().player().no() << "\n"
		    "  of player = " << this->playerno() << "\n"
		    "  must_have.cards_no() = " << this->must_have_.cards_no()
		    << " > "
		    << this->player().cards_to_play() << " = cards to play");

    DEBUG_THROW(InvalidGameException, InvalidGameException());
  }

  return ;
} // void CardsInformation::OfPlayer::update_information(Card card)

/**
 ** updates 'can have' according to 'remaining cards'
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
void
CardsInformation::OfPlayer::update_remaining_cards()
{
  unsigned const remaining_cards_no
    = (this->player().cards_to_play() - this->must_have_.cards_no());

  if ( !(remaining_cards_no
	 < this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)) )
    return ;
  if ( !(this->must_have_.cards_no() != this->can_have_.cards_no()) )
    return ;

  set<Card> cards;
  for (CardCounter::const_iterator c = this->can_have_.begin();
       c != this->can_have_.end();
       ++c) {
    if (this->can_have(c->first)
	> remaining_cards_no - this->must_have(c->first))
      cards.insert(c->first);
  } // for (c \in this->can_have_)
  for (set<Card>::const_iterator c = cards.begin();
       c != cards.end();
       ++c)
    this->add_can_have(*c, remaining_cards_no + this->must_have(*c));

  return ;
} // void CardsInformatio::OfPlayern::update_remaining_cards()

/**
 ** checks whether the cards which the player can have are the ones
 ** he has to have
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::OfPlayer::check_can_is_must()
{
  if ( !(this->can_have_.cards_no() == this->player().cards_to_play()))
    return ;

  // so all 'can_have' are 'must_have'

  if ( !(this->can_have_.cards_no() != this->must_have_.cards_no()))
    return ;

  list<Card> update_cards;
  for (CardCounter::const_iterator c = this->can_have_.begin();
       c != this->can_have_.end();
       ++c) {
    if (this->must_have_[c->first] != c->second) {
      if (this->must_have_.min_set(c->first, c->second))
	update_cards.push_back(c->first);
    }
  } // for (c \in this->can_have_)

  for (list<Card>::const_iterator c = update_cards.begin();
       c != update_cards.end();
       ++c)
    this->cards_information().update_information(*c);

  return ;
} // void CardsInformation::OfPlayer::check_can_is_must()

/**
 ** check whether the cards the player has to have are the only ones
 ** he can have
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::OfPlayer::check_must_is_can()
{
  if ( !(this->must_have_.cards_no() == this->player().cards_to_play()))
    return ;

  // so all 'must_have_' are 'can_have'

  if ( !(this->must_have_.cards_no() != this->can_have_.cards_no()))
    return ;

  list<Card> update_cards;
  for (CardCounter::const_iterator c = this->can_have_.begin();
       c != this->can_have_.end();
       ++c) {
    unsigned const must_have = this->must_have(c->first);
    if (c->second != must_have) {
      //if (this->can_have_.max_set(c->first, must_have))
      update_cards.push_back(c->first);
    }
  }

  list<Card> update_cards2;
  for (list<Card>::const_iterator c = update_cards.begin();
       c != update_cards.end();
       ++c)
    if (this->can_have_.max_set(*c, this->must_have(*c)))
      update_cards2.push_back(*c);

  for (list<Card>::const_iterator c = update_cards2.begin();
       c != update_cards2.end();
       ++c)
    this->cards_information().update_information(*c);

  return ;
} // void CardsInformation::OfPlayer::check_must_is_can()

/**
 ** -> result
 **
 ** @param	of_player_a	first object
 ** @param	of_player_b	second object
 **
 ** @return	whether the two objects are equal
 **		(the correspondign cards information may differ)
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
bool
operator==(CardsInformation::OfPlayer const& of_player_a,
	   CardsInformation::OfPlayer const& of_player_b)
{
  return ( (of_player_a.playerno_
	    == of_player_b.playerno_)
	  && (of_player_a.played_
	      == of_player_b.played_)
	  && (of_player_a.must_have_
	      == of_player_b.must_have_)
	  && (of_player_a.can_have_
	      == of_player_b.can_have_)
	  && (of_player_a.does_not_have_tcolor_
	      == of_player_b.does_not_have_tcolor_) );
} // bool operator==(CardsInformation::OfPlayer of_player_a, CardsInformation::OfPlayer of_player_b)

/**
 ** -> result
 **
 ** @param	of_player_a	first object
 ** @param	of_player_b	second object
 **
 ** @return	whether the two objects are different
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
bool
operator!=(CardsInformation::OfPlayer const& of_player_a,
	   CardsInformation::OfPlayer const& of_player_b)
{
  return !(of_player_a == of_player_b);
} // bool operator!=(CardsInformation::OfPlayer of_player_a, CardsInformation::OfPlayer of_player_b)

/**
 ** self check
 ** when an error is found, an ASSERTION is created
 **
 ** @param	-
 **
 ** @return	whether the self-check was successful (no error)
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
bool
CardsInformation::OfPlayer::self_check() const
{
  // the player has to have at least as many cards as he has to play
  if ( !(this->can_have_.cards_no() >= this->player().cards_to_play())) {
    DEBUG_ASSERTION(false,
		    "CardsInformation::OfPlayer::self_check():\n"
		    "  playerno = " << this->playerno() << "\n"
		    "  can_have < cards to play:\n"
		    << "  " << this->can_have_.cards_no()
		    << " < " << this->player().cards_to_play());
    return false;
  }

  // the player has to be able to play all 'must' cards
  if ( !(this->must_have_.cards_no() <= this->player().cards_to_play())) {
#ifdef DEBUG_ASSERT
    //cerr << this->cards_information() << endl;
#endif
    DEBUG_ASSERTION(false,
		    "CardsInformation::OfPlayer::self_check():\n"
		    "  playerno = " << this->playerno() << "\n"
		    "  must_have > cards to play:\n"
		    << "  " << this->must_have_.cards_no()
		    << " > " << this->player().cards_to_play());
    return false;
  }

  // check 'can have == cards to play' ==> 'can have == must have'
  if ( !(   (this->can_have_.cards_no() != this->player().cards_to_play())
	 || (this->can_have_.cards_no() == this->must_have_.cards_no()) ) ) {
    DEBUG_ASSERTION(false,
		    "CardsInformation::OfPlayer::self_check():\n"
		    "  playerno = " << this->playerno() << "\n"
		    "  cards_to_play == can_have != must_have:\n"
		    << "  " << this->player().cards_to_play()
		    << " == " << this->can_have_.cards_no()
		    << " != " << this->must_have_.cards_no()
		   );
    return false;
  }

  // for each card check: 
  for (vector<Card>::const_iterator
       c = this->game().rule().valid_cards().begin();
       c != this->game().rule().valid_cards().end();
       ++c) {
    unsigned played = this->cards_information().played(*c);
    { // played(player) <= played(all)
      if (played < this->played(*c)) {
	DEBUG_ASSERTION(false,
			"CardsInformation::OfPlayer::self_check():\n"
			"  playerno = " << this->playerno() << "\n"
			"  card '" << *c << "' has been played "
			<< played << " times, but by the player "
			<< this->playerno() << " '" << this->played(*c)
			<< "' times");
	return false;
      }
    } // played(player) <= played(all)

    unsigned const can_have = this->can_have(*c);
    { // check 'can have < must have'
      if ( !(can_have >= this->must_have(*c))) {
#ifdef DEBUG_ASSSERT
	//cerr << this->cards_information();
#endif
	DEBUG_ASSERTION(false,
			"CardsInformation::OfPlayer::self_check():\n"
			"  playerno = " << this->playerno() << "\n"
			"  card '" << *c << "': "
			"can_have = " << can_have << " < "
			"must_have = " << this->must_have_[*c]
		       );
	return false;
      }
    } // check 'can have < must have'

    { // check 'can have < cards to play'
      if ( !(can_have <= this->player().cards_to_play())) {
#ifdef DEBUG_ASSSERT
	//cerr << this->cards_information();
#endif
	DEBUG_ASSERTION(false,
			"CardsInformation::OfPlayer::self_check():\n"
			"  playerno = " << this->playerno() << "\n"
			"  card '" << *c << "': "
			"can_have = " << can_have << " > "
			"cards to play = " << this->player().cards_to_play()
		       );
	return false;
      }
    } // check 'can have < cards to play'

    { // check 'can have + played + sum must have <= #Cards'
      unsigned sum = 0;
      for (unsigned p = 0; p < this->game().playerno(); ++p)
	if (p != this->playerno())
	  sum += this->cards_information().of_player(p).must_have(*c);

      if (can_have + played + sum
	  > this->game().rule()(Rule::NUMBER_OF_SAME_CARDS) ) {
#ifdef DEBUG_ASSERT
	for (unsigned p = 0; p < this->game().playerno(); ++p) {
	  if (p != this->playerno())
	    if (this->cards_information().of_player(p).must_have(*c) > 0)
	      cerr << p << ": must have = " << this->cards_information().of_player(p).must_have(*c) << endl;
	}
#endif
	DEBUG_ASSERTION(false,
			"CardsInformation::OfPlayer::self_check():\n"
			"  playerno = " << this->playerno() << "\n"
			"  card '" << *c << "':\n"
			"  can_have + played + Sum must_have > NUMBER_OF_CARDS:\n"
			"  "
			<< can_have
			<< " + " << played << " + " << sum << " > "
			<< this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)
			//<< "\ngame:\n{\n" << this->game() << "}\n"
			//<< "\n" << this->cards_information()
		       );

	return false;
      }
    } // check 'can have + played + sum must have <= #Cards'
  } // for (c \in valid_cards)

  { // checked '#played intern <= #played by player'
    unsigned played_cardno = 0;
    for (map<Card, unsigned>::const_iterator p = this->played_.begin();
	 p != this->played_.end();
	 ++p)
      played_cardno += p->second;
    unsigned const played_cardno_by_player
      = (this->game().trickno() - this->player().cards_to_play());
    if (played_cardno > played_cardno_by_player) {
      DEBUG_ASSERTION(false,
		      "CardsInformation::OfPlayer::self_check():\n"
		      "  playerno = " << this->playerno() << "\n"
		      "  " << played_cardno << " cards are marked as played, "
		      "but the player has played "
		      << played_cardno_by_player
		      << "\n" << this->game());
      return false;
    }
  } // checked '#played intern <= #played by player'

  return true;
} // bool CardsInformation::OfPlayer::self_check() const
