/**********************************************************************
 *
 *   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 "team_information.h"
#include "team_information.heuristics.h"

#include "constants.h"

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

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

#define DEBUG_PLAYER 0

// For configuration values search for '*Value*'

/*
 * TeamInformation handles the information a player has which team the other
 * players are / can be.
 * If the team of a player is not known, the actions of the player are rated
 * (pro/contra 're'/'contra'). The rating of the unknown players are compared
 * and if they do not lie close together the teams are set to the 'maybe' team.
 *
 * Certain information:
 * the certain information is (mostly) taken from 'game', but I will lay down
 * here the ideas.
 * The only interesting case is a normal game, in every other case the team
 * information is known (marriage is a special case till the determination).
 * - announcement 're'/'contra'
 *   the team of the player is 're'/'contra'
 * - playing of a club queen
 *   the team of the player is 're'
 * - not serving trump
 *   the team of the player is 'contra' if it is not already 're'
 * - information from 'CardsInformation'
 *   if 'CardsInformation' can say something about having / not having the club
 *   queen we add this information.
 * - counting 're's and 'contra's
 *   if there are two re players, the remaining players must be contra
 *   if there are three contra players, the remaining player must be re
 *     (remember the silent marriage!)
 *   if there are two contra players, the player itself is re and plays no
 *     silent marriage, the remaining player is re
 *
 * Uncertain information:
 * According to some heuristics (see 'TeamInformationHeuristic') the actions
 * of the players are rated (here we assume the other players do play
 * intelligently). Positive means pro re, negative means pro contra.
 * When the rating of the unknown players differ enough the player(s) with the
 * higher value is set to 'maybe re', the other(s) to 'maybe contra'
 * (see 'TeamInformation::update_team()' for more explicit information).
 * I do also check for a silent marriage:
 *   If there are already two contra and one re player, the remaining player is
 *   generally set to 'maybe re', but if he has a strong negative value he is
 *   also set to 'maybe contra'.
 */

/**
 ** constructor
 **
 ** @param	player			corresponding player
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
TeamInformation::TeamInformation(Ai const& player) :
  player_(&player),
  team_values_(player.game().playerno(), 0),
  teams_(player.game().playerno(), TEAM::UNKNOWN),
  known_teams_no_(0)
{
  this->update_teams();
}

/**
 ** copy constructor
 **
 ** @param	team_information	object to be copied
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
TeamInformation::TeamInformation(TeamInformation const& team_information) :
  player_(team_information.player_),
  team_values_(team_information.team_values_),
  teams_(team_information.teams_),
  known_teams_no_(team_information.known_teams_no_)
{ }

/**
 ** copy operator
 **
 ** @param	team_information	object to be copied
 **
 ** @return	object
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
TeamInformation&
TeamInformation::operator=(TeamInformation const& team_information)
{
  this->player_ = team_information.player_;
  this->team_values_ = team_information.team_values_;
  this->teams_ = team_information.teams_;
  this->known_teams_no_ = team_information.known_teams_no_;

  return *this;
} // TeamInformation& TeamInformation::operator=(TeamInformation team_information)

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

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

/**
 ** writes 'team_information' in 'ostr'
 **
 ** @param	ostr		output stream
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
void
TeamInformation::write(ostream& ostr) const
{
  ostr << "team information:\n"
    << "{\n";

  for (unsigned p = 0; p < this->game().playerno(); ++p)
    ostr << p << ": "
      << setw(5)
      << this->team_value(p) << ' '
      << this->team(p)
      << '\n';

  ostr << "}\n";

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

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

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

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

/**
 ** -> result
 **
 ** @param	player	player
 **
 ** @return	the team value of the player (can be maybe)
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
int const&
TeamInformation::team_value(Player const& player) const
{
  return this->team_values_[player.no()];
}

/**
 ** -> result
 **
 ** @param	playerno	player number
 **
 ** @return	the team value of the player (can be maybe)
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
int const&
TeamInformation::team_value(unsigned const playerno) const
{
  return this->team_values_[playerno];
}

/**
 ** -> result
 **
 ** @param	player	player
 **
 ** @return	the team value of the player (can be maybe)
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
int&
TeamInformation::team_value(Player const& player)
{
  return this->team_values_[player.no()];
}

/**
 ** -> result
 **
 ** @param	playerno	player number
 **
 ** @return	the team value of the player (can be maybe)
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
int&
TeamInformation::team_value(unsigned const playerno)
{
  return this->team_values_[playerno];
}

/**
 ** -> result
 **
 ** @param	player	player
 **
 ** @return	the team of the player (can be maybe)
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/

Team const&
TeamInformation::team(Player const& player) const
{
  return this->teams_[player.no()];
}

/**
 ** -> result
 **
 ** @param	playerno	player number
 **
 ** @return	the team of the player (can be maybe)
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
Team const&
TeamInformation::team(unsigned const playerno) const
{
  return this->teams_[playerno];
}

/**
 ** -> result
 **
 ** @param	player	player
 **
 ** @return	the team of the player (can be maybe)
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/

Team&
TeamInformation::team(Player const& player)
{
  return this->teams_[player.no()];
}

/**
 ** -> result
 **
 ** @param	playerno	player number
 **
 ** @return	the team of the player (can be maybe)
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
Team&
TeamInformation::team(unsigned const playerno)
{
  return this->teams_[playerno];
}

/**
 ** -> result
 **
 ** @param	player	player
 **
 ** @return	the guessed team of the player (cannot be maybe)
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.2
 **/

