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

#include "../utils/string.h"

#include "rule.h"
#include "../game/game_summary.h"
#include "../player/player.h"
#include "../player/ai/ai.h"
#include "../card/trick.h"

#include "../misc/setting.h"

#include "../ui/ui.h"
#include "../ui/ui.wrap.h"


/**
 **
 ** constructor:
 ** load the players from the private data directory
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
Party::Party() :
  rule_(new Rule),
  players_(0),
  seed_first_(SEED_START),
  startplayer_first_(STARTPLAYER),
  round_startgame_(),
  roundno_(0),
  gameno_(0),
  game_(),
  seed_(this->seed_first_),
  startplayer_(this->startplayer_first_),
  game_summaries_()
{
  // load the players from the private data directory
  // or take default ones
  for (unsigned n = 0;
       (this->playerno() < this->rule()(Rule::NUMBER_OF_PLAYERS));
       n++) {
    Player* player = Player::new_(::setting(Setting::PRIVATE_DATA_DIRECTORY)
				  + "/player."
				  + DK::Utils::String::to_string(n + 1));
    if (player)
      this->players_.push_back(player);
    else
      this->players_.push_back(Player::new_((((n == 0) && (!FAST_PLAY))
					     ? Player::HUMAN
					     : Player::AI)));

    this->players_.back()->no_set(n);
  } // for (n; this->playerno() < this->rule()(Rule::NUMBER_OF_PLAYERS))

  return ;
} // Party::Party()

/**
 **
 ** destructor
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
Party::~Party()
{
  if (::setting(Setting::SAVE_PARTY_CHANGES))
    this->save_changes();

  delete this->game_;
  delete this->rule_;

  return ;
} // Party::~Party()

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the number of players
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
unsigned
Party::playerno() const
{
  return this->players().size();
} // unsigned Party::playerno() const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	number of humans in the party
 **
 ** @version	0.6.7
 **
 ** @author	Diether Knof
 **
 **/
unsigned
Party::humanno() const
{
  // count the number of human players
  unsigned human_no = 0;
  for (vector<Player*>::const_iterator player = this->players().begin();
       player != this->players().end();
       player++)
    if ((*player)->type() == Player::HUMAN)
      human_no += 1;

  return human_no;
} // unsigned Party::humanno() const

/**
 **
 ** -> result
 **
 ** @param	p	number of the player
 **
 ** @return	the player with the number 'p'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
Player&
Party::player(unsigned const p)
{
  DEBUG_ASSERTION((p < this->playerno()),
		  "Party::player(p):\n"
		  "'p' is not valid (");

  return *(this->players()[p]);
} // Player& Party::player(unsigned const p)

/**
 **
 ** -> result
 **
 ** @param	p	number of the player
 **
 ** @return	the player with the number 'p'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
Player const&
Party::player(unsigned const p) const
{
  DEBUG_ASSERTION((p < this->playerno()),
		  "Party::player(p):\n"
		  "'p' is not valid (");

  return *(this->players()[p]);
} // Player const& Party::player(unsigned const p) const

/**
 **
 ** Sets the player in the party
 ** (the playernumber is taken from 'player').
 ** The player is taken directly, 'party' deletes it afterwards.
 **
 ** @param	player	the player to be set
 ** @param	n	the number of the player
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::player_set(Player* player, unsigned const n)
{
  DEBUG_ASSERTION((n < this->playerno()),
		  "Party::player_set(player, n):\n"
		  "'n' is not valid");

  DEBUG_ASSERTION(!((::game_status & GAMESTATUS::GAME)
		    && !this->game().trick_current().isfull()),
		  "Party::player_set(player, n):\n"
		  "  game_status is in the game.");

  delete this->players_[n];
  this->players_[n] = player;
  this->players_[n]->no_set(n);

  if (::ui != NULL)
    ::ui->player_update(this->player(n));

  return ;
} // void Party::player_set(Player* player, unsigned const n)

/**
 **
 ** set the name of the player 'player_no' to 'new_name'
 **
 ** @param	player_no	the number of the player
 ** @param	new_name	the new name
 **
 ** @return	-
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::playername_set(unsigned const player_no,
		      string const& new_name)
{
  DEBUG_ASSERTION((player_no < this->playerno()),
		  "Party::playername_set():  Illegal Value for player number: "
		  << player_no);

  this->players_[player_no]->name_set(new_name);
  if (::ui != NULL)
    ::ui->name_update(this->player(player_no));

  return ;
} // void Party::playername_set(player_no, new_name)

/**
 **
 ** sets the type of the player 'player_no' to 'new_type'
 **
 ** @param	player_no	the number of the player
 ** @param	new_type	the new type
 **
 ** @return	-
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 ** @todo	keep the aiconfig
 **
 **/
void
Party::playertype_set(unsigned const player_no,
		      Player::Type const new_type)
{
  DEBUG_ASSERTION((player_no < this->playerno()),
		  "Party::playertype_set(): "
		  << "  Illegal Value for player number: "
		  << player_no); 

  DEBUG_ASSERTION(this->players()[player_no],
		  "Party::playertype_set(): "
		  << "  player " << player_no << " does not exists");

  if (this->player(player_no).type() == new_type)
    return ;

  // create the new player
  this->player_set(Player::new_(new_type, this->player(player_no)),
		   player_no);

  return ;
} // void Party::playertype_set(unsigned const player_no, Player::Type const new_type)

/**
 **
 ** sets the starting seed of the party to be random
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::set_random_seed_first()
{
  this->set_seed_first(UINT_MAX);

  return ;
} // void Party::set_random_seed_first()

/**
 **
 ** sets the seed
 **
 ** @param	seed_new	new seed
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::set_seed(unsigned const seed_new)
{
  if (seed_new == UINT_MAX)
    this->set_random_seed();
  else
    this->seed_ = seed_new % PSEUDO_RAND_MAX;

  return ;
} // void Party::set_seed(unsigned const seed_new)

/**
 **
 ** sets the seed of the party to a random value
 ** (uses the system clock)
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 ** @todo	'better' random value
 **
 **/
void
Party::set_random_seed()
{
  // *** not so elegant
  //this->seed_ = static_cast<unsigned>(clock()) % PSEUDO_RAND_MAX;

  this->seed_ = static_cast<unsigned>(time(NULL) % PSEUDO_RAND_MAX);
  this->seed_next();

  return ;
} // void Party::set_random_seed()

