/**********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *    
 *   Copyright (C) 2001-2005  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 "trick.h"

#include "hand.h"
#include "../game/game.h"
#include "../player/player.h"
#include "../party/party.h"
#include "../party/rule.h"
#include "../ui/ui.h"
#include "../utils/string.h"


/**
 ** constructor
 **
 ** @param	startplayer	the player, who has to play the first card
 **
 ** @return	-
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
Trick::Trick(Player const& startplayer) :
cards_(),
  game_(&(startplayer.game())),
  no_(startplayer.game().trick_current_no()),
  startplayer_(startplayer.no()),
  winnerplayer_(UINT_MAX),
intrickpile_(false)
{
  return ;
} // Trick::Trick(Player const& startplayer)

/**
 ** constructor
 **
 ** @param	istr	the stream to read the trick from
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.5.4
 **
 **/
Trick::Trick(istream& istr) :
cards_(),
  game_(NULL),
  no_(UINT_MAX),
  startplayer_(UINT_MAX),
  winnerplayer_(UINT_MAX),
intrickpile_(false)
{
  string line;
  getline(istr, line);
  if (DK::Utils::String::word_first(line) != "Startplayer:")
    return ;

  DK::Utils::String::word_first_remove(line);
  this->startplayer_ = static_cast<unsigned>(atoi(line.c_str()));

  while (istr.good()) {
    getline(istr, line);
    if (line.empty())
      break;
    if (DK::Utils::String::word_first(line) == "Winner:")
      break;

    // the line is like: 1: club ace
    DK::Utils::String::word_first_remove(line);
    DK::Utils::String::remove_blanks(line);
    this->cards_.push_back(HandCard(Card(line)));
  } // while (istr.good())
  if (line.empty()
      || !istr.good())
    return ;

  if (DK::Utils::String::word_first(line) == "Winner:") {
    DK::Utils::String::word_first_remove(line);
    this->winnerplayer_ = static_cast<unsigned>(atoi(line.c_str()));
  } // if (DK::Utils::String::word_first(line) == "Winner:")

  return ;
} // Trick::Trick(istream& istr)

/**
 ** constructor
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
Trick::Trick() :
cards_(),
  game_(NULL),
  no_(UINT_MAX),
  startplayer_(UINT_MAX),
  winnerplayer_(UINT_MAX),
intrickpile_(false)
{
  return ;
} // Trick::Trick()

/**
 ** destructor
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
Trick::~Trick()
{
  return ;
} // Trick::~Trick()

/**
 ** copy constructor
 **
 ** @param	trick	the trick to copy
 **
 ** @return	-
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
Trick::Trick(Trick const& trick) :
cards_(trick.cards_),
  game_(trick.game_),
  no_(trick.no_),
  startplayer_(trick.startplayer_),
  winnerplayer_(trick.winnerplayer_),
intrickpile_(trick.intrickpile_)
{
  return ;
} // Trick::Trick(Trick const& trick)

/**
 ** copy method
 **
 ** @param	trick	the trick to copy
 **
 ** @return	the trick
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
Trick&
Trick::operator=(Trick const& trick)
{
  this->cards_ = trick.cards_;
  this->game_ = trick.game_;
  this->no_ = trick.no_;
  this->startplayer_ = trick.startplayer_;
  this->winnerplayer_ = trick.winnerplayer_;
  this->intrickpile_ = trick.intrickpile_;

  return *this;
} // Trick& Trick::operator=(Trick const& trick)

/**
 ** self check
 ** when an error is found, an ASSERTION is created
 **
 ** @param	-
 **
 ** @return	wether the self-check was successful (no error)
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
bool
Trick::self_check() const
{
  DEBUG_ASSERTION(this->game_,
		  "Trick::self_check()\n"
		  "  no game");

  DEBUG_ASSERTION((this->no_ <= this->game_->trick_current_no()),
		  "Trick::self_check()\n"
		  "  number not valid: " << this->no_);

  DEBUG_ASSERTION((this == &this->game_->trick(this->no_)),
		  "Trick::self_check()\n"
		  "  Not the trick the number says to be");

  DEBUG_ASSERTION(this->startplayerno() != UINT_MAX,
		  "Trick::self_check()\n"
		  "  no startplayer");

  DEBUG_ASSERTION((!this->intrickpile_
		   || (this->winnerplayerno() != UINT_MAX)),
		  "Trick::self_check()\n"
		  "  in trickpile but no winnerplayer");

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

/**
 **
 ** output of the trick
 **
 ** @param	ostr	output stream
 **
 ** @return	output stream
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
ostream&
Trick::out(ostream& ostr) const
{
  ostr << "Startplayer: " << this->startplayerno() << "\n";
  for(unsigned i = 0; i < this->actcardno(); i++) {
    ostr
      << (this->game_
	  ? this->player_of_card(i).no()
	  : (this->startplayerno() + i))
      << ": "
      << this->card(i) << "\n";
  }

  if (!this->game_
      || this->isfull())
    ostr << "Winner: " << this->winnerplayerno() << "\n";

  return ostr;
} // ostream& Trick::out(ostream& ostr) const

/**
 **
 ** output of the trick
 **
 ** @param	ostr	output stream
 ** @param	trick	Trick to be put in the output stream
 **
 ** @return	output stream
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
ostream&
operator<<(ostream& ostr, Trick const& trick)
{
  trick.out(ostr);

  return ostr;
} // ostream& operator<<(ostream& ostr, Trick const& trick)

/**
 **
 ** adds the card 'c' to the trick
 **
 ** @param	card	card, which should be added to this trick
 **
 ** @return	trick with c as last card
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
Trick&
Trick::operator+=(HandCard const& card)
{
  DEBUG_ASSERTION(card,
		  "Trick::operator+=(HandCard):\n"
		  "  shall add empty card to the trick");

  DEBUG_ASSERTION(!this->isfull(),
		  "Trick::operator+=(HandCard):\n"
		  "  trick is already full");

  DEBUG_ASSERTION((card.player() == this->actplayer()),
		  "Trick::operator+=(HandCard):\n"
		  "  Card is from the wrong player "
		  << card.player().no()
		  << " (should be " << this->actplayer().no() << ")\n"
		  "  Pointer: " << &card.player() << " != "
		  << &this->actplayer());

  // update the winnerplayer
  if (this->isstartcard()) {
    this->winnerplayer_ = this->startplayerno();
  } else {
    if (this->winnercard().less(card)) {
      this->winnerplayer_ = this->actplayer().no();
    }
  } // if !(this->isstartcard())

  // add the card to the trick
  this->cards_.push_back(card);

  return *this;
} // Trick& Trick::operator+=(Card const& c)

/**
 **
 ** -> result
 **
 ** @param	c	number of card for the result
 **
 ** @return	card c of actual trick
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
HandCard const&
Trick::card(unsigned const c) const
{
  DEBUG_ASSERTION((c < this->actcardno()),
		  "Trick::card(c):\n"
		  "  'c' is to great (" << c << ">=" << this->actcardno()
		  << ")");

  return this->cards_[c];
} // HandCard const& Trick::card(unsigned const c) const


/**
 **
 ** -> result
 **
 ** @param	player	player whose card is returned
 **
 ** @return	card of 'player' of actual trick
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
HandCard const&
Trick::card_of_player(Player const& player) const
{
  return ((player.no() >= this->startplayerno())
		   ? this->card(player.no()
				- this->startplayerno())
		   : this->card(player.no()
				+ this->game().playerno()
				- this->startplayerno())
		  );
} // HandCard const& Trick::card_of_player(Player const& player) const

/**
 **
 ** -> result
 **
 ** @param	c	number of card for the result
 **
 ** @return	the player, who has played the 'c'th card
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
Player const&
Trick::player_of_card(unsigned const c) const
{
  return ((this->startplayerno() + c
	   < this->game().playerno())
	  ? this->game().player(c + this->startplayerno())
	  : this->game().player(c + this->startplayerno()
				- this->game().playerno())
	 );
} // Player const& Trick::player_of_card(unsigned const c) const

/**
 **
 ** -> result
 **
 ** @param	player	player, whose cardno is returnes
 **
 ** @return	the number of the card, the player has played
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
unsigned
Trick::cardno_of_player(Player const& player) const
{
  return ((this->startplayerno()
	   <= player.no())
	  ? (player.no() - this->startplayerno())
	  : (player.no()
	     + this->game().playerno()
	     - this->startplayerno())
	 );
} // unsigned Trick::cardno_of_player(Player const& player) const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the number of the startplayer
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 **/
unsigned
Trick::startplayerno() const
{
  return this->startplayer_;
} // unsigned Trick::startplayerno() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the startplayer
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 **/
Player const&
Trick::startplayer() const
{
  DEBUG_ASSERTION(this->startplayerno() != UINT_MAX,
		  "Trick::startplayer():\n"
		  "  startplayerno() == N != UINT_MAXULL");

  return this->game().player(this->startplayerno());
} // Player const& Trick::startplayer() const

/**
 **
 ** set the startplayer
 **
 ** @param	startplayer	new startplayer
 **
 ** @return	the startplayer
 **
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
void
Trick::set_startplayer(Player const& startplayer)
{
  DEBUG_ASSERTION(this->startplayerno() == UINT_MAX,
		  "Trick::startplayer_set(startplayer):\n"
		  "  startplayer already set");

  this->startplayer_ = startplayer.no();

  return ;
} // void Trick::set_startplayer(Player startplayer)

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the startcard
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
HandCard const&
Trick::startcard() const
{
  DEBUG_ASSERTION(!this->isstartcard(),
		  "Trick::startcard():\n"
		  "  trick is empty");

  return this->cards_[0];
} // HandCard Trick::startcard() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	whether the trick is empty
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
bool
Trick::isstartcard() const
{
  return (this->actcardno() == 0);
} // bool Trick::isstartcard() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	whether the trick is empty
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
bool
Trick::isempty() const
{
  return (this->actcardno() == 0);
} // bool Trick::isempty() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	whether the trick is full
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
bool
Trick::isfull() const
{
  return (this->actcardno() == this->game().playerno());
} // bool Trick::isfull() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	whether the trick lacks only one card
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
bool
Trick::islastcard() const
{
  return (this->actcardno() + 1
	  == this->game().playerno());
} // bool Trick::islastcard() const

/**
 **
 ** moves the trick in the trickpile
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 ** @bug	const cast
 **
 **/
void
Trick::move_in_trickpile()
{
  // Bug: const cast
  const_cast<Player&>(this->winnerplayer()).move_in_trickpile(*this);

  this->intrickpile_ = true;

  if (!this->game().isvirtual())
    ::ui->trick_move_in_pile();

  return ;
} // void Trick::move_in_trickpile()

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the number of the acting card
 **		(that is, how many cards there are in the tricks)
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
unsigned
Trick::actcardno() const
{
  return this->cards_.size();
} // unsigned Trick::actcardno() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the acting player
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
Player const&
Trick::actplayer() const
{
  return ((this->startplayerno() + this->actcardno()
	   < this->game().playerno())
	  ? this->game().player(this->startplayerno()
				+ this->actcardno()
			       )
	  : this->game().player(this->startplayerno()
				+ this->actcardno()
				- this->game().playerno()
			       )
	 );
} // Player const& Trick::actplayer() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the points in the trick
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
unsigned
Trick::points() const
{
  unsigned points = 0;

  for (unsigned c = 0; c < this->actcardno(); c++)
    points += this->card(c).points();

  return points;
} // unsigned Trick::points() const

/**
 **
 ** -> result
 **
 ** @param	card	card which is checked
 **
 ** @return	whether it is valid to play 'card' with its hand
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 ** @todo	remove (replaced by 'HandCard::isvalid(Trick)'
 **
 **/
bool
Trick::isvalid(HandCard const& card) const
{
  return card.isvalid(*this);
} // bool Trick::isvalid(HandCard card) const

/**
 **
 ** -> result
 **
 ** @param	card	card which is checked
 ** @param	hand	hand of the card
 **
 ** @return	whether it is valid to play 'card' with the hand 'hand'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 ** @todo	remove (replaced by 'HandCard::isvalid(Trick)'
 **
 **/
  bool
  Trick::isvalid(Card const& card, Hand const& hand) const
{
  return this->isvalid(HandCard(hand, card));
} // bool Trick::isvalid(Card const& card, Hand const& hand) const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the number of the winnerplayer
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 **/
unsigned
Trick::winnerplayerno() const
{
  return this->winnerplayer_;
} // unsigned Trick::winnerplayerno() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the player which wins this trick
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 **/
Player const&
Trick::winnerplayer() const
{
  // for Game::numberoftricks_of_player()
  if (this->winnerplayerno() == UINT_MAX)
    return this->startplayer();

  DEBUG_ASSERTION(this->winnerplayerno() != UINT_MAX,
		  "Trick::winnerplayer():\n"
		  "  winnerplayerno() == UINT_MAX");

  return this->game().player(this->winnerplayerno());
} // Player const& Trick::winnerplayer() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the team which wins the trick
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
Team
Trick::winnerteam() const
{
  return this->winnerplayer().team();
} // Team Trick::winnerteam() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	card that `makes` the trick
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
HandCard
Trick::winnercard() const
{
  if (this->isempty())
    return HandCard();

  return this->card_of_player(this->winnerplayer());
} // HandCard Trick::winnercard() const

/**
 **
 ** returns the number of the winnercard
 **
 ** @param	-
 **
 ** @return	cardno that `makes` the trick
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.4.4
 **
 **/
unsigned
Trick::winnercardno() const
{
  return this->cardno_of_player(this->winnerplayer());
} // unsigned Trick::winnercardno() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	whether this is the last trick in the game
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
bool
Trick::islast() const
{
  return (this->no() == this->game().trickno() - 1);
} // bool Trick::islast() const

/**
 ** comparison of two tricks
 **
 ** @param	trick1	first trick to compare
 ** @param	trick2	second trick to compare
 **
 ** @return	whether the tricks are equal
 **
 ** @author	Diether Knof
 **
 ** @version	0.5.4
 **
 **/
bool
operator==(Trick const& trick1, Trick const& trick2)
{
  if ((trick1.startplayerno() != trick2.startplayerno())
      || (trick1.actcardno() != trick2.actcardno()))
    return false;

  for (unsigned c = 0; c < trick1.actcardno(); c++)
    if (trick1.card(c) != trick2.card(c))
      return false;

  if (trick1.winnerplayerno() != trick2.winnerplayerno())
    return false;

  return true;
} // bool operator==(Trick const& trick1, Trick const& trick2)

/**
 ** comparison of two tricks
 **
 ** @param	trick1	first trick to compare
 ** @param	trick2	second trick to compare
 **
 ** @return	whether the tricks are different
 **
 ** @author	Diether Knof
 **
 ** @version	0.5.4
 **
 **/
bool
operator!=(Trick const& trick1, Trick const& trick2)
{
  return !(trick1 == trick2);
} // bool operator!=(Trick const& trick1, Trick const& trick2)

/**
 **
 ** counts all points witch are turned on in rules and determines
 ** team which has won this point.
 ** takes all inforation from the game the trick belongs to.
 **
 ** @param	-
 **
 ** @return	a vector with names of specialpoints and type of them
 **		for solo games return value is an empty vector
 **
 ** @author	Diether Knof
 **
 ** @version	0.5.1
 **
 **/
Specialpointsvector
Trick::specialpoints() const
{
  vector<Team> teams;

  for (vector<Player*>::const_iterator p = this->game().player_begin();
       p != this->game().player_end();
       p++)
    teams.push_back(this->game().teaminfo(**p));

  return this->specialpoints(this->game().type(), teams, this->islast());
} // Specialpointsvector Trick::specialpoints() const

/**
 **
 ** counts all points witch are turned on in rules and determines
 ** team which has won this point
 **
 ** @param	gametype	the gametype
 ** @param	real_team	the real teams of the players
 ** @param	islasttrick	whether this is the last trick
 **
 ** @return	a vector with names of specialpoints and type of them
 **		for solo games return value is an empty vector
 **
 ** @author	Borg Enders
 **
 ** @version	0.4.4
 **
 **/
Specialpointsvector
Trick::specialpoints(GameType const gametype,
		     vector<Team> const& real_team,
		     bool const islasttrick) const
{
  return this->specialpoints(gametype, real_team, real_team, islasttrick);
} // Specialpointsvector Trick::specialpoints(GameType const gametype, vector<Team> const& real_team, bool const islasttrick) const

/**
 **
 ** counts all points witch are turned on in rules and determines
 **		team which has won this point
 **
 ** @param	gametype	the gametype
 ** @param	real_team	the real teams of the players
 ** @param	known_team	the teams known by the caller
 ** @param	islasttrick	whether this is the last trick
 **
 ** @return	a vector with names of specialpoints and type of them
 **		for solo games return value is an empty vector
 **
 ** @author	Borg Enders
 **
 ** @version	0.4.4
 **
 **/
Specialpointsvector
Trick::specialpoints(GameType const gametype,
		     vector<Team> const& real_team,
		     vector<Team> const& known_team,
		     bool const islasttrick) const
{
  Specialpointsvector sp=Specialpointsvector();

  Team const local_winnerteam = known_team[this->winnerplayerno()];

  // if this game is a solo there are no specialspoints
  if (GAMETYPE::is_solo(gametype))
    return sp;

  bool dolle_caught = false;

  // doppelkopf
  if (this->points() >= 40) {
    Specialpoints s(SPECIALPOINT::DOPPELKOPF, local_winnerteam);
    s.player_get_no = this->winnerplayerno();

    sp.push_back(s);
  }


  bool fl_caught_charlie=false;
  for(unsigned i=0; i<this->actcardno();i++) {
    // the card belongs to the winner team
    if ( ((local_winnerteam == TEAM::RE)
	  || (local_winnerteam == TEAM::CONTRA))
	 && (local_winnerteam == known_team[this->player_of_card(i).no()]) )
      continue;

    // fox caught
    if (this->card(i).isfox()
	&& party.rule()(Rule::EXTRAPOINT_CATCH_FOX)
	&& (this->winnerplayer() != this->player_of_card(i))) {
      Specialpoints s(SPECIALPOINT::CAUGHT_FOX, local_winnerteam);
      s.player_get_no = this->winnerplayerno();
      s.player_of_no = this->player_of_card(i).no();

      sp.push_back(s);
    }

    // Charlie caught
    if (islasttrick && card(i)==Card::CHARLIE && !fl_caught_charlie
	&& party.rule()(Rule::EXTRAPOINT_CATCH_CHARLIE)
	&& (this->winnerplayer() != this->player_of_card(i)))
    {

      fl_caught_charlie=true;

      Specialpoints s(SPECIALPOINT::CAUGHT_CHARLIE, local_winnerteam);
      s.player_get_no = this->winnerplayerno();
      s.player_of_no = this->player_of_card(i).no();
      sp.push_back(s);
      // check for double charlies caught
      if (party.rule()(Rule::EXTRAPOINT_CATCH_DOUBLE_CHARLIE))
	for (unsigned n=i+1; n<actcardno(); n++) {
	  if(card(n)==Card::CHARLIE &&
	     real_team[n]!=s.team) {
	    Specialpoints s(SPECIALPOINT::CAUGHT_CHARLIE,
			    local_winnerteam);
	    s.player_get_no = this->winnerplayerno();
	    s.player_of_no = this->player_of_card(n).no();
	    sp.push_back(s);
	  }
	}
    } // if (charlie caught)

    // dolle caught
    dolle_caught |= this->card(i).isdolle();

  } // for (i < this->actcardno())

  if (party.rule()(Rule::EXTRAPOINT_DOLLE_JABS_DOLLE)
     && dolle_caught
     && (winnercard() == Card::DOLLE)) {
    for (unsigned c = 0; c < this->actcardno(); c++) {
      if ((this->card(c) == Card::DOLLE)
	  && (local_winnerteam != known_team[this->player_of_card(c).no()])
	 ) {
	Specialpoints s(SPECIALPOINT::DOLLE_CAUGHT_DOLLE,
			local_winnerteam);
	s.player_get_no = this->winnerplayerno();
	s.player_of_no = this->player_of_card(c).no();

	sp.push_back(s);
      } // if (dolle caught of opposite team)
    } // for (c)
  } // if (dolle caught)

  if (this->game().rule()(Rule::EXTRAPOINT_HEART_TRICK)
      && (GAMETYPE::is_normal(this->game().type()))
      && (this->winnercard().tcolor() == Card::HEART)) {
    Specialpoints s(SPECIALPOINT::HEART_TRICK,
		    local_winnerteam);
    s.player_get_no = this->winnerplayerno();

    sp.push_back(s);
  } // if (heart trick)

  // check for foxes who are winning the last trick
  if (islasttrick
      && (winnercard().isfox())
      && party.rule()(Rule::EXTRAPOINT_FOX_LAST_TRICK))
  {
    Specialpoints s(SPECIALPOINT::FOX_LAST_TRICK,
		    local_winnerteam);
    s.player_get_no = this->winnerplayerno();
    //s.player_of_no = this->winnerplayerno();
    sp.push_back(s);

    // check for double fox winning
    if (party.rule()(Rule::EXTRAPOINT_DOUBLE_FOX_LAST_TRICK))
      for (unsigned n=winnercardno()+1; n<actcardno(); n++)
      {
	if(card(n).isfox() &&
	   real_team[n]==s.team)
	{
	  Specialpoints s(SPECIALPOINT::FOX_LAST_TRICK,
			  local_winnerteam);
	  s.player_get_no = this->winnerplayerno();
	  //s.player_of_no = this->player_of_card(n).no();
	  sp.push_back(s);
	}
      }
  }


  // check for charlies in lasttrick
  if (islasttrick) {
    if ((winnercard()==Card::CHARLIE)
	&& party.rule()(Rule::EXTRAPOINT_CHARLIE))
    {

      Specialpoints s(SPECIALPOINT::CHARLIE,
		      local_winnerteam);
      s.player_get_no = this->winnerplayerno();
      //s.player_of_no = this->winnerplayerno();
      sp.push_back(s);

      // check for double charlies
      if (party.rule()(Rule::EXTRAPOINT_DOUBLE_CHARLIE))
	for (unsigned n=winnercardno()+1; n<actcardno(); n++)
	{
	  if(card(n)==Card::CHARLIE &&
	     real_team[n]==s.team)
	  {
	    Specialpoints s(SPECIALPOINT::CHARLIE,
			    local_winnerteam);
	    s.player_get_no = this->winnerplayerno();
	    //s.player_of_no = this->player_of_card(n).no();

	    sp.push_back(s);
	  }
	}
    } // if (charlie)
  }

  return sp;
} // Specialpointsvector Trick::specialpoints(GameType const gametype, vector<Team> const& real_team, vector<Team> const& known_team, bool const islasttrick) const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	a vector with the cards that made a specialpoint
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
HandCards
Trick::specialpoints_cards() const
{
  Specialpointsvector const spv = this->specialpoints();

  HandCards cards;

  for (Specialpointsvector::const_iterator sp = spv.begin();
       sp != spv.end();
       ++sp) {
    switch (sp->type) {
    case SPECIALPOINT::NOSPECIALPOINT:
      break;
    case SPECIALPOINT::CAUGHT_FOX:
      for (HandCards::const_iterator c = this->cards_.begin();
	   c != this->cards_.end();
	   ++c)
	if (c->istrumpace()) {
	  cards.push_back(*c);
	  break;
	}
      break;
    case SPECIALPOINT::FOX_LAST_TRICK:
      cards.push_back(this->winnercard());
      break;
    case SPECIALPOINT::CHARLIE:
      cards.push_back(this->winnercard());
      break;
    case SPECIALPOINT::CAUGHT_CHARLIE:
      for (HandCards::const_iterator c = this->cards_.begin();
	   c != this->cards_.end();
	   ++c)
	if (*c == Card::CHARLIE) {
	  cards.push_back(*c);
	  break;
	}
      break;
    case SPECIALPOINT::DOLLE_CAUGHT_DOLLE:
      for (HandCards::const_reverse_iterator c = this->cards_.rbegin();
	   c != this->cards_.rend();
	   ++c)
	if (*c == Card::CHARLIE) {
	  cards.push_back(*c);
	  break;
	}
      break;
    case SPECIALPOINT::HEART_TRICK:
      cards.push_back(this->winnercard());
      break;
    case SPECIALPOINT::DOPPELKOPF:
      cards.push_back(this->winnercard());
      break;
    case SPECIALPOINT::NO120:
    case SPECIALPOINT::NO90:
    case SPECIALPOINT::NO60:
    case SPECIALPOINT::NO30:
    case SPECIALPOINT::NO0:
    case SPECIALPOINT::NO90_WON:
    case SPECIALPOINT::NO60_WON:
    case SPECIALPOINT::NO30_WON:
    case SPECIALPOINT::NO0_WON:
    case SPECIALPOINT::NO120_SAID:
    case SPECIALPOINT::NO90_SAID:
    case SPECIALPOINT::NO60_SAID:
    case SPECIALPOINT::NO30_SAID:
    case SPECIALPOINT::NO0_SAID:
    case SPECIALPOINT::NO120_REPLY:
    case SPECIALPOINT::NO90_REPLY:
    case SPECIALPOINT::NO60_REPLY:
    case SPECIALPOINT::NO30_REPLY:
    case SPECIALPOINT::NO0_REPLY:
    case SPECIALPOINT::CONTRA_WON:
    case SPECIALPOINT::SOLO:
      break;
    } // switch (sp->type)
  } // for (sp \in spv)

  return cards;
} // HandCards Trick::specialpoints_cards() const