Team
TeamInformation::guessed_team(Player const& player) const
{
  return this->guessed_team(player.no());
}

/**
 ** -> result
 **
 ** @param	playerno	player number
 **
 ** @return	the guessed team of the player (cannot be maybe)
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.2
 **/
Team
TeamInformation::guessed_team(unsigned const playerno) const
{
    if (this->team(playerno) == TEAM::UNKNOWN)
      return ::opposite(this->player().team());
    else
      return ::maybe_to_team(this->team(playerno));
}

/**
 ** -> result
 **
 ** @param	-
 **
 ** @return	the teams of the players guessed to re/contra
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.2
 **/
vector<Team>
TeamInformation::guessed_teams() const
{
  vector<Team> teams;
  for (unsigned p = 0; p < this->game().playerno(); ++p)
    teams.push_back(this->guessed_team(p));

  return teams;
} // vector<Team> TeamInformation::guessed_teams() const

/**
 ** sets the teams according to the parameter
 **
 ** @param	teams    teams
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.2
 **/
void
TeamInformation::set_teams(vector<Team> const& teams)
{
  this->teams_ = teams;

  // recalculate the number of known teams
  this->known_teams_no_ = 0;
  for (vector<Team>::const_iterator t = this->teams().begin();
       t != this->teams().end();
       ++t)
    if (::is_real(*t))
      this->known_teams_no_ += 1;

  return ;
} // void set_teams(vector<Team> teams)

/**
 ** the game starts
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
void
TeamInformation::game_start()
{
  this->reset();

  // set the own team
  if (this->game().type() == GAMETYPE::NORMAL)
    this->team(this->player())
      = ( (this->player().hand().numberofclubqueens() > 0)
	 ? TEAM::RE
	 : TEAM::CONTRA);

  this->update_teams();

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

/**
 ** the trick is opened
 **
 ** @param      trick	opened trick
 **
 ** @return     -
 **
 ** @author     Diether Knof
 **
 ** @version    0.7.1
 **
 ** @todo	perhaps check whether any other player has had more information
 **		than we thought at his turn
 **/
void
TeamInformation::trick_full()
{ 
  return ;
} // void TeamInformation::trick_full()

/**
 ** 'card' has been played - update the information
 **
 ** @param	card	played card
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **
 ** @todo	all: analyse the card and check for help/bother another player
 **/
void
TeamInformation::card_played(HandCard const& card)
{
  using namespace TeamInformationHeuristic;

  if (this->game().type() != GAMETYPE::NORMAL)
    return ;

  // take the cards information to check
  // whether the player can still have a club queen
  if (   (this->game().type() == GAMETYPE::NORMAL)
      && !::is_real(this->team(card.player()))
      && (card != Card::CLUB_QUEEN)
      && (this->player().cards_information().of_player(card.player()).can_have(Card::CLUB_QUEEN) == 0) ) {
    this->team_values_[card.player().no()] = TEAM::CONTRA;
  }

  int value = 0;
#ifdef RELEASE
#define CALL_HEURISTIC(function) \
  if (value == 0) \
  value = function(card.player(), card, \
		   this->game().trick_current(), this->player())
#else
#define CALL_HEURISTIC(function) \
  if (value == 0) { \
    value = function(card.player(), card, \
		     this->game().trick_current(), this->player()); \
    if (   value \
	&& !this->game().isvirtual() \
	&& (this->player().no() == DEBUG_PLAYER) ) \
    COUT << this->player().no() << ": " \
    << "team_value(" << card.player().no() << ") = " \
    << setw(3) << (this->team_values_[card.player().no()] + value) \
    << " (" << setw(3) << setiosflags(ios::showpos) << value \
    << resetiosflags(ios::showpos) << ") \t" \
    << #function \
    << endl; \
  }
#endif

  CALL_HEURISTIC(startplayer_color);
  CALL_HEURISTIC(penultimate_pfund);
  CALL_HEURISTIC(last_player);

  CALL_HEURISTIC(does_not_have_color);
  CALL_HEURISTIC(pfund_in_color_trick);

#undef CALL_HEURISTIC

  this->team_values_[card.player().no()] += value;

  this->update_teams();

  return ;
} // void TeamInformation::card_played(HandCard card)

/**
 ** 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.7.1
 **/
void
TeamInformation::announcement_made(Announcement const& announcement,
				   Player const& player)
{
  this->update_teams();

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

/**
 ** the marriage is determined
 ** if the bride is the bridegroom, the bridegroom must play a solo
 **
 ** @param	bridegroom	the player with the marriage
 ** @param	bride		the bride
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
void
TeamInformation::marriage(Player const& bridegroom, Player const& bride)
{
  for (unsigned p = 0; p < this->game().playerno(); ++p)
    this->team(p) = (   (p == bridegroom.no())
		     || (p == bride.no())
		     ? TEAM::RE
		     : TEAM::CONTRA );

  this->known_teams_no_ = this->game().playerno();

  return ;
} // void TeamInformation::marriage(Player bridegroom, Player bride)

/**
 ** player 'genscher' has a genscher
 **
 ** @param	genscher	player with the genscher
 ** @param	partner		selected partner
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
void
TeamInformation::genscher(Player const& genscher, Player const& partner)
{
  for (unsigned p = 0; p < this->game().playerno(); ++p)
    this->team(p) = (   (p == genscher.no())
		     || (p == partner.no())
		     ? TEAM::RE
		     : TEAM::CONTRA );

  this->known_teams_no_ = this->game().playerno();

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

/**
 ** reset all
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
void
TeamInformation::reset()
{
  this->team_values_ = vector<int>(this->game().playerno(), 0); 
  this->teams_ = vector<Team>(this->game().playerno(), TEAM::UNKNOWN); 
  this->known_teams_no_ = 0;

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

/**
 ** updates the teams information
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **
 ** @todo	nearly all
 **/
void
TeamInformation::update_teams()
{
  using namespace TEAM;

  if (this->known_teams_no() == this->game().playerno())
    // all teams are known
    return ;

  // in an undetermined marriage set the contra players to 'maybe contra'
  if (   (this->game().type() == GAMETYPE::MARRIAGE)
      && (this->game().marriage_selector() != MARRIAGE_SELECTOR::TEAM_SET)) {
    for(vector<Player*>::const_iterator player = this->game().players_begin();
	player != this->game().players_end();
	player++) {
      if (*player == &this->game().soloplayer())
	this->team(**player) = RE;
      else if (*player == &this->player())
	this->team(**player) = MAYBE_CONTRA;
      else
	this->team(**player) = MAYBE_RE;
    }
    return ;
  } // if (undetermined marriage)

  // Update with the teaminfo of 'game'
  // and add information from 'CardsInformation'.
  // Also count the 're's and 'contra's
  unsigned re_no = 0; // number of re
  unsigned contra_no = 0; // number of re
  for(vector<Player*>::const_iterator player = this->game().players_begin();
      player != this->game().players_end();
      player++) {

    // information from the game
    this->team(**player) = this->game().teaminfo(**player);

    // the player knows his own team
    if (   !::is_real(this->team(**player))
	&& (*player == &this->player()) )
      this->team(**player) = this->player().team();

    // use also the cards information
    if (!::is_real(this->team(**player))) {
      if (   this->player().cards_information().of_player(**player).must_have(Card::CLUB_QUEEN)
	  || this->player().cards_information().of_player(**player).played(Card::CLUB_QUEEN))
	this->team(**player) = RE;
      else if (   this->player().cards_information().of_player(**player).cannot_have(Card::CLUB_QUEEN)
	       && !this->player().cards_information().of_player(**player).played(Card::CLUB_QUEEN))
	this->team(**player) = CONTRA;
    } // if (!::is_real(this->team(**player)))

    switch (this->team(**player)) {
    case RE:
      re_no += 1;
      break;
    case CONTRA:
      contra_no += 1;
      break;
    default:
      break;
    } // switch (this->team(**player)
  }

  // check whether all teams are known
  this->known_teams_no_ = re_no + contra_no;

  if (this->known_teams_no() == this->game().playerno()) {
    // OK, all teams known
    return ;
  }

  { // all not re players must be contra
    if (re_no == this->game().rule()(Rule::NUMBER_OF_PLAYERS_PER_TEAM)) {
      for (unsigned p = 0; p < this->game().playerno(); ++p)
	if (this->team(p) != RE)
	  this->team(p) = CONTRA;
      this->known_teams_no_ = this->game().playerno();
      return ;
    } // if (re_no == this->game().rule()(Rule::NUMBER_OF_PLAYERS_PER_TEAM))
  } // all not re players must be contra

  { // there must be at least one re player
    if (contra_no == this->game().playerno() - 1) {
      for (unsigned p = 0; p < this->game().playerno(); ++p)
	if (this->team(p) != CONTRA)
	  this->team(p) = RE;
      this->known_teams_no_ = this->game().playerno();
      return ;
    } // if (contra_no == this->game().playerno() - 1)
  } // there must be at least one re player

  { // there must be another re player but me
    if (   this->player_
	&& (contra_no == this->game().playerno() - 2)
	&& (this->team(this->player()) == RE)
	&& (this->player().hand().numberofall(Card::CLUB_QUEEN)
	    < this->game().rule()(Rule::NUMBER_OF_SAME_CARDS))
       ) {
      for (unsigned p = 0; p < this->game().playerno(); ++p)
	if (this->team(p) != CONTRA)
	  this->team(p) = RE;
      this->known_teams_no_ = this->game().playerno();
      return ;
    } // if (last unknown player must be re)
  } // there must be another re player but me

  // set the team of the players to maybe according to the team value
  switch (this->known_teams_no()) {
  case 0:
  case 1: { // (almost) no information
    // Idea:
    // |  re  | unknown | contra |
    // with enough distance between the two extreme values

    int min_value = INT_MAX;
    int max_value = INT_MIN;
    for (unsigned p = 0; p < this->game().playerno(); ++p) {
      if (!::is_real(this->team(p))) {
	if (this->team_value(p) < min_value)
	  min_value = this->team_value(p);
	if (this->team_value(p) > max_value)
	  max_value = this->team_value(p);
      }
    } // for (p)

    if (max_value - min_value < 10) // *Value*
      break;

    // count how many possible contra and re there are
    for (unsigned p = 0; p < this->game().playerno(); ++p) {
      if (!::is_real(this->team(p))) {
	if (this->team_value(p) >= max_value - (max_value - min_value) / 3)
	  re_no += 1;
	else if (this->team_value(p) <= min_value + (max_value - min_value) / 3)
	  contra_no += 1;
      }
    } // for (p)

    if (   (contra_no <= this->game().rule()(Rule::NUMBER_OF_PLAYERS_PER_TEAM))
	&& (re_no <= this->game().rule()(Rule::NUMBER_OF_PLAYERS_PER_TEAM)) ) {
      for (unsigned p = 0; p < this->game().playerno(); ++p) {
	if (!::is_real(this->team(p))) {
	  if (this->team_value(p)
	      >= max_value - (max_value - min_value) / 3)
	    this->team(p) = TEAM::MAYBE_RE;
	  else if (this->team_value(p)
		   <= min_value + (max_value - min_value) / 3)
	    this->team(p) = TEAM::MAYBE_CONTRA;
	}
      } // for (p)
    } // if (contra_no, re_no <= 2)

    break;
  } // case 0, 1

  case 2: {
    switch (contra_no) {
    case 0: // contra_no
      DEBUG_ASSERTION(false,
		      "TeamInformation::update_teams()\n"
		      "  contra no = 0");
      break;
    case 1: { // contra_no
      // one re and one contra player are known
      int min_value = INT_MAX;
      int max_value = INT_MIN;
      for (unsigned p = 0; p < this->game().playerno(); ++p) {
	if (!::is_real(this->team(p))) {
	  if (this->team_value(p) < min_value)
	    min_value = this->team_value(p);
	  if (this->team_value(p) > max_value)
	    max_value = this->team_value(p);
	}
      } // for (p)

      // enough difference to set the teams
      if (max_value - min_value < (this->player().value(Aiconfig::TRUSTING)
				   ? 10
				   : 20) ) // *Value*
	break;

      for (unsigned p = 0; p < this->game().playerno(); ++p) {
	if (!::is_real(this->team(p))) {
	  if (this->team_value(p) == max_value)
	    this->team(p) = TEAM::MAYBE_RE;
	  else // == min_value
	    this->team(p) = TEAM::MAYBE_CONTRA;
	}
      }
      break;
    } // one re and one contra player are known
    case 2: // contra_no
      // both other players are re but in the case that someone plays a silent
      // marriage
      for (unsigned p = 0; p < this->game().playerno(); ++p)
	if (!::is_real(this->team(p))) {
	  // I think here we can be very strict
	  if (this->team_value(p) >= -5) // *Value*
	    this->team(p) = TEAM::MAYBE_RE;
	  else if (this->team_value(p) <= -25) // *Value*
	    this->team(p) = TEAM::MAYBE_CONTRA;
	} // switch(contra_no)

      break;
    default:
      break;
    } // switch (contra_no)

    break;
  } // case 2

  case 3: {
    // this case can only be when there are two contra and one re,
    // so the question is whether the re player plays a silent marriage.

    for (unsigned p = 0; p < this->game().playerno(); ++p)
      if (!::is_real(this->team(p))) {
	// I think here we can be very strict
	if (this->team_value(p) >= -10) // *Value*
	  this->team(p) = TEAM::MAYBE_RE;
	else
	  this->team(p) = TEAM::MAYBE_CONTRA;
	break;
      }
    break;
  } // case 3

  case 4:
    DEBUG_ASSERTION(false,
		    "TeamInformation::update_teams()\n"
		    "  should have left the method earlier because all teams "
		    "are known:\n"
		    "  known teams no = " << this->known_teams_no()
		    << " == " << this->game().playerno()
		    << " = player number in game");
    break;

  default:
    DEBUG_ASSERTION(false,
		    "TeamInformation::update_teams()\n"
		    "  known teams number is '" << this->known_teams_no()
		    << "' > 4 that is the maximum (players in game = "
		    << this->game().playerno());
    break;
  } // switch (this->known_teams_no())

  return ;
} // void TeamInformation::update_teams()

/**
 ** -> result
 **
 ** @param	team_information_a	first object
 ** @param	team_information_b	second object
 **
 ** @return	whether the two objects are equal (the game may differ)
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.1
 **/
bool
operator==(TeamInformation const& team_information_a,
	   TeamInformation const& team_information_b)
{
  return (   (team_information_a.team_values_
	      == team_information_b.team_values_)
	  && (team_information_a.teams_
	      == team_information_b.teams_) );
} // bool operator==(TeamInformation team_information_a, TeamInformation team_information_b)

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