/**********************************************************************
 *
 *   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 "cards_information.of_player.h"

#include "ai.h"

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

// whether to make self checks
// local macro
#ifndef DEBUG_NO
#ifndef RELEASE
#define SELF_CHECK
#endif
#endif

/*
 * CardsInformation contains the information a player has of the distribution
 * of the cards in a game. The player does not necessarily be an active player,
 * The 'user' of this class has to call the methods corresponding to the
 * gameplay.
 * Information can be contained from the methods 'possible_hand(Player)',
 * 'estimated_hand(Player) and with 'of_player(Player)' specific information
 * about a player.
 *
 * According to the number of remembered tricks the first played cards are
 * forgotten (and with it that a player does not have the color -- 
 * nearly just as if the first tricks did not exists at all.
 * In virtual games there is no additional trick lost when calculating one
 * trick further.
 *
 * The idea is to take all information from the gameplay to tell which cards
 * - are played
 * - a specific player must have
 * - a specific player cannot have
 * Merging the informations between the players (see CardsInformation::OfPlayer)
 * more data can be created (if par example three players do not have spade,
 * then the last one must have all remaining spade cards).
 *
 * The collected informations are
 * - the hand of the ai is set
 *   the other players cannot have the cards
 *   (in virtual games a bit more complicated, since the hand can contain more
 *    cards than the ai has to play)
 * - a card is played
 *   the card does not longer remain, it is marked as played by the player
 * - a player has not served a color
 *   the player has no more card of the color
 * - announcement re (normal game)
 *   the player has at least one club queen (can be already played)
 * - announcement contra (normal game)
 *   the player has no club queen
 * - marriage
 *   the player has both club queens
 * - poverty
 *   the poverty player has as many trumps as he has gotten back
 * - announcement swines / hyperswines / genscher
 *   the player has both trump aces / nines / kings
 *
 * When more information was collected the information about a player is
 * updated as good as I cound program it (see 'OfPlayer').
 */

/*
 * Implemention remark
 * the class cannot be an 'OS' because then the functions are not called in
 * virtual games
 */

/**
 ** constructor
 **
 ** @param	player			corresponding player
 ** @param	remembertrickno		how many tricks are remembered
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
CardsInformation::CardsInformation(Ai const& player) :
  player_(&player),
  of_player_(),
  played_(),
  in_recalcing_(false)
{
  for (unsigned p = 0; p < this->game().playerno(); ++p)
    this->of_player_.push_back(OfPlayer(*this, p));
}

/**
 ** copy constructor
 **
 ** @param	cards_information	object to be copied
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
CardsInformation::CardsInformation(CardsInformation const& cards_information) :
  player_(cards_information.player_),
  of_player_(cards_information.of_player_),
  played_(cards_information.played_),
  in_recalcing_(cards_information.in_recalcing_)
{
  for (vector<OfPlayer>::iterator p = this->of_player_.begin();
       p != this->of_player_.end();
       ++p)
    p->set_cards_information(*this);

#ifdef SELF_CHECK
  this->self_check();
#endif
} // CardsInformation::CardsInformation(CardsInformation cards_information)

/**
 ** copy operator
 **
 ** @param	cards_information	object to be copied
 **
 ** @return	object
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
CardsInformation&
CardsInformation::operator=(CardsInformation const& cards_information)
{
  this->player_ = cards_information.player_;
  this->of_player_ = cards_information.of_player_;
  for (vector<OfPlayer>::iterator p = this->of_player_.begin();
       p != this->of_player_.end();
       ++p)
    p->set_cards_information(*this);

  this->played_ = cards_information.played_;

#ifdef SELF_CHECK
  this->self_check();
#endif

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

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

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

/**
 ** writes 'cards_information' in 'ostr'
 **
 ** @param	ostr		output stream
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::write(ostream& ostr) const
{
  ostr << "cards information:\n"
    << "{\n";

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

  for (vector<OfPlayer>::const_iterator p = this->of_player_.begin();
       p != this->of_player_.end();
       ++p)
    ostr << *p << '\n';

  ostr << "}\n";

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

/**
 ** resets all information
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::reset()
{
  this->played_.clear();

  for (vector<OfPlayer>::iterator p = this->of_player_.begin();
       p != this->of_player_.end();
       ++p)
    p->reset();

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

/**
 ** recalcs all information
 ** takes 'remembertricksno' into account
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::recalc()
{
  this->in_recalcing_ = true;

  this->reset();

  if (::game_status >= GAMESTATUS::GAME_PLAY) {
    // the first trick to take into account
    unsigned const trick_begin
      = ((::party.game().trick_current_no()
	  <= this->player().value(Aiconfig::REMEMBERTRICKS))
	 ? 0
	 : (::party.game().trick_current_no()
	    - this->player().value(Aiconfig::REMEMBERTRICKS)));

    { // mark the played cards
      for (unsigned t = trick_begin; t < this->game().trick_current_no(); ++t) {
	Trick const& trick = this->game().trick(t);
	Trick trick2(trick.startplayer());
	for (unsigned c = 0; c < trick.actcardno(); ++c) {
	  HandCard const& card = trick.card(c);
	  trick2 += card;
	  this->card_played(card, trick2);
	}
      }
    } // mark the played cards

    { // test for colors the players do not have
      for (unsigned t = trick_begin; t < this->game().trick_current_no(); ++t) {
	Card::TColor const tcolor = this->game().trick(t).startcard().tcolor();
	Trick const& trick = this->game().trick(t);
	for (unsigned c = 1; c < trick.actcardno(); ++c) {
	  if (trick.card(c).tcolor() != tcolor)
	    this->of_player_[trick.card(c).player().no()
	      ].add_does_not_have(tcolor);
	} // for (c < trick.actcardno())
      } // for (t < trick_current_no)
    } // test for colors the players do not have
    { // add announcements info
      for (vector<OfPlayer>::iterator p = this->of_player_.begin();
	   p != this->of_player_.end();
	   ++p) {
	if (this->game().announcement_of_player(p->player()))
	  this->announcement_made(this->game().announcement_of_player(p->player()), p->player());
      } // for (p \in this->of_player_)
    } // add announcements info
  } // if (::game_status >= GAMESTATUS::GAME_PLAY)

  { // add swines info
    if (this->game().swines_owner()) {
      Card const swine(this->game().trumpcolor(), Card::ACE);
      for (vector<OfPlayer>::iterator p = this->of_player_.begin();
	   p != this->of_player_.end();
	   ++p)
	if (p->playerno() != this->game().swines_owner()->no())
	  p->add_cannot_have(swine);

      if (this->game().hyperswines_owner()) {
	Card const hyperswine(this->game().trumpcolor(),
			      (this->game().rule()(Rule::WITH_NINES)
			       ? Card::NINE
			       : Card::KING));
	for (vector<OfPlayer>::iterator p = this->of_player_.begin();
	     p != this->of_player_.end();
	     ++p)
	  if (p->playerno() != this->game().hyperswines_owner()->no())
	    p->add_cannot_have(hyperswine);
      } // if (this->game().hyperswines_owner())
    } // if (this->game().swines_owner())
  } // add swines info
  { // add genscher info
    if (this->game().type() == GAMETYPE::GENSCHER) {
      Card const genscher_card(this->game().trumpcolor(), Card::KING);
      for (vector<OfPlayer>::iterator p = this->of_player_.begin();
	   p != this->of_player_.end();
	   ++p)
	if (p->playerno() != this->game().soloplayer().no())
	  p->add_cannot_have(genscher_card);
    }

  } // add genscher info

  if (this->player_)
    this->set_hand(this->player(), this->player().hand());

  this->in_recalcing_ = false;

  // this must be called here because it is skipped beeing in recalcing
  this->update_remaining_cards();

  this->update_information_all();

#ifdef SELF_CHECK
  this->self_check();
#endif

  return ;
} // void CardsInformation::recalc()

/**
 ** -> result
 **
 ** @param	player	the player whose information is asked for
 **
 ** @return	the information of 'player'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
CardsInformation::OfPlayer const&
CardsInformation::of_player(Player const& player) const
{
  return this->of_player(player.no());
} // CardsInformation::OfPlayer const& CardsInformation::of_player(Player player) const

/**
 ** -> result
 **
 ** @param	playerno	the player whose information is asked for
 **
 ** @return	the information of player 'playerno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
CardsInformation::OfPlayer const&
CardsInformation::of_player(unsigned const playerno) const
{
  return this->of_player_[playerno];
} // CardsInformation::OfPlayer CardsInformation::of_player(unsigned playerno) const

/**
 ** -> result
 **
 ** @param	player	the player whose hand is asked for
 **
 ** @return	hand with all cards the player can have
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
Hand
CardsInformation::possible_hand(Player const& player) const
{
  DEBUG_ASSERTION((&this->game() == &player.game()),
		  "CardsInformation::hand(player):\n"
		  "  the game of the player is not the game of the player");

  return this->of_player_[player.no()].possible_hand();
} // Hand CardsInformation::possible_hand(Player player) const

/**
 ** -> result
 **
 ** @param	player	the player whose hand is asked for
 **
 ** @return	the estimated hand for the player
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.3
 **/
Hand
CardsInformation::estimated_hand(Player const& player) const
{
  DEBUG_ASSERTION((&this->game() == &player.game()),
		  "CardsInformation::hand(player):\n"
		  "  the game of the player is not the game of the player");

  return this->of_player_[player.no()].estimated_hand();
} // Hand CardsInformation::estimated_hand(Player player) const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the highest remaining trump in the game
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.3
 **/
HandCard
CardsInformation::highest_remaining_trump() const
{
  HandCard card;
  for (vector<Player*>::const_iterator p = this->game().players_begin();
       p != this->game().players_end();
       ++p) {
    HandCard const c = this->possible_hand(**p).highest_card(Card::TRUMP);
    if (   c
	&& (   !card
	    || card.less(c)
	   )
       )
      card = c;
  }

  return card;
} // HandCard CardsInformation::highest_remaining_trump() const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the highest remaining trump of the other players in the game
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.3
 **/
HandCard
CardsInformation::highest_remaining_trump_of_others() const
{
  HandCard card;
  for (vector<Player*>::const_iterator p = this->game().players_begin();
       p != this->game().players_end();
       ++p) {
    if ((*p)->is_same(this->player()))
      continue;
    HandCard const c = this->possible_hand(**p).highest_card(Card::TRUMP);
    if (   c
	&& (   !card
	    || card.less(c)
	   )
       )
      card = c;
  }

  return card;
} // HandCard CardsInformation::highest_remaining_trump_of_others() const

/**
 ** -> result
 **
 ** @param	card   card to compare with
 **
 ** @return	whether there exists a higher card than 'card' on the other hands
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.3
 **/
bool
CardsInformation::higher_card_exists(HandCard const& card) const
{
  for (vector<Player*>::const_iterator p = this->game().players_begin();
       p != this->game().players_end();
       ++p) {
    if ((*p)->is_same(card.player()))
      continue;
    if (this->possible_hand(**p).higher_card_exists(card))
      return true;
  }

  return false;
} // bool CardsInformation::higher_card_exists(HandCard card) const


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

/**
 ** sets (changes) the player
 **
 ** @param	player	new player
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
void
CardsInformation::set_player(Ai const& player)
{
  this->player_ = &player;

  return ;
} // void CardsInformation::set_player(Ai player)

/**
 ** sets the hand of the player
 **
 ** @param	player	player whose hand is set
 ** @param	hand	the hand of the player
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::set_hand(Player const& player, Hand const& hand)
{
  if (::game_status == GAMESTATUS::GAME_POVERTY_SHIFT)
    return ;

  this->of_player_[player.no()].add_can_only_have(hand.cards());

#ifdef SELF_CHECK
  this->self_check();
#endif

  return ;
} // void CardsInformation::set_hand(Player player, Hand hand)

/**
 ** updates the information of the card
 **
 ** @param	card	card to update the information of
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::update_information(Card const& card)
{
  for (vector<OfPlayer>::iterator p = this->of_player_.begin();
       p != this->of_player_.end();
       ++p)
    p->update_information(card);

  return ;
}
/**
 ** updates the information of all cards
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.0
 **/
void
CardsInformation::update_information_all()
{
  for (vector<Card>::const_iterator
       c = this->game().rule().valid_cards().begin();
       c != this->game().rule().valid_cards().end();
       ++c)
    this->update_information(*c);

  return ;
} // void CardsInformation::update_information_all()

/**
 ** updates 'can have' according to 'remaining cards'
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
void
CardsInformation::update_remaining_cards()
{
  for (vector<OfPlayer>::iterator p = this->of_player_.begin();
       p != this->of_player_.end();
       ++p)
    p->update_remaining_cards();

  return ;
} // void CardsInformation::update_remaining_cards()

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the number of forgotten tricks
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.0
 **/
unsigned
CardsInformation::forgotten_tricks_no() const
{
  if (this->game().trick_current_no() == UINT_MAX)
    return 0;
  return ( (this->player().value(Aiconfig::REMEMBERTRICKS)
	    < ::party.game().real_trick_current_no())
	  ? (::party.game().real_trick_current_no()
	     - this->player().value(Aiconfig::REMEMBERTRICKS))
	  : 0
	 );
} // unsigned CardsInformation::forgotten_tricks_no() const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the number of forgotten cards
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.0
 **/
unsigned
CardsInformation::forgotten_cards_no() const
{
  return (this->game().playerno() * this->forgotten_tricks_no());
} // unsigned CardsInformation::forgotten_cards_no() const

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	how many cards have been played in total in the game
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
unsigned
CardsInformation::played_cards_no() const
{
  return this->played_.cards_no();
} // unsigned CardsInformation::played_cards_no() const

/**
 ** -> result
 **
 ** @param	card	card to look at
 **
 ** @return	how many of 'card' have been played
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
unsigned
CardsInformation::played(Card const& card) const
{
  return (this->played_)[card];
} // unsigned CardsInformation::played(Card card) const

/**
 ** -> result
 **
 ** @param	tcolor   tcolor to look at
 **
 ** @return	how many cards of 'tcolor' have been played
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.3
 **/
unsigned
CardsInformation::played(Card::TColor const& tcolor) const
{
  if (tcolor == Card::TRUMP) {
    unsigned n = 0;
    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()))
	n += this->played(*c);
    return n;
  } else { // if !(tcolor == Card::TRUMP)
    unsigned n = 0;
    for (vector<Card::Value>::const_iterator
	 v = this->game().rule().valid_card_values().begin();
	 v != this->game().rule().valid_card_values().end();
	 ++v)
      if (!Card(tcolor, *v).istrump(this->game()))
	n += this->played(Card(tcolor, *v));
    return n;
  } // if !(tcolor == Card::TRUMP)
} // unsigned CardsInformation::played(Card::TColor tcolor) const

/**
 ** the game starts
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.3
 **/
void
CardsInformation::game_start()
{
  // reset (needed in poverty: swines owner can change)
  this->reset();

  if (this->player_)
    this->set_hand(this->player(), this->player().hand());

  // Take care of swines when the game is started,
  // else I get problems with shifted swines.
  if (this->game().swines_announced())
    this->swines_announced(*this->game().swines_owner());
  if (this->game().hyperswines_announced())
    this->hyperswines_announced(*this->game().hyperswines_owner());

  switch (this->game().type()) {
  case GAMETYPE::MARRIAGE:
    this->of_player_[this->game().soloplayer().no()].add_must_have(Card::CLUB_QUEEN,
								   2);
    break;
  case GAMETYPE::POVERTY:
    if (this->game().poverty_cardno() == 0) {
      this->of_player_[this->game().soloplayer().no()].add_does_not_have(Card::TRUMP);
    }
    break;
  default:
    break;
  } // switch (this->game().type())

  return ;
} // void CardsInformation::game_start()

/**
 ** the trick is opened
 **
 ** @param      trick	opened trick
 **
 if (this->tricks_.empty())
 return false;
 ** @return     -
 **
 ** @author     Diether Knof
 **
 ** @version    0.7.1
 **/
void
CardsInformation::trick_open(Trick const& trick)
{ 
  // In a virtual game DO NOT RECALC!
  // The problem is, that additional information get lost
  // (t.i. the hand of the player that has started the virtual game),
  // so there is a discrepance between the hand of the player and the cards
  // he can have.
  if ( (this->game().trick_current_no()
	> this->player().value(Aiconfig::REMEMBERTRICKS))
      && !this->game().isvirtual() )
    this->recalc();

  return ;
} // void CardsInformation::trick_open(Trick const& trick)

/**
 ** 'card' has been played - update the information
 **
 ** @param	card	played card
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.3
 **/
void
CardsInformation::card_played(HandCard const& card)
{
  this->card_played(card, card.game().trick_current());
  return ;
} //  void CardsInformation::card_played(HandCard card)

/**
 ** 'card' has been played - update the information
 **
 ** @param	card	played card
 ** @param	trick   current trick
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.3
 **/
void
CardsInformation::card_played(HandCard const& card, Trick const& trick)
{
  this->played_.inc(card);
  DEBUG_ASSERTION(this->played(card)
		  <= this->game().rule()(Rule::NUMBER_OF_SAME_CARDS),
		  "CardsInformation::card_played(" << card << ")\n"
		  "  card is played more often than it is in the game");

  this->of_player_[card.player().no()].card_played(card, trick);

  if (this->in_recalcing())
    return ;

  if (card.tcolor() != this->game().trick_current().startcard().tcolor()) {
    this->of_player_[card.player().no()].add_does_not_have(this->game().trick_current().startcard().tcolor());
  }

  // check whether the player has played as many trump as he has got back
  if ((this->game().type() == GAMETYPE::POVERTY)
      && (card.player().no() == this->game().soloplayer().no())
      && !this->of_player(this->game().soloplayer().no()
			 ).does_not_have(Card::TRUMP)
      && card.istrump()
      && (this->of_player(this->game().soloplayer().no()).played(Card::TRUMP)
	  == this->game().poverty_cardno())) {
    this->of_player_[this->game().soloplayer().no()].add_does_not_have(Card::TRUMP);
  }

  this->update_information(card);

#ifdef SELF_CHECK
  this->self_check();
#endif
  if (!this->game().isvirtual())
    DEBUG_ASSERTION(this->self_check(),
		    "CardsInformation::card_played(" << card << ")\n"
		    << "  self_check failed");

  return ;
} // void CardsInformation::card_played(HandCard card, Trick trick)

/**
 ** an announcement has been made - update the information
 **
 ** @param	announcement	made announcement
 ** @param	player		player who has made the announcement
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::announcement_made(Announcement const& announcement,
				    Player const& player)
{
  // in a virtual game the distribution of the remaining club queens does not
  // depend on the team, so we can get no more information
  if (this->game().isvirtual())
    return ;

  if (this->game().type() == GAMETYPE::NORMAL) {
    if (this->game().teaminfo(player) == TEAM::RE) {
      if ( (this->of_player_[player.no()].played(Card::CLUB_QUEEN) == 0)
	  && (this->forgotten_tricks_no() == 0) )
	this->of_player_[player.no()].add_must_have(Card::CLUB_QUEEN);
    } else if (this->game().teaminfo(player) == TEAM::CONTRA) {
      this->of_player_[player.no()].add_cannot_have(Card::CLUB_QUEEN);
    } else {
      DEBUG_ASSERTION(false,
		      "CardsInformation::announcement_made("
		      << announcement << ", " << player.no() << "):\n"
		      "  team of the player not known in the game: "
		      << this->game().teaminfo(player));
    }

    this->update_information(Card::CLUB_QUEEN);
  } // if (this->game().type() == GAMETYPE::NORMAL)

#ifdef SELF_CHECK
  this->self_check();
#endif

  return ;
} // void CardsInformation::announcement_made(Announcement announcement, Player player)

/**
 ** a player has announced swines - update the information
 **
 ** @param	player	player who has swines
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::swines_announced(Player const& player)
{
  Card const swine(this->game().trumpcolor(), Card::ACE);

  if (::game_status == GAMESTATUS::GAME_POVERTY_SHIFT)
    // this is needed in the case that the swines are shifted
    this->recalc();

  if (this->game().rule()(Rule::SWINE_ONLY_SECOND))
    this->of_player_[player.no()].add_must_have(swine, 1);
  else
    this->of_player_[player.no()].add_must_have(swine, 2);

  for (vector<OfPlayer>::iterator p = this->of_player_.begin();
       p != this->of_player_.end();
       ++p)
    if (p->playerno() != player.no())
      this->of_player_[p->playerno()].add_cannot_have(swine);

#ifdef SELF_CHECK
  this->self_check();
#endif

  return ;
} // void CardsInformation::swines_announced(Player player)

/**
 ** a player has announced hyperswines - update the information
 **
 ** @param	player	player who has hyperswines
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::hyperswines_announced(Player const& player)
{
  // Wait till the game is started, else I get problems with shifted swines.
  if (::game_status <= GAMESTATUS::GAME_RESERVATION)
    return ;

  Card const hyperswine(this->game().trumpcolor(),
			(this->game().rule()(Rule::WITH_NINES)
			 ? Card::NINE
			 : Card::KING));
  this->of_player_[player.no()].add_must_have(hyperswine, 2);
  for (vector<OfPlayer>::iterator p = this->of_player_.begin();
       p != this->of_player_.end();
       ++p)
    if (p->playerno() != player.no())
      this->of_player_[p->playerno()].add_cannot_have(hyperswine);

#ifdef SELF_CHECK
  this->self_check();
#endif

  return ;
} // void CardsInformation::hyperswines_announced(Player player)

/**
 ** player 'genscher' has a genscher
 **
 ** @param	genscher	player with the genscher
 ** @param	partner		selected partner
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
void
CardsInformation::genscher(Player const& genscher, Player const& partner)
{
  Card const genscher_card(this->game().trumpcolor(), Card::KING);
  this->of_player_[genscher.no()].add_must_have(genscher_card, 2);
  for (vector<OfPlayer>::iterator p = this->of_player_.begin();
       p != this->of_player_.end();
       ++p)
    if (p->playerno() != genscher.no())
      this->of_player_[p->playerno()].add_cannot_have(genscher_card);

#ifdef SELF_CHECK
  this->self_check();
#endif

  return ;
} // void CardsInformation::genscher(Player genscher, Player partner)

/**
 ** 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::self_check() const
{
  if (this->in_recalcing())
    return true;
#ifdef WORKAROUND
  // We get problems with 'forgotten cards no'
  // because the last trick is closed but no further cards are forgotten.
  if (this->game().finished())
    return true;
#endif

  { // the number of played cards plus the number of forgotten cards
    // equals the number of played cards in the game 
    if (this->played_.cards_no() + this->forgotten_cards_no()
	!= this->game().played_cards_no()) {
      DEBUG_ASSERTION(false,
		      "CardsInformation::self_check():\n"
		      "  played cards no + forgotten cards no"
		      << " = " << this->played_.cards_no()
		      << " + " << this->forgotten_cards_no()
		      << " != " << this->game().played_cards_no()
		      << " = game.played_cards_no()\n"
		      << "  trick = " << this->game().real_trick_current_no());
      return false;
    }
  } // the number of a card is played does not exceeds
  // the number of it is played in the game
  for (CardCounter::const_iterator p = this->played_.begin();
       p != this->played_.end();
       ++p) {
    { // every card is at max played as many times as it is in the game
      if (p->second > this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)) {
	DEBUG_ASSERTION(false,
			"CardsInformation::self_check():\n"
			"  card '" << p->first << "' has been played"
			<< p->second << "times.\n"
		       );
	return false;
      }
    } // every card is at max played as many times as it is in the game
    { // the number of played in total is the sum of played over the players
      unsigned n = 0;
      for (vector<OfPlayer>::const_iterator f = this->of_player_.begin();
	   f != this->of_player_.end();
	   ++f)
	n += f->played(p->first);

      if (p->second != n) {
	DEBUG_ASSERTION(false,
			"CardsInformation::self_check():\n"
			"  card '" << p->first << "' has been played"
			<< p->second << "times, but the players say "
			<< n << " times.\n"
		       );
	return false;
      }
    } // the number of played in total is the sum of played over the players

  } // for (p \in this->played_)

  { // every card is either played or a player can have it or it is forgotten
    vector<Card> const& cards = this->game().rule().valid_cards();

    for (vector<Card>::const_iterator c = cards.begin();
	 c != cards.end();
	 ++c) {
      unsigned n = this->played(*c);
      for (vector<OfPlayer>::const_iterator f = this->of_player_.begin();
	   f != this->of_player_.end();
	   ++f)
	n += f->can_have(*c);

      n += this->forgotten_cards_no();

#ifdef DEBUG_ASSERT
      if (n < this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)) {
	if (   (::game_status & GAMESTATUS::GAME)
	    && (this->game().trick_current_no() != UINT_MAX)) {
	  cerr << "current trick\n";
	  cerr << this->game().trick_current() << endl;
	  cerr << *this << endl;
	}
      }
#endif
      DEBUG_ASSERTION((n >= this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)),
		      "CardsInformation::self_check():\n"
		      "  card '" << *c << "' is only distributed "
		      << n << " times, must be "
		      << this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)
		      << " times.\n"
		      << "  played: " << this->played(*c)
		      << ", sum can_have = " << (n - this->played(*c)));
    } // for (c \in cards)
  } // every card is either played or a player can have it

  // ToDo: check there are at most as many cards missing as are forgotten

  { // the information of the single players is valid
    for (vector<OfPlayer>::const_iterator f = this->of_player_.begin();
	 f != this->of_player_.end();
	 ++f)
      if (!f->self_check())
	return false;
  } // the information of the single players is valid

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

/**
 ** -> result
 **
 ** @param	cards_information_a	first object
 ** @param	cards_information_b	second object
 **
 ** @return	whether the two objects are equal (the player may differ)
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
bool
operator==(CardsInformation const& cards_information_a,
	   CardsInformation const& cards_information_b)
{
  return ( (cards_information_a.player().no()
	    == cards_information_a.player().no())
	  && (cards_information_a.of_player_
	      == cards_information_b.of_player_) );
} // bool operator==(CardsInformation cards_information_a, CardsInformation cards_information_b)

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

#ifdef SELF_CHECK
#undef SELF_CHECK
#endif