/**
 **
 ** calculates the next seed with the old one
 **
 ** @param	-
 **
 ** @return	the new seed
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
unsigned
Party::seed_next()
{
  ::pseudo_rand_set(this->seed());

  // don't take the next random value but skip 48 values
  // (because 48 values are used for the distribution of the cards
  //  and we do want totally new hands)
  for (unsigned i = 0; i < 49; i++)
    this->seed_ = ::pseudo_rand_next();

  if (SEED_OUT) {
    // count the seeds
    static int seed_count = 0;
    seed_count += 1;

    // when a random number has already been tested, stop
    static vector<bool> tested(PSEUDO_RAND_MAX, false);
    if (tested[this->seed()] || (seed_count == PSEUDO_RAND_MAX)) {
      cout << "\nNumber of seeds: " << seed_count
	<< " (" << PSEUDO_RAND_MAX << ")" << endl;
      ::game_status = GAMESTATUS::QUIT;
      ::exit(0);
    }

    tested[this->seed()] = true;
  } // if (SEED_OUT)

  return this->seed();
} // unsigned Party::seed_next()

/**
 **
 ** sets the first startplayer to random
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::set_random_startplayer_first()
{
  this->startplayer_first_ = UINT_MAX;

  return ;
} // void Party::set_random_startplayer_first()

/**
 **
 ** -> result
 **
 ** @param	gameno	number of the game
 **
 ** @return	whether game 'gameno' starts a new round
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
bool
Party::starts_new_round(unsigned const gameno) const
{
  DEBUG_ASSERTION((gameno <= this->gameno()),
		  "Party::starts_new_round:\n"
		  "  'gameno' = " << gameno
		  << " > " << this->gameno() << " = this->gameno()");

  for (vector<unsigned>::const_iterator s = this->round_startgame().begin();
       s != this->round_startgame().end();
       ++s) {
    if (*s == gameno) {
      return true;
    }
    else if (*s > gameno) {
      return false;
    }
  }

  return false;
} // bool Party::starts_new_round(unsigned const gameno) const

/**
 **
 ** -> result
 **
 ** @param	gameno	number of the game
 **
 ** @return	the round game 'gameno' is in
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
unsigned
Party::round_of_game(unsigned const gameno) const
{
  DEBUG_ASSERTION((gameno <= this->gameno()),
		  "Party::round_of_game:\n"
		  "  'gameno' = " << gameno
		  << " > " << this->gameno() << " = this->gameno()");

  for (unsigned r = 1; r <= this->roundno(); ++r)
    if (this->round_startgame()[r] > gameno)
      return r - 1;

  return this->roundno();
} // unsigned Party::round_of_game(unsigned const gameno) const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	the game summary of the last game
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
GameSummary const&
Party::last_game_summary() const
{
  return *this->game_summaries().back();
} // GameSummary const& Party::last_game_summary() const;

/**
 **
 ** -> result
 **
 ** @param	gameno	number of the game
 **
 ** @return	the game summary of game 'gameno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
GameSummary const&
Party::game_summary(unsigned const gameno) const
{
  DEBUG_ASSERTION((gameno < this->game_summaries().size()),
		  "Party::game_summary(gameno):\n"
		  "  gameno = " << gameno << " is too great "
		  "(maximal " << this->game_summaries().size());
  return *this->game_summaries()[gameno];
} // GameSummary const& Party::game_summary(unsigned const gameno) const;

/**
 **
 ** -> result
 **
 ** @param	roundno	number of round
 **
 ** @return	the number of the first game in the round
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
unsigned
Party::round_startgame(unsigned const roundno) const
{
  return this->round_startgame()[roundno];
} // unsigned Party::round_startgame(unsigned const roundno) const

/**
 **
 ** -> result
 **
 ** @param	-
 **
 ** @return	how many points has been made so far
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum() const
{
  if (this->game_summaries().empty())
    return 0;
  return this->pointsum_till_game(this->game_summaries().size() - 1);
} // int Party::pointsum() const

/**
 **
 ** -> result
 **
 ** @param	playerno	number of player
 **
 ** @return	how many points has been made till game 'gameno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum_till_game(unsigned const gameno) const
{
  int points = 0;

  for (unsigned g = 0; g <= gameno; ++g)
    points += this->game_summary(g).points();

  return points;
} // int Party::pointsum_till_game(unsigned const gameno) const

/**
 **
 ** -> result
 **
 ** @param	roundno		number of round
 **
 ** @return	how many points has been made till round 'roundno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum_till_round(unsigned const roundno) const
{
  int points = 0;

  for (unsigned r = 0; r <= roundno; ++r)
    points += this->pointsum_in_round(r);

  return points;
} // int Party::pointsum_till_round(unsigned const roundno) const

/**
 **
 ** -> result
 **
 ** @param	roundno		number of round
 **
 ** @return	how many points has been made in round 'roundno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum_in_round(unsigned const roundno) const
{
  unsigned const gameno_end
    = ( (roundno == this->roundno())
	? this->gameno() + ((::game_status == GAMESTATUS::GAME_FINISHED)
			    ? 1
			    : 0)
	: this->round_startgame(roundno + 1) );

  int points = 0;

  for (unsigned g = this->round_startgame(roundno); g < gameno_end; ++g)
    points += this->game_summary(g).points();

  return points;
} // int Party::pointsum_in_round(unsigned const roundno) const

/**
 **
 ** -> result
 **
 ** @param	player	player
 **
 ** @return	how many points 'player' has made so far
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum(Player const& player) const
{
  return this->pointsum(player.no());
} // int Party::pointsum(Player player) const

/**
 **
 ** -> result
 **
 ** @param	playerno	number of player
 **
 ** @return	how many points player 'playerno' has made so far
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum(unsigned const playerno) const
{
  if (this->game_summaries().empty())
    return 0;
  return this->pointsum_till_game(this->game_summaries().size() - 1, playerno);
} // int Party::pointsum(unsigned const playerno) const

/**
 **
 ** -> result
 **
 ** @param	gameno		number of the game
 ** @param	playerno	number of player
 **
 ** @return	how many points player 'playerno' has made till game 'gameno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum_till_game(unsigned const gameno, unsigned const playerno) const
{
  int points = 0;

  for (unsigned g = 0; g <= gameno; ++g)
    points += this->game_summary(g).points(playerno);

  return points;
} // int Party::pointsum_till_game(unsigned const gameno, unsigned const playerno) const

/**
 **
 ** -> result
 **
 ** @param	roundno		number of round
 ** @param	playerno	number of player
 **
 ** @return	how many points player 'playerno' has made till round 'roundno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum_till_round(unsigned const roundno,
			   unsigned const playerno) const
{
  int points = 0;

  for (unsigned r = 0; r <= roundno; ++r)
    points += this->pointsum_in_round(r, playerno);

  return points;
} // int Party::pointsum_till_round(unsigned const roundno, unsigned const playerno) const

/**
 **
 ** -> result
 **
 ** @param	roundno		number of round
 ** @param	playerno	number of player
 **
 ** @return	how many points player 'playerno' has made in round 'roundno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
int
Party::pointsum_in_round(unsigned const roundno, unsigned const playerno) const
{
  unsigned const gameno_end
    = ( (roundno == this->roundno())
	? this->gameno() + ((::game_status == GAMESTATUS::GAME_FINISHED)
			    ? 1
			    : 0)
	: this->round_startgame(roundno + 1) );

  int points = 0;

  for (unsigned g = this->round_startgame(roundno); g < gameno_end; ++g)
    points += this->game_summary(g).points(playerno);

  return points;
} // int Party::pointsum_in_round(unsigned const roundno, unsigned const playerno) const

/**
 **
 ** -> result
 **
 ** @param	player	player
 **
 ** @return	the rang of 'player'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 **/
unsigned
Party::rang(Player const& player) const
{
  return this->rang(player.no());
} // unsigned Party::rang(Player player) const

/**
 **
 ** -> result
 **
 ** @param	playerno	number of player
 **
 ** @return	the rang of the player
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 **/
unsigned
Party::rang(unsigned const playerno) const
{
  unsigned rang = 0;
  int const points = this->pointsum(playerno);

  for (unsigned p = 0; p < this->playerno(); ++p)
    if ((p != playerno)
	&& (points < this->pointsum(p)))
      rang += 1;

  return rang;
} // unsigned Party::rang(unsigned const playerno) const

/**
 **
 ** -> result
 **
 ** @param	player	player
 **
 ** @return	the number of solo games 'player' has played
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 ** @todo	not calculate duty soli
 **
 **/
unsigned
Party::lustsolo_games(Player const& player) const
{
  return this->lustsolo_games(player.no());
} // unsigned Party::lustsolo_games(Player player) const

/**
 **
 ** -> result
 **
 ** @param	playerno	number of player
 **
 ** @return	the number of solo games the player has played
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 ** @todo	not calculate duty soli
 **
 **/
unsigned
Party::lustsolo_games(unsigned const playerno) const
{
  unsigned n = 0;

  for (vector<GameSummary*>::const_iterator
       game_summary = this->game_summaries().begin();
       game_summary != this->game_summaries().end();
       ++game_summary)
    if ((*game_summary)->is_solo()
	&& ((*game_summary)->soloplayer_no() == playerno) )
      n += 1;

  return n;
} // unsigned Party::lustsolo_games(unsigned const playerno) const

/**
 **
 ** write the party into the output stream
 **
 ** @param	ostr	output stream
 **
 ** @return	output stream
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 ** @todo	output of the game?
 **
 **/
ostream&
Party::out(ostream& ostr) const
{
  ostr << "Party:\n";

  ostr << "first seed: " << this->seed_first() << "\n"
    << "first startplayer: " << this->startplayer_first() << "\n"
    << "seed: " << this->seed() << "\n"
    << "startplayer: " << this->startplayer() << "\n"
    << "gameno: " << this->gameno() << "\n"
    << "\n";
  ostr << "Rules: \n"
    << "{\n"
    << this->rule()
    << "}\n";

  ostr << "Players: \n"
    << "{";
  for (vector<Player*>::const_iterator player
       = ::party.players().begin();
       player != this->players().end();
       player++)
    ostr << "Player " << (*player)->no()
      << " (" << (*player)->name()
      << "):\n"
	     << **player
	       << "\n";
  ostr << "}\n";
  ostr << "\n";

#ifdef POSTPHONED
  // *** is this necessary?
  if (this->game_)
    ostr << "Game:\n"
      << this->game();
#endif

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

/**
 **
 ** inits the party:
 ** get the info of the first startplayer, the first seed,
 ** the ruleset and the players
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::init()
{
  for (vector<Player*>::const_iterator player = this->players().begin();
       player != this->players().end();
       player++)
    ::ui->name_update(**player);

  if (::game_status != GAMESTATUS::PARTY_INIT)
    return ;

  // get the settings
  ::ui->party_get_settings();
  if (dynamic_cast<UI_Wrap*>(::ui)
      && (dynamic_cast<UI_Wrap*>(::ui)->ui->type() == UI_TYPE::DUMMY)) {
    for (unsigned p = 0; p < this->playerno(); p++)
      this->playertype_set(p, Player::AI);
  } // if (::ui->type() == UI_TYPE::NONE)


  if (::game_status != GAMESTATUS::PARTY_INIT)
    return ;

  // set the seed
  this->set_seed(this->seed_first());
  // set the startplayer
  if (this->startplayer_first() == UINT_MAX) {
    this->startplayer_ = (static_cast<unsigned>(::time(NULL))
			  % this->playerno());
  } else {
    this->startplayer_ = this->startplayer_first();
  }

  if (::setting(Setting::SAVE_PARTY_CHANGES))
    this->save_changes();

  return ;
} // void Party::init()

/**
 **
 ** save the changes
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::save_changes() const
{
  // save the rules in the private data directory
  this->rule().save(::setting(Setting::PRIVATE_DATA_DIRECTORY)
		    + "/"
		    + ::setting(Setting::RULES_DIRECTORY)
		    + "/"
		    + "last");

  { // save the players in the private data directory
    unsigned n = 0;
    for (vector<Player*>::const_iterator player = this->players().begin();
	 player != this->players().end();
	 ++n, ++player)
      (*player)->save(::setting(Setting::PRIVATE_DATA_DIRECTORY)
		      + "/player." + DK::Utils::String::to_string(n + 1));
  } // save the players in the private data directory

  return ;
} // void Party::save_changes() const

/**
 **
 ** the party is opened
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::open()
{
  this->gameno_ = 0;
  this->roundno_ = 0;

  return ;
} // void Party::open()

/**
 **
 ** the party is closed
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::close()
{
  this->round_startgame_.clear();

  for (vector<GameSummary*>::iterator gs = this->game_summaries_.begin();
       gs != this->game_summaries_.end();
       ++gs)
    delete *gs;
  this->game_summaries_.clear();

  return ;
} // void Party::close()

/**
 **
 ** play the party:
 ** play games until the user ends the party
 ** or the party is ended according to the rules
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
void
Party::play()
{
  using namespace GAMESTATUS;

  // init the party
  ::game_status = PARTY_INIT;
  this->init();

  if (::game_status != PARTY_INIT)
    return ;

  ::game_status = PARTY_PLAY;


  this->round_startgame_.push_back(0);
  this->roundno_ = 0;
  try {
    ::ui->party_start();
    ::ui->party_start_round(this->roundno());
    for (this->gameno_ = 0; ::game_status == PARTY_PLAY; ++(this->gameno_)) {

#ifdef POSTPHONED
      if (this->gameno_ == this->rule()(Rule::NUMBER_OF_GAMES))
	break;
#endif

      if (SEED_OUT)
	this->startplayer_ = 0;

      // create a new game
      ::game_status = GAME_NEW;
      this->game_ = new Game(*this, *(this->players_[this->startplayer()]));
      try {
	this->game().play();
	if (SEED_OUT) {
	  for (vector<Player*>::const_iterator p = this->players().begin();
	       p != this->players().end();
	       ++p)
	    (*p)->game_close(this->game());
	  ::ui->game_close();

	  delete this->game_;
	  this->game_ = NULL;
	  this->seed_next();
	  ::game_status = PARTY_PLAY;
	  continue;
	}

	DEBUG_ASSERTION((::game_status == GAME_PLAY),
			"Party::play():\n"
			"  wrong gamestatus (needs 'GAME_PLAY': " << ::game_status);

	::game_status = GAME_FINISHED;
	this->game().finish();

	this->game_summaries_.push_back(new GameSummary(this->game()));
	for (unsigned p = 0; p < this->playerno(); ++p)
	  this->player(p).db_->evaluate(this->player(p),
					*this->game_summaries().back());

	::ui->game_finish();
      } catch (...) {
	for (vector<Player*>::const_iterator p = this->players().begin();
	     p != this->players().end();
	     ++p)
	  (*p)->game_close(this->game());
	::ui->game_close();

	delete this->game_;
	this->game_ = NULL;

	throw;
      } // try

      for (vector<Player*>::const_iterator p = this->players().begin();
	   p != this->players().end();
	   ++p)
	(*p)->game_close(this->game());
      ::ui->game_close();

      ::game_status = PARTY_PLAY;
#ifdef OUTDATED
      // 0.6.6
      if ((this->game().type() == GAMETYPE::NORMAL)
	  || ((this->game().type() == GAMETYPE::POVERTY)
	      && (this->rule()(Rule::POVERTY_SHIFT))
	     )
	  || (this->game().type() == GAMETYPE::MARRIAGE))
#endif
	if (!GAMETYPE::is_solo(this->game().type())
	    || !this->rule()(Rule::LUSTSOLO_PLAYER_LEADS)) {

	  if ( ((this->startplayer_ + 1) % this->playerno())
	       == this->startplayer_first_) {
	    // end of round

	    if (this->rule()(Rule::NUMBER_OF_ROUNDS_LIMITED)
		&& (this->roundno() + 1 >= this->rule()(Rule::NUMBER_OF_ROUNDS)) )
	      ::game_status = GAMESTATUS::PARTY_FINISHED;
	    else { 
	      this->roundno_ += 1;
	      this->round_startgame_.push_back(this->gameno() + 1);
	      ::ui->party_start_round(this->roundno());
	    }
	  } // if (round ended)

	  (this->startplayer_ += 1) %= this->playerno();
	} // if (next player)

      delete this->game_;
      this->game_ = NULL;

#ifdef WORKAROUND
      // as long as the program crashes
      // or one cannot interrupt the ai
      if (::setting(Setting::SAVE_PARTY_CHANGES))
	this->save_changes();
#endif

      this->seed_next();
    } // for (gameno)
  } catch (GameStatus const& gamestatus) {
    if (gamestatus != GAMESTATUS::PARTY_FINISHED)
      throw;

    ::game_status = GAMESTATUS::PARTY_FINISHED;
  } // try

  ::ui->party_finish();

  return ;
} // void Party::play()

/**
 **
 ** write the party into the output stream
 **
 ** @param	ostr	output stream
 ** @param	party	party
 **
 ** @return	output stream
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **
 **/
ostream&
operator<<(ostream& ostr, Party const& party)
{
  party.out(ostr);

  return ostr;
} // ostream& operator<<(ostream& ostr, Party const& party)
