/********************************************************************** * * FreeDoko a Doppelkopf-Game * * Copyright (C) 2001-2007 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 #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 "../misc/translations.h" #include "../ui/ui.h" #include "../ui/ui.wrap.h" #ifdef CHECK_RUNTIME #include "../runtime.h" #endif /** ** 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), real_seed_first_(SEED_START), real_startplayer_first_(STARTPLAYER), round_startgame_(), points_(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 p = 0; (this->playerno() < this->rule()(Rule::NUMBER_OF_PLAYERS)); p++) { this->players_.push_back(Player::new_(p, (p == 0) ? Player::HUMAN : Player::AI)); } // 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() { 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::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 - ** ** @return the players ** ** @author Diether Knof ** ** @version 0.7.0 **/ vector& Party::players() { return this->players_; } // vector& Party::players() /** ** -> 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 (" << p << ")"); return *(this->players()[p]); } // Player const& Party::player(unsigned const p) const /** ** Sets the player in the party ** 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::set_player(Player* player, unsigned const n) { DEBUG_ASSERTION((n < this->playerno()), "Party::set_player(player, n):\n" "'n' is not valid"); DEBUG_ASSERTION(!(::game_status & GAMESTATUS::GAME), "Party::set_player(player, n):\n" " game_status is in the game."); delete this->players_[n]; this->players_[n] = player; this->players_[n]->set_no(n); if (::ui != NULL) ::ui->player_changed(this->player(n)); return ; } /** ** switches the player in the party ** The player is taken directly, the replaced player is returned ** ** @param player the player to be switched ** @param n the number of the player ** ** @return replaced player ** (has to be deleted!) ** ** @author Diether Knof ** ** @version 0.7.2 **/ Player* Party::switch_player(Player* player, unsigned const n) { DEBUG_ASSERTION((n < this->playerno()), "Party::switch_player(player, n):\n" "'n' is not valid: " << n); DEBUG_ASSERTION(!(::game_status & GAMESTATUS::GAME), "Party::switch_player(player, n):\n" " game_status is in the game."); Player* const player_bak = this->players_[n]; this->players_[n] = player; this->players_[n]->set_no(n); if (::ui != NULL) ::ui->player_changed(this->player(n)); return player_bak; } // Player* Party::switch_player(Player* player, unsigned const n) /** ** switches the position of the players ** ** @param n0 the first player ** @param n1 the second player ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.2 **/ void Party::switch_players(unsigned const n0, unsigned const n1) { DEBUG_ASSERTION((n0 < this->playerno()), "Party::switch_players(n0, n1):\n" "'n0' is not valid"); DEBUG_ASSERTION((n1 < this->playerno()), "Party::switch_players(n0, n1):\n" "'n1' is not valid"); DEBUG_ASSERTION(!(::game_status & GAMESTATUS::GAME), "Party::switch_players(n0, n1):\n" " game_status is in the game."); std::swap(this->players_[n0], this->players_[n1]); this->players_[n0]->set_no(n0); this->players_[n1]->set_no(n1); ::ui->players_switched(this->player(n0), this->player(n1)); return ; } // void Party::switch_players(unsigned const n0, unsigned const n1) /** ** 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::set_playername(unsigned const player_no, string const& new_name) { DEBUG_ASSERTION((player_no < this->playerno()), "Party::set_playername(): Illegal Value for player number: " << player_no); this->players_[player_no]->set_name(new_name); return ; } // void Party::set_playername(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::set_playertype(unsigned const player_no, Player::Type const new_type) { DEBUG_ASSERTION((player_no < this->playerno()), "Party::set_playertype(): " << " Illegal Value for player number: " << player_no); DEBUG_ASSERTION(this->players()[player_no], "Party::set_playertype(): " << " player " << player_no << " does not exists"); Player const& player = this->player(player_no); if (player.type() == new_type) return ; bool const default_name = ( Player::default_name(player.type(), player_no) == player.name()); // create the new player this->set_player(Player::new_(new_type, player), player_no); if (default_name) this->player(player_no).set_default_name(); return ; } // void Party::set_playertype(unsigned const player_no, Player::Type const new_type) /** ** -> result ** ** @param - ** ** @return the seed ** ** @author Diether Knof ** ** @version 0.7,3 **/ unsigned Party::seed() const { if (this->in_configuration()) return this->seed_first(); return this->seed_; } // unsigned Party::seed() const /** ** sets the seed ** ** @param seed new seed ** ** @return - ** ** @author Diether Knof ** ** @version 0.7,3 **/ void Party::set_seed(unsigned const seed) { if (this->in_configuration()) { if (this->seed_first() != seed) { this->seed_first_ = seed; ::ui->party_seed_changed(); } } if (seed == UINT_MAX) this->set_random_seed(); else this->seed_ = seed % PSEUDO_RAND_MAX; return ; } // void Party::set_seed(unsigned seed) /** ** sets the seed of the party to a random value ** (uses the system clock) ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 ** ** @todo 'better' random value **/ void Party::set_random_seed() { if (this->in_configuration()) { if (this->seed_first() != UINT_MAX) { this->seed_first_ = UINT_MAX; ::ui->party_seed_changed(); } } // cannot use 'seed_next' since that would take 'seed_first' for 'seed()' ::pseudo_rand_set(static_cast(time(NULL) % PSEUDO_RAND_MAX)); for (unsigned i = 0; i < 10; i++) ::pseudo_rand_next(); this->seed_ = ::pseudo_rand_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 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() /** ** -> result ** ** @param - ** ** @return the startplayer ** ** @author Diether Knof ** ** @version 0.7,3 **/ unsigned Party::startplayer() const { if (this->in_configuration()) return this->startplayer_first(); return this->startplayer_; } // unsigned Party::startplayer() const /** ** sets the first startplayer ** ** @param startplayer number of the startplayer ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void Party::set_startplayer(unsigned const startplayer) { if (this->in_configuration()) { if (this->startplayer_first() != startplayer) { this->startplayer_first_ = startplayer; ::ui->party_startplayer_changed(); } } if (startplayer == UINT_MAX) this->set_random_startplayer(); else this->startplayer_ = startplayer; return ; } // void Party::set_startplayer(unsigned const startplayer) /** ** sets the startplayer to random ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 ** ** @todo better 'random' value **/ void Party::set_random_startplayer() { if (this->in_configuration()) { if (this->startplayer_first() != UINT_MAX) { this->startplayer_first_ = UINT_MAX; ::ui->party_startplayer_changed(); } } this->startplayer_ = static_cast(time(NULL) % this->playerno()); return ; } // void Party::set_random_startplayer_first() /** ** -> result ** ** @param - ** ** @return whether the party can be configured (seed, startplayer) ** ** @author Diether Knof ** ** @version 0.7.4 **/ bool Party::in_configuration() const { switch (::game_status) { case GAMESTATUS::PROGRAMSTART: case GAMESTATUS::PARTY_NEW: return true; case GAMESTATUS::PARTY_INITIAL_LOADED: return (this->gameno() == 0); default: return false; } // switch (::game_status) } // bool Party::in_configuration() const /** ** -> result ** ** @param - ** ** @return the number of the current round ** ** @author Diether Knof ** ** @version 0.6.9 **/ unsigned Party::roundno() const { if (this->round_startgame_.empty()) return 0; return (this->round_startgame_.size() - 1); } // unsigned Party::roundno() const /** ** -> result ** ** @param - ** ** @return the number of remaining rounds ** ** @author Diether Knof ** ** @version 0.6.9 **/ unsigned Party::remaining_rounds() const { if (!this->rule()(Rule::NUMBER_OF_ROUNDS_LIMITED)) return 0; return (this->rule()(Rule::NUMBER_OF_ROUNDS) - this->roundno()); } // unsigned Party::remaining_rounds() const /** ** -> result ** ** @param - ** ** @return the number of the current game ** ** @author Diether Knof ** ** @version 0.6.9 **/ unsigned Party::gameno() const { if (::game_status == GAMESTATUS::GAME_FINISHED) return (this->game_summaries().size() - 1); else return this->game_summaries().size(); } // unsigned Party::gameno() const /** ** -> result ** ** @param - ** ** @return whether this is the last game ** ** @author Diether Knof ** ** @version 0.7.4 ** ** @todo duty solo **/ bool Party::is_last_game() const { // must be in a running party if (!(::game_status >= GAMESTATUS::PARTY_PLAY)) return false; // if the number of rounds is limited // it must be the last round and the next player to start // is the startplayer of the party if ( this->rule()(Rule::NUMBER_OF_ROUNDS_LIMITED) && (this->remaining_rounds() <= 1) && ((this->startplayer() + 1) % this->playerno() == this->startplayer_first() ) && ( (::game_status <= GAMESTATUS::GAME_RESERVATION) || !( GAMETYPE::is_solo(this->game().type()) // ToDo: duty solo && this->rule()(Rule::LUSTSOLO_PLAYER_LEADS) ) ) ) return true; // the points are limited if ( this->rule()(Rule::POINTS_LIMITED) && (this->remaining_points() <= 0) ) return true; return false; } // bool Party::is_last_game() const /** ** -> result ** ** @param - ** ** @return the remaining points ** ** @author Diether Knof ** ** @version 0.7.4 **/ int Party::remaining_points() const { if (!this->rule()(Rule::POINTS_LIMITED)) return -1; return (static_cast(this->rule()(Rule::POINTS)) - this->points()); } // unsigned Party::remaining_points() const /** ** -> 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::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 { if (gameno <= this->gameno()) { for (unsigned r = 1; r <= this->roundno(); ++r) if (this->round_startgame()[r] > gameno) return r - 1; return this->roundno(); } // if (gameno <= this->gameno()) // for future games, we have to estimate if (this->roundno() == 0) return (gameno / this->playerno()); return ( (gameno * this->roundno()) / this->round_startgame()[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.7.3 **/ unsigned Party::round_startgame(unsigned const roundno) const { if (roundno <= this->roundno()) return this->round_startgame()[roundno]; if (roundno == 0) return 0; // if roundno > current round we have to estimate if (::game_status == GAMESTATUS::GAME_FINISHED) return (this->gameno() + (this->playerno() - ((this->playerno() + this->startplayer() - this->startplayer_first() ) % this->playerno()) ) + ( ( GAMETYPE::is_solo(this->game().type()) && this->rule()(Rule::LUSTSOLO_PLAYER_LEADS)) ? 1 : 0) + (roundno - this->roundno() - 1) * this->playerno() ); else if (::game_status & GAMESTATUS::GAME) return (this->gameno() + (this->playerno() - ((this->playerno() + this->startplayer() - this->startplayer_first() ) % this->playerno()) ) + ( ( GAMETYPE::is_solo(this->game().type()) && this->rule()(Rule::LUSTSOLO_PLAYER_LEADS)) ? 1 : 0) + (roundno - this->roundno() - 1) * this->playerno() ); else return (this->gameno() + (this->playerno() - ((this->startplayer() - this->startplayer_first() + this->playerno()) % this->playerno()) ) + (roundno - this->roundno() - 1) * this->playerno() ); } // 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 gameno game number ** ** @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); if ( (gameno < this->gameno()) || (::game_status == GAMESTATUS::GAME_FINISHED) ) points += this->game_summary(gameno).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::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 ** The current game is not written. ** ** @param ostr output stream ** ** @return whether the writing was successful ** ** @author Diether Knof ** ** @version 0.6.9 ** ** @todo output of the game? **/ bool Party::write(ostream& ostr) const { if (::game_status < GAMESTATUS::PARTY_PLAY) { ostr << "first seed = " << Unsigned(this->seed_first()) << "\n" << "first startplayer = " << Unsigned(this->startplayer_first()) << "\n" << "\n"; } else { ostr << "first seed = " << Unsigned(this->real_seed_first()) << "\n" << "first startplayer = " << Unsigned(this->real_startplayer_first()) << "\n" << "\n"; } ostr << "rules \n" << "{\n" << this->rule() << "}\n"; ostr<< '\n'; ostr << "players\n" << "{\n"; for (vector::const_iterator player = this->players().begin(); player != this->players().end(); player++) ostr << "{\n" << **player << "}\n\n"; ostr << "}\n"; ostr << '\n'; ostr << "game summaries\n"; ostr << "{\n"; for (vector::const_iterator s = this->game_summaries().begin(); s != this->game_summaries().end(); ++s) ostr << "{\n" << **s << "}\n"; ostr << "}\n"; ostr << '\n'; ostr << "points\n"; ostr << "{\n"; for (vector::const_iterator player = this->players().begin(); player != this->players().end(); player++) ostr << (*player)->name() << ": " << this->pointsum(**player) << endl; ostr << "}\n"; return ostr.good(); } // bool Party::write(ostream& ostr) const /** ** reads the party from the input stream ** ** @param istr input stream ** ** @return whether the reading was successful ** ** @author Diether Knof ** ** @version 0.6.9 **/ bool Party::read(istream& istr) throw (ReadException) { // party config values Unsigned first_seed = UINT_MAX - 1; Unsigned first_startplayer = UINT_MAX - 1; Rule* rule = NULL; vector players; vector game_summaries; Config config; // the config read int depth = 0; // the depth in the blocks try { do { // while (istr) istr >> config; if (istr.eof()) break; if (istr.fail()) break; if ((config.name == "") && (config.value == "")) break; if (config.value.empty()) { if (config.name == "{") { depth += 1; } else if (config.name == "}") { depth -= 1; // finished with the party if (depth == -1) istr.putback('}'); if (depth <= 0) break; #ifndef OUTDATED // 0.7.4 2006-09-14 } else if ( (config.name == "rules") || (config.name == "Rules") ) { #else } else if (config.name == "rules") { #endif if (rule) throw ReadException("Party read: rule already read"); rule = new Rule(); if (!rule->read(istr)) throw ReadException("Party read: rule failed"); #ifndef OUTDATED // 0.7.4 2006-09-14 } else if ( (config.name == "players") || (config.name == "Players") ) { #else } else if (config.name == "players") { #endif if (!players.empty()) throw ReadException("Party read: multiple players"); istr >> config; if (! (config.value.empty() && (config.name == "{")) ) throw ReadException("Party read: players: expected '{'"); while (istr.peek() != '}') { Player* player = Player::new_(players.size(), istr); if (player == NULL) throw ReadException("Party read: players: read of player failed"); players.push_back(player); // overread empty lines while ( (istr.peek() == '\n') || (istr.peek() == '\r')) istr.get(); } // while (istr.peek() != '}') istr >> config; if (! (config.value.empty() && (config.name == "}")) ) throw ReadException("Party read: players: expected '}'"); } else if (config.name == "game summaries") { { string const line = read_line(istr); if (line != "{") throw ReadException("Party read: game summaries: expected '{', " " found '" + line + "'"); } GameSummary* game_summary = NULL; while (istr.good() && istr.peek() != '}') { try { game_summary = new GameSummary(*rule, istr); } catch(...) { delete game_summary; throw; } game_summaries.push_back(game_summary); } // while (istr.good() && istr.peek() !=- '}') { string const line = read_line(istr); if (line != "}") throw ReadException("Party read: game summaries: expected '}', " " found '" + line + "'"); } } else if (config.name == "points") { // ignore the block { string const line = read_line(istr); if (line != "{") throw ReadException("Party read: points: expected '{', " " found '" + line + "'"); } while (istr.good() && istr.peek() != '}') { Config points_config; istr >> points_config; } // while (istr.good() && istr.peek() != '}') } else { // if (config.name == ...) throw ReadException("Party read: unknown line " "'" + config.name + "'"); } // if (config.name == ...) } else { // if !(config.value.empty()) if (config.name == "first seed") { istringstream istr2(config.value); istr2 >> first_seed; if (!istr2.good() && !istr2.eof()) throw ReadException("Party read: error loading first seed"); } else if (config.name == "first startplayer") { istringstream istr2(config.value); istr2 >> first_startplayer; if (!istr2.good() && !istr2.eof()) throw ReadException("Party read: error loading first seed"); } else { // if (config.name == ...) throw ReadException("Party read: unknown line " "'" + config.name + " = " + config.value + "'"); } // if (config.name == ...) } // if !(config.value.empty()) } while (istr.good()); if (!istr.good() && !istr.eof()) throw ReadException("Party read: unknown error in the input stream"); if (first_seed == UINT_MAX - 1) throw ReadException("Party read: first seed not loaded"); if (first_startplayer == UINT_MAX - 1) throw ReadException("Party read: first startplayer not loaded"); if (!rule) throw ReadException("Party read: rule not loaded"); if (players.size() != (*rule)(Rule::NUMBER_OF_PLAYERS_IN_GAME)) throw ReadException("Party read: incorrect number of players"); } catch (...) { delete rule; for (vector::iterator p = players.begin(); p != players.end(); ++p) delete *p; for (vector::iterator g = game_summaries.begin(); g != game_summaries.end(); ++g) delete *g; throw; } // try ::game_status = GAMESTATUS::PARTY_LOADED; delete this->rule_; this->rule_ = rule; for (vector::iterator p = this->players_.begin(); p != this->players_.end(); ++p) delete *p; this->players_ = players; for (unsigned p = 0; p < this->playerno(); ++p) this->player(p).set_no(p); this->seed_first_ = this->real_seed_first_ = first_seed; this->startplayer_first_ = this->real_startplayer_first_ = first_startplayer; for (vector::iterator g = this->game_summaries_.begin(); g != this->game_summaries_.end(); ++g) delete *g; this->game_summaries_ = game_summaries; if (this->game_summaries_.empty()) { this->seed_ = this->seed_first(); this->startplayer_ = this->startplayer_first(); this->round_startgame_.clear(); } else { // if !(this->game_summaries_.empty()) this->seed_ = this->game_summaries_.back()->seed(); this->startplayer_ = this->game_summaries_.back()->startplayer_no(); { // calculate round startgame unsigned gameno = 0; this->round_startgame_.clear(); this->round_startgame_.push_back(0); for (vector::const_iterator gs = this->game_summaries_.begin(); gs != this->game_summaries_.end(); ++gs, ++gameno) { // count the points for (unsigned p = 0; p < this->playerno(); ++p) { if (this->rule()(Rule::COUNTING) == COUNTING_NS::PLUS) this->points_ += (*gs)->points(p); else this->points_ -= (*gs)->points(p); } // for (p < this->playerno()) // increment the startplayer if (! ( GAMETYPE::is_solo((*gs)->game_type()) && this->rule()(Rule::LUSTSOLO_PLAYER_LEADS))) { if ((((*gs)->startplayer_no() + 1) % this->playerno()) == this->startplayer_first()) { this->round_startgame_.push_back(gameno + 1); } } // (startplayer changes } // for (gs \in this->game_summaries_) } // calculate round startgame // we want to start the following game if ( !(GAMETYPE::is_solo(this->game_summaries().back()->game_type()) && this->rule()(Rule::LUSTSOLO_PLAYER_LEADS))) (this->startplayer_ += 1) %= this->playerno(); this->seed_next(); } // if !(this->game_summaries_.empty()) return (istr.good() || istr.eof()); } // bool Party::read(istream& istr) /** ** saves the party in the given file ** ** @param filename file to save the party in ** ** @return whether the party could be saved ** ** @author Diether Knof ** ** @version 0.6.9 **/ bool Party::save(string const& filename) const { ofstream ostr(filename.c_str()); if (ostr.good()) { ostr << "#!FreeDoko\n" << "# FreeDoko party (version " << ::version << ")\n" << '\n'; this->write(ostr); } return ostr.good(); } // bool Party::save(string const& filename) const /** ** loads the party in the given file ** ** @param filename file to load the party from ** ** @return whether the loading was successful ** ** @author Diether Knof ** ** @version 0.6.9 **/ bool Party::load(string const& filename) throw (ReadException) { try { ifstream istr(filename.c_str()); if (istr.fail()) throw ReadException("Party load: open failed"); this->read(istr); if (!istr.good() && !istr.eof()) throw ReadException("Party load: unknown error"); } catch (ReadException const& exception) { ::ui->information(::translation("Message::party load: load error '%sfilename%'", filename) + "\n" + exception.message(), INFORMATION::PROBLEM); return false; } // try return true; } // bool Party::load(string const& filename) /** ** load the players ** loads the files 'player.?' from the FreeDoko private directory ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.4 **/ bool Party::load_players() throw (ReadException) { bool loaded_fail = false; for (unsigned p = 0; p < this->playerno(); ++p) { Player* player = Player::new_(p, ::setting(Setting::PRIVATE_DATA_DIRECTORY) + "/player." + DK::Utils::String::to_string(p + 1)); if (player) this->set_player(player, p); else loaded_fail = true; } // for (n < this->playerno()) return !loaded_fail; } // bool Party::load_players() /** ** 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::const_iterator player = this->players().begin(); player != this->players().end(); player++) ::ui->name_changed(**player); if ( dynamic_cast(::ui) && (dynamic_cast(::ui)->ui->type() == UI_TYPE::DUMMY)) { for (unsigned p = 0; p < this->playerno(); ++p) if (this->player(p).type() == Player::HUMAN) this->set_playertype(p, Player::AI); } // if (::ui->type() == UI_TYPE::NONE) // reset the points this->points_ = 0; // save the starting values this->real_seed_first_ = this->seed_; this->real_startplayer_first_ = this->startplayer_; if (::setting(Setting::SAVE_PARTY_CHANGES)) this->save_changes(); return ; } // void Party::init() /** ** the party is opened ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.7 **/ void Party::open() { return ; } // void Party::open() /** ** 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; ::ui->party_start(); if (this->gameno() == 0) this->round_startgame_.push_back(0); try { while (::game_status == PARTY_PLAY) { // check that the party is not finished, yet if ( this->rule()(Rule::NUMBER_OF_ROUNDS_LIMITED) && (this->roundno() >= this->rule()(Rule::NUMBER_OF_ROUNDS)) ) { ::game_status = GAMESTATUS::PARTY_FINISHED; break; } if (this->round_startgame_.back() == this->gameno()) ::ui->party_start_round(this->roundno()); // create a new game ::game_status = GAME_NEW; this->game_ = new Game(*this, *(this->players_[this->startplayer()])); try { // play the game this->game().play(); if (SEED_OUT) { for (vector::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; } // if (SEED_OUT) 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()); for (unsigned p = 0; p < this->playerno(); ++p) { if (this->rule()(Rule::COUNTING) == COUNTING_NS::PLUS) this->points_ += this->game_summaries_.back()->points(p); else this->points_ -= this->game_summaries_.back()->points(p); } // for (p < this->playerno()) if (::setting(Setting::SAVE_PARTY_CHANGES)) this->save_changes(); ::ui->game_finish(); } catch (...) { // play the game for (vector::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::const_iterator p = this->players().begin(); p != this->players().end(); ++p) (*p)->game_close(this->game()); ::ui->game_close(); #ifdef CHECK_RUNTIME cout << '\n' << ::runtime << endl; #endif if (this->is_last_game()) ::game_status = GAMESTATUS::PARTY_FINISHED; else ::game_status = PARTY_PLAY; if (!( ( GAMETYPE::is_solo(this->game().type()) && (this->game().type() != GAMETYPE::MARRIAGE_SILENT) ) && this->rule()(Rule::LUSTSOLO_PLAYER_LEADS)) ) { if ( ((this->startplayer_ + 1) % this->playerno()) == this->startplayer_first()) { // end of round this->round_startgame_.push_back(this->gameno()); } // if (end of round) (this->startplayer_ += 1) %= this->playerno(); } // if (next player) delete this->game_; this->game_ = NULL; if (this->rule()(Rule::MUTATE)) this->rule_->mutate(); this->seed_next(); } // while (game_status == PARTY_PLAY) } catch (GameStatus const& gamestatus) { if (gamestatus != GAMESTATUS::PARTY_FINISHED) throw; ::game_status = GAMESTATUS::PARTY_FINISHED; } // try if (::setting(Setting::SAVE_PARTY_CHANGES)) this->remove_auto_save(); return ; } // void Party::play() /** ** the party is closed ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.7 **/ void Party::close() { this->round_startgame_.clear(); for (vector::iterator gs = this->game_summaries_.begin(); gs != this->game_summaries_.end(); ++gs) delete *gs; this->game_summaries_.clear(); return ; } // void Party::close() /** ** save the file ** ** @param - ** ** @return whether the saving was successful ** ** @author Diether Knof ** ** @version 0.7.2 ** ** @todo autosave in network game **/ bool Party::auto_save() const { // save the party in the private data directory return this->save(::setting(Setting::PRIVATE_DATA_DIRECTORY) + "/" + ::setting(Setting::PARTIES_DIRECTORY) + "/" + "current"); } // bool Party::auto_save() const /** ** remove the auto saved file ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.7 **/ void Party::remove_auto_save() const { std::remove((::setting(Setting::PRIVATE_DATA_DIRECTORY) + "/" + ::setting(Setting::PARTIES_DIRECTORY) + "/" + "current").c_str()); return; } // void Party::remove_auto_save() const /** ** save the changes ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.9 **/ void Party::save_changes() const { #ifdef NETWORK if (!::server->empty()) return ; #endif ::ui->set_busy(); try { this->auto_save(); // 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::const_iterator player = this->players().begin(); player != this->players().end(); ++n, ++player) { if ((*player)->type() != Player::NETWORK) { (*player)->save(::setting(Setting::PRIVATE_DATA_DIRECTORY) + "/player." + DK::Utils::String::to_string(n + 1)); } } } // save the players in the private data directory ::ui->set_not_busy(); } catch(...) { ::ui->set_not_busy(); throw; } // try return ; } // void Party::save_changes() const /** ** write the party into the output stream ** ** @param ostr output stream ** @param party party ** ** @return output stream ** ** @author Diether Knof ** ** @version 0.6.9 **/ ostream& operator<<(ostream& ostr, Party const& party) { party.write(ostr); return ostr; } // ostream& operator<<(ostream& ostr, Party const& party)