/********************************************************************* * * 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 "constants.h" #include "bug_report_replay.h" #include "../player/player.h" #include "../player/aiconfig.h" #include "../game/gameplay_actions.h" #include "../card/trick.h" #include "../ui/ui.h" #include "../ui/ui.wrap.h" #include "../misc/setting.h" #include "../utils/string.h" #include "../utils/file.h" #include // whether this is the old format static bool OLD_FORMAT = false; // There can only be one 'bug report replay' instance. // If a new is created, the old is removed. OS_NS::BugReportReplay* bug_report_replay = NULL; namespace OS_NS { // returns the value from the line 'line' static string get_value(string const& line); // returns the keyword from the line 'line' static string get_keyword(string const& line); /** ** -> result ** ** @param line the line ** ** @return the value from the given line ** ** @author Diether Knof ** ** @version 0.7.3 **/ string get_value(string const& line) { if (line.empty()) return ""; string value = line; DK::Utils::String::word_first_remove_with_blanks(value); return value; } // static string get_value(string line) /** ** -> result ** ** @param line the line ** ** @return the keyword from the given line ** ** @author Diether Knof ** ** @version 0.7.3 **/ string get_keyword(string const& line) { if (line.empty()) return ""; string const word = ( (std::find(line.begin(), line.end(), ':') != line.end()) ? string(line.begin(), std::find(line.begin(), line.end(), ':')) : line #ifdef OUTDATED // 0.7.1 : DK::Utils::String::word_first(string line) #endif ); return word; } // static string get_keyword(string line) /** ** Constructor ** (to be called from a child class) ** ** @param filename the file with the bug report ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.7 **/ BugReportReplay::BugReportReplay(string const& filename) : OS(OS_TYPE::OS_BUG_REPORT_REPLAY), filename_(filename), loaded_(false), finished_(false), human_finished_(false), auto_action_end_(0), check_action_(UINT_MAX), version_(NULL), compiled_(), system_(), time_(), language_(), trickno_(), message_(), seed_(UINT_MAX), startplayer_no_(UINT_MAX), game_type_(GAMETYPE::NORMAL), marriage_selector_(MARRIAGE_SELECTOR::TEAM_SET), soloplayer_no_(UINT_MAX), rule_(), players_(), poverty_cards_shifted_(NULL), poverty_cards_returned_(NULL), hands_(), swines_player_no_(UINT_MAX), hyperswines_player_no_(UINT_MAX), actions_(), current_action_no_(0), actions_discrepancies_(), full_tricks_(), game_summary_(NULL), current_hands_(), current_trick_(NULL), human_actions_(), current_human_action_no_(0), human_actions_discrepancies_() { // remove an old bug report if (::bug_report_replay) { static_cast(::ui)->remove(::bug_report_replay); delete ::bug_report_replay; ::bug_report_replay = NULL; } // if (::bug_report_replay) if (!DK::Utils::File::isfile(filename)) { cerr << "Selected bug report '" << filename << "' is no file." << endl; return ; } // if (!DK::Utils::File::isfile(filename)) try { this->read_file(); } catch (ReadException const& read_exception) { return ; } this->init(); this->print_header(); return ; } // BugReportReplay::BugReportReplay(string const& filename) /** ** ** Destructor ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.7 ** **/ BugReportReplay::~BugReportReplay() { ::ui->bug_report_replay_close(); delete this->game_summary_; delete this->current_trick_; delete this->poverty_cards_shifted_; delete this->poverty_cards_returned_; if (this == ::bug_report_replay) { static_cast(::ui)->remove(::bug_report_replay); ::bug_report_replay = NULL; } return ; } // BugReportReplay::~BugReportReplay() /** ** initializes the bug report ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void BugReportReplay::init() { ::bug_report_replay = this; ::setting.set(Setting::SAVE_PARTY_CHANGES, false); static_cast(::ui)->append(this); if (::game_status == GAMESTATUS::PARTY_NEW) { this->party_open(::party); this->party_get_settings(); } #ifdef POSTPHONED // is called in 'party_open' so that this party is set // (-> UI_GTKMM::BugReportReplay::update_info) ::ui->bug_report_replay_open(*this); #endif return ; } // void BugReportReplay::init() /** ** write the data of this bug report into 'ostr' ** ** @param ostr output stream ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.1 **/ void BugReportReplay::write(ostream& ostr) const { ostr << "file: " << this->filename() << '\n'; if (this->version_) ostr << "version: " << this->version() << '\n'; else ostr << "version: -\n"; ostr << "compiled: " << this->compiled() << '\n'; ostr << "system: " << this->system() << '\n'; ostr << "time: " << this->time() << '\n'; ostr << "language: " << this->language() << '\n'; ostr << "trickno: " << this->trickno() << '\n'; ostr << '\n'; ostr << "message\n" << "{\n" << this->message() << "}\n"; ostr << '\n'; ostr << "seed: " << this->seed() << '\n'; ostr << "startplayer_no: " << this->startplayer_no() << '\n'; ostr << "game_type: " << this->game_type() << '\n'; ostr << "marriage_selector: " << this->marriage_selector() << '\n'; ostr << "soloplayer_no: " << this->soloplayer_no() << '\n'; ostr << '\n'; ostr << "rule\n" << "{\n" << this->rule() << "}\n"; ostr << '\n'; ostr << "players\n" << "{\n"; for (vector< ::Player*>::const_iterator p = this->players().begin(); p != this->players().end(); ++p) ostr << "{\n" << **p << "}\n"; ostr << "}"; ostr << '\n'; if (this->poverty_cards_shifted_) { ostr << "poverty cards shifted\n" << "{\n"; for (vector::const_iterator c = this->poverty_cards_shifted()->begin(); c != this->poverty_cards_shifted()->end(); ++c) ostr << *c << '\n'; ostr << "}\n"; } else { ostr << "poverty cards shifted: -\n"; } if (this->poverty_cards_returned_) { ostr << "poverty cards returned\n" << "{\n"; for (vector::const_iterator c = this->poverty_cards_shifted()->begin(); c != this->poverty_cards_shifted()->end(); ++c) ostr << *c << '\n'; ostr << "}\n"; } else { ostr << "poverty cards returned: -\n"; } ostr << '\n'; ostr << "hands\n" << "{\n"; for (vector< ::Hand>::const_iterator h = this->hands().begin(); h != this->hands().end(); ++h) ostr << "{\n" << *h << "}\n"; ostr << "}\n"; ostr << '\n'; ostr << "swines: "; if (this->swines_player_no() == UINT_MAX) ostr << '-'; else ostr << this->swines_player_no(); ostr << '\n'; ostr << "hyperswines: "; if (this->hyperswines_player_no() == UINT_MAX) ostr << '-'; else ostr << this->hyperswines_player_no(); ostr << '\n'; ostr << '\n'; ostr << "game actions\n" << "{\n"; for (vector::const_iterator a = this->actions().begin(); a != this->actions().end(); ++a) ostr << **a << '\n'; ostr << "}\n"; ostr << '\n'; ostr << "full tricks\n" << "{\n"; for (vector< ::Trick>::const_iterator t = this->full_tricks().begin(); t != this->full_tricks().end(); ++t) ostr << *t << '\n'; ostr << "}\n"; ostr << '\n'; ostr << "game summary\n" << "{\n"; if (this->game_summary_) ostr << this->game_summary(); ostr << "}\n"; ostr << '\n'; ostr << "current hands\n" << "{\n"; for (vector< ::Hand>::const_iterator h = this->current_hands().begin(); h != this->current_hands().end(); ++h) ostr << "{\n" << *h << "}\n"; ostr << "}\n"; ostr << '\n'; if (this->current_trick_) ostr << "current trick\n" << this->current_trick(); else ostr << "current trick: -\n"; ostr << '\n'; ostr << "human actions\n" << "{\n"; for (vector::const_iterator a = this->human_actions().begin(); a != this->human_actions().end(); ++a) ostr << **a << '\n'; ostr << "}\n"; return ; } // void BugReportReplay::write(ostream& ostr) const /** ** test, whether the line is the expected ** ** @param read_line line that was read ** @param expected_line line that is read ** ** @return whether the line is the expected ** ** @author Diether Knof ** ** @version 0.7.3 **/ bool BugReportReplay::expect_line(string const& read_line, string const& expected_line) { if (read_line != expected_line) { cerr << "BugReportReplay:\n" << " expected line '" << expected_line << "', " << "got: '" << read_line << "'" << "\n"; this->mismatch(); return false; } // if (read_line != expected_line) return true; } // bool BugReportReplay::expect_line(string read_line, string expected_line) /** ** test, whether the keyword is the expected ** ** @param read_keyword keyword that was read ** @param expected_keyword keyword that is read ** ** @return whether the keyword is the expected ** ** @author Diether Knof ** ** @version 0.7.3 **/ bool BugReportReplay::expect_keyword(string const& read_keyword, string const& expected_keyword) { if (read_keyword != expected_keyword) { cerr << "BugReportReplay:\n" << " expected keyword '" << expected_keyword << "', " << "got: '" << read_keyword << "'" << "\n"; this->mismatch(); return false; } // if (read_keyword != expected_keyword) return true; } // bool BugReportReplay::expect_keyword(string read_keyword, string expected_line) /** ** reads the whole file ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 ** ** @todo poverty ** @todo game summary **/ void BugReportReplay::read_file() { ifstream istr(this->filename().c_str()); string line; // the line read string keyword; // the keyword read string value; // the value read #define GETLINE \ do { \ if (istr.eof()) { \ throw EOF; \ } \ std::getline(istr, line); \ DK::Utils::String::remove_blanks(line); \ if (*(line.end() - 1) == '\r') \ line.erase(line.end() - 1); \ } while (line.empty() && istr.good()) ; \ keyword = get_keyword(line); \ value = get_value(line); \ // reads the variable from 'value' #define READ_FROM_VALUE(var) \ { \ value = get_value(line); \ istringstream istr(value); \ istr >> var; \ } // expect the keyword // if it is not valid, return from the function #define EXPECT_LINE(expected_line) \ if (true) { \ if (!this->expect_line(line, expected_line)) \ throw ReadException("expected line '" + string(line) + "'") ; \ } else #if 0 ; #endif #define EXPECT_KEYWORD(expected_keyword) \ if (true) { \ if (!this->expect_keyword(keyword, expected_keyword)) \ throw ReadException("expected keyword '" + string(keyword) + "'") ; \ } else #if 0 ; #endif try { { // read the first lines (including message) // the first line GETLINE; if (line != "# FreeDoko Bugreport") { cerr << "BugReportReplay::read_first_lines()\n" << " The file is no bug report!" << endl; this->mismatch(); return ; } // if (line != "# FreeDoko Bugreport") // read and print the next informations // Example: // Version: 0.6.7b (2004-12-19) // Compiled: Dec 19 2004, 19:36:18 // System: Windows // Time: Fri Feb 25 22:55:22 2005 // // Language: de // Trick: 9 do { // while(!line.empty()); GETLINE; if (line == "") { } else if (line == "/---------\\") { break; #ifndef OUTDATED // old format: pre 0.7.3 } else if ( (keyword == "version") || (keyword == "Version") ) { #else } else if (keyword == "version") { #endif this->version_ = DK::Utils::Version::new_(value); if (!this->version_) { cerr << "BugReportReplay: " << "could not read version: '" << value << "'\n" << "ignoring it." << endl; } if ( this->version_ && (this->version() <= DK::Utils::Version(0, 6, 6) ) ) { cerr << "BugReportReplay: Too old version " << this->version() << " < 0.6.6" << endl; #ifndef DKNOF exit(0); #endif } #ifndef OUTDATED // new format since 0.7.3 if ( this->version_ && (this->version() <= DK::Utils::Version(0, 7, 3, DK::Utils::Date(2006, 7, 23)) ) ) { cout << "read old file" << endl; return this->read_file_pre_073(); } #endif } else if (keyword == "compiled") { this->compiled_ = value; } else if (keyword == "system") { this->system_ = value; } else if (keyword == "time") { this->time_ = value; } else if (keyword == "language") { this->language_ = value; } else if (keyword == "trick") { READ_FROM_VALUE(this->trickno_); } else { // if !(keyword == ...) cerr << "BugReportReplay: " << "unknown keyword '" << keyword << "'\n" << "ignoring it." << endl; } // if !(keyword == ...) } while(line != "/---------\\"); { // read the message // the header exists of three lines -- ignore them GETLINE; GETLINE; this->message_.clear(); do { // while (line != "/---------\\") GETLINE; if (line != "/--------\\") this->message_ += " " + line + "\n"; if (istr.fail()) { cerr << "BugReportReplay::read_first_lines()\n" << " (unexpected) Error reading the first lines.\n" << "Aborting." << endl; exit(1); } // if (istr.fail()) } while(line != "/--------\\") ; // the foot exists of three lines -- ignore them (one is already ignored) GETLINE; GETLINE; } // read the message GETLINE; } // read the first lines if (line == "finished") { throw EOF; } // if (line == "finished") { // seed, startplayer, rules EXPECT_KEYWORD("seed"); READ_FROM_VALUE(this->seed_); GETLINE; EXPECT_KEYWORD("startplayer"); READ_FROM_VALUE(this->startplayer_no_); GETLINE; EXPECT_LINE("rules"); this->rule_.read(istr); } // seed, startplayer, rules { // players GETLINE; EXPECT_LINE("players"); // a '{' GETLINE; EXPECT_LINE("{"); while (istr.peek() != '}') { this->players_.push_back(Player::new_(this->players_.size(), istr)); if (!this->players_.back()) { cerr << "BugReportReplay::read_file()\n" << " could not load player.\n" << "Aborting." << endl; exit(EXIT_FAILURE); } // if (!this->players.back()) while ((istr.peek() == '\n') || (istr.peek() == '\r')) istr.get(); } // while (istr.peek() != '}') GETLINE; EXPECT_LINE("}"); } // players { // gametype GETLINE; EXPECT_KEYWORD("gametype"); DK::Utils::String::word_first_remove_with_blanks(line); for (int t = GAMETYPE::FIRST; t <= GAMETYPE::LAST; ++t) if (::name(static_cast(t)) == line) { this->game_type_ = static_cast(t); break; } { // special cases if (GAMETYPE::is_solo(this->game_type())) { GETLINE; EXPECT_KEYWORD("soloplayer"); DK::Utils::String::word_first_remove_with_blanks(line); DK::Utils::String::word_first_remove_with_blanks(line); this->soloplayer_no_ = static_cast(atoi(line.c_str())); } if (this->game_type() == GAMETYPE::MARRIAGE) { // read the selector and the bride GETLINE; EXPECT_KEYWORD("selector"); DK::Utils::String::word_first_remove_with_blanks(line); this->marriage_selector_ = MARRIAGE_SELECTOR::from_name(line); GETLINE; EXPECT_KEYWORD("bride"); DK::Utils::String::word_first_remove_with_blanks(line); DK::Utils::String::word_first_remove_with_blanks(line); this->soloplayer_no_ = static_cast(atoi(line.c_str())); } // if (this->game_type() == GAMETYPE::MARRIAGE) if (this->game_type() == GAMETYPE::POVERTY) { // ToDo } // if (this->game_type == GAMETYPE::POVERTY) } // special cases } // gametype { // hands GETLINE; EXPECT_LINE("hands"); GETLINE; EXPECT_LINE("{"); for (vector::const_iterator player = this->players_.begin(); player != this->players_.end(); player++) { // the player number GETLINE; this->hands_.push_back(Hand(istr)); } // for (player \in this->game().player) // a '}' GETLINE; EXPECT_LINE("}"); } // hands { // actions, tricks GETLINE; EXPECT_LINE("gameplay actions"); GETLINE; EXPECT_LINE("{"); do { // while (line != "}") /* Format for a trick: * * Trick: 0 * Played: Player 0: spade ace * Played: Player 1: spade nine * Played: Player 2: spade ace * Played: Player 3: spade nine * * Trick full: 1 * Startplayer: 0 * 0: spade ace * 1: spade nine * 2: spade ace * 3: spade nine * Winner: 0 */ GETLINE; if (line == "}") { break; } else if (line[0] == '#') { continue; } else if (line == "stop") { this->auto_action_end_ = this->actions().size(); continue; } else if (line == "check") { this->check_action_ = this->actions().size() + 1; this->auto_action_end_ = this->actions().size(); continue; } GameplayAction const* action = GameplayAction::new_(line, istr); if (!action) { cerr << "BugReportReplay:\n" << " Unknown action '" << line << "'\n" << " Ignoring it." << endl; #ifdef DKNOF SEGFAULT; #endif continue; } #ifndef WORKAROUND if (action->type == GameplayAction::TRICK_FULL) { // Replace 'TrickFull' with 'TrickClosed', // since the trick in 'TrickFull' needs a corresponding game. delete action; action = new GameplayAction::TrickClosed; } // if (action->type == GameplayAction::TRICK_FULL) #endif this->actions_.push_back(action); #ifndef POSTPHONED if (action->type == GameplayAction::TRICK_FULL) this->full_tricks_.push_back(static_cast(action)->trick); #endif { // special behaviour in a poverty: save the shifted cards if (action->type == GameplayAction::POVERTY_SHIFT) { this->poverty_cards_shifted_ = new vector(static_cast(action)->cards); } else if (action->type == GameplayAction::POVERTY_RETURNED) { this->poverty_cards_returned_ = new vector(static_cast(action)->cards); } // if (action->type == GameplayAction::POVERTY_SHIFT) } // special behaviour in a poverty: save the shifted cards } while (line != "}"); } // actions, tricks { // game summary GETLINE; if (line == "game summary") { EXPECT_LINE("game summary"); this->game_summary_ = new GameSummary(this->rule(), istr); GETLINE; if (line == "special points") { // Just skip them -- they are contained in the game summary EXPECT_LINE("special points"); GETLINE; EXPECT_LINE("{"); do { GETLINE; } while (line != "}"); GETLINE; } // if (line == "special points") } // if (line == "game summary") } // game summary { // current hands EXPECT_LINE("current hands"); GETLINE; EXPECT_LINE("{"); for (vector::const_iterator player = this->players_.begin(); player != this->players_.end(); player++) { GETLINE; this->current_hands_.push_back(Hand(istr)); } // for (player \in this->game().player) // a '}' GETLINE; EXPECT_LINE("}"); GETLINE; } // current hands { // current trick #ifndef OUTDATED if ( (line == "current trick") || (line == "current trick:") ) #else if (line == "current trick") #endif { this->current_trick_ = new Trick(istr); GETLINE; } } // current trick { // human actions EXPECT_LINE("human actions"); GETLINE; EXPECT_LINE("{"); do { // while (line != "}") GETLINE; if (line == "}") break; GameplayAction const* const action = GameplayAction::new_(line, istr); if (!action) { cerr << "BugReportReplay:\n" << " Unknown human action '" << line << "'\n" << " Ignoring it." << endl; continue; } this->human_actions_.push_back(action); } while (line != "}"); if (this->human_actions_.empty()) this->human_finished_ = true; } // human actions } catch (int const status) { if (status == EOF) { // finished // return ; } else { throw; } } catch (ReadException const& read_exception) { cerr << "BugReportReplay::read_file()\n" << " read exception: " << read_exception.message() << endl; throw; } catch (...) { cerr << "BugReportReplay::read_file()\n" << " unknown exception" << endl; throw; } #undef GETLINE #undef READ_FROM_VALUE #undef EXPECT_LINE #undef EXPECT_KEYWORD this->loaded_ = true; return ; } // void BugReportReplay::read_file() /** ** reads the whole file ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 ** ** @todo poverty ** @todo game summary **/ void BugReportReplay::read_file_pre_073() { ifstream istr(this->filename().c_str()); string line; // the line read string keyword; // the keyword read string value; // the value read #define GETLINE \ do { \ if (istr.eof()) { \ throw EOF; \ } \ std::getline(istr, line); \ if (*(line.end() - 1) == '\r') \ line.erase(line.end() - 1); \ } while (line.empty() && istr.good()) ; \ if (line == "Finished") \ throw string("finished"); \ keyword = get_keyword(line); \ value = get_value(line); // reads the variable from 'value' #define READ_FROM_VALUE(var) \ { \ value = get_value(line); \ istringstream istr(value); \ istr >> var; \ } // expect the keyword // if it is not valid, return from the function #define EXPECT_LINE(expected_line) \ if (true) { \ if (!this->expect_line(line, expected_line)) \ throw ReadException("expected line '" + string(line) + "'") ; \ } else #if 0 ; #endif #define EXPECT_KEYWORD(expected_keyword) \ if (true) { \ if (!this->expect_keyword(keyword, expected_keyword)) \ throw ReadException("expected keyword '" + string(keyword) + "'") ; \ } else #if 0 ; #endif try { try { // catch "finished" { // read the first lines (including message) // the first line GETLINE; if (line != "# FreeDoko Bugreport") { cerr << "BugReportReplay::read_first_lines()\n" << " The file is no bug report!" << endl; this->mismatch(); return ; } // if (line != "# FreeDoko Bugreport") // read and print the next informations // Example: // Version: 0.6.7b (2004-12-19) // Compiled: Dec 19 2004, 19:36:18 // System: Windows // Time: Fri Feb 25 22:55:22 2005 // // Language: de // Trick: 9 do { // while(!line.empty()); GETLINE; if (line == "") { } else if (line == "/--------\\") { break; } else if (keyword == "Version") { this->version_ = DK::Utils::Version::new_(value); if (!this->version_) { cerr << "BugReportReplay: " << "could not read version: '" << value << "'\n" << "ignoring it." << endl; } if (this->version_ && (this->version() <= DK::Utils::Version(0, 6, 6) ) ) { cerr << "BugReportReplay: Too old version " << this->version() << " < 0.6.6" << endl; #ifndef DKNOF exit(0); #endif } #ifndef OUTDATED // new format sinc 0.7.3 ::OLD_FORMAT = (this->version_ && (this->version() < DK::Utils::Version(0, 7, 3, DK::Utils::Date(2006, 8, 1)) ) ); #endif } else if (keyword == "Compiled") { this->compiled_ = value; } else if (keyword == "System") { this->system_ = value; } else if (keyword == "Time") { this->time_ = value; } else if (keyword == "Language") { this->language_ = value; } else if (keyword == "Trick") { READ_FROM_VALUE(this->trickno_); } else if (line == "With backup") { } else { // if !(keyword == ...) cerr << "BugReportReplay: " << "unknown keyword '" << keyword << "'\n" << "ignoring it." << endl; } // if !(keyword == ...) } while(line != "/--------\\"); { // read the message // the header exists of three lines -- ignore them GETLINE; GETLINE; this->message_.clear(); do { // while (line != "/--------\\") GETLINE; if (line != "/-------\\") this->message_ += " " + line + "\n"; if (istr.fail()) { cerr << "BugReportReplay::read_first_lines()\n" << " (unexpected) Error reading the first lines.\n" << "Aborting." << endl; exit(1); } // if (istr.fail()) } while(line != "/-------\\") ; // the foot exists of three lines -- ignore them (one is already ignored) GETLINE; GETLINE; } // read the message GETLINE; } // read the first lines { // seed, startplayer, rules EXPECT_KEYWORD("Seed"); READ_FROM_VALUE(this->seed_); GETLINE; EXPECT_KEYWORD("Startplayer"); READ_FROM_VALUE(this->startplayer_no_); GETLINE; EXPECT_LINE("Rules"); this->rule_.read(istr); } // seed, startplayer, rules { // players GETLINE; EXPECT_LINE("Players"); // a '{' GETLINE; EXPECT_LINE("{"); while (istr.peek() != '}') { this->players_.push_back(Player::new_(this->players_.size(), istr)); if (!this->players_.back()) { cerr << "BugReportReplay::read_file()\n" << " could not load player.\n" << "Aborting." << endl; exit(EXIT_FAILURE); } // if (!this->players.back()) while ((istr.peek() == '\n') || (istr.peek() == '\r')) istr.get(); } // while (istr.peek() != '}') GETLINE; EXPECT_LINE("}"); } // players GETLINE; { // poverty if (keyword == "Poverty") { // remove 'Poverty:' DK::Utils::String::word_first_remove_with_blanks(line); // next comes 'player' followed the player number, READ_FROM_VALUE(this->soloplayer_no_); this->poverty_cards_shifted_ = new vector(HandCards(istr)); this->actions_.push_back(new GameplayAction::PovertyShift(this->soloplayer_no(), *this->poverty_cards_shifted())); GETLINE; unsigned accepted_player_no = UINT_MAX; // remove 'Poverty:' DK::Utils::String::word_first_remove_with_blanks(line); // next comes 'player' followed by the player number, READ_FROM_VALUE(accepted_player_no); this->poverty_cards_returned_ = new vector(HandCards(istr)); this->actions_.push_back(new GameplayAction::PovertyReturned(accepted_player_no, *this->poverty_cards_returned())); GETLINE; } // if (keyword == "Poverty") } // poverty { // swines if (keyword == "Swines") { READ_FROM_VALUE(this->swines_player_no_); GETLINE; if (keyword == "Hyperswines") { READ_FROM_VALUE(this->hyperswines_player_no_); GETLINE; } // if (keyword == "Hyperswines) } // if (keyword == "Swines) } // swines { // gametype EXPECT_KEYWORD("Gametype"); DK::Utils::String::word_first_remove_with_blanks(line); for (int t = GAMETYPE::FIRST; t <= GAMETYPE::LAST; ++t) if (::name(static_cast(t)) == line) { this->game_type_ = static_cast(t); break; } { // special cases if (GAMETYPE::is_solo(this->game_type())) { GETLINE; EXPECT_KEYWORD("Soloplayer"); DK::Utils::String::word_first_remove_with_blanks(line); DK::Utils::String::word_first_remove_with_blanks(line); this->soloplayer_no_ = static_cast(atoi(line.c_str())); } if (this->game_type() == GAMETYPE::MARRIAGE) { // read the selector and the bride GETLINE; EXPECT_KEYWORD("Selector"); DK::Utils::String::word_first_remove_with_blanks(line); this->marriage_selector_ = MARRIAGE_SELECTOR::from_name(line); GETLINE; EXPECT_KEYWORD("Bride"); DK::Utils::String::word_first_remove_with_blanks(line); DK::Utils::String::word_first_remove_with_blanks(line); this->soloplayer_no_ = static_cast(atoi(line.c_str())); } // if (this->game_type() == GAMETYPE::MARRIAGE) if (this->game_type() == GAMETYPE::POVERTY) { // ToDo } // if (this->game_type == GAMETYPE::POVERTY) } // special cases } // gametype { // hands GETLINE; EXPECT_LINE("Hands:"); GETLINE; EXPECT_LINE("{"); for (vector::const_iterator player = this->players_.begin(); player != this->players_.end(); player++) { GETLINE; #ifdef OUTDATED // the keyword is since 0.7.1 the whole line, not just the first word EXPECT_KEYWORD("Player"); #endif this->hands_.push_back(Hand(istr)); } // for (player \in this->game().player) // a '}' GETLINE; EXPECT_LINE("}"); } // hands { // actions, tricks do { // while (line != "}") /* Format for a trick: * * Trick: 0 * Played: Player 0: spade ace * Played: Player 1: spade nine * Played: Player 2: spade ace * Played: Player 3: spade nine * * Trick full: 1 * Startplayer: 0 * 0: spade ace * 1: spade nine * 2: spade ace * 3: spade nine * Winner: 0 */ GETLINE; if (line == "}") break; if (keyword == "Trick") continue; if (line == "stop") { this->auto_action_end_ = this->actions().size(); continue; } if (keyword == "Game summary") { // skip all until 'finished' do { GETLINE; // note: in reality when 'Finished' is reached by 'GETLINE' // an exception is thrown } while (keyword != "Finished"); break; } // if() GameplayAction const* action = GameplayAction::new_(line, istr); if (!action) { cerr << "BugReportReplay:\n" << " Unknown action '" << line << "'\n" << " Ignoring it." << endl; continue; } #ifndef WORKAROUND if (action->type == GameplayAction::TRICK_FULL) { // Replace 'TrickFull' with 'TrickClosed', // since the trick in 'TrickFull' needs a corresponding game. delete action; action = new GameplayAction::TrickClosed; } // if (action->type == GameplayAction::TRICK_FULL) #endif this->actions_.push_back(action); #ifndef POSTPHONED if (action->type == GameplayAction::TRICK_FULL) this->full_tricks_.push_back(static_cast(action)->trick); #endif } while (line != "}"); } // actions, tricks { // game summary #ifdef POSTPHONED GETLINE; #endif EXPECT_LINE("Game summary"); this->game_summary_ = new GameSummary(this->rule(), istr); } // game summary { // special points // Just skip them -- they are contained in the game summary GETLINE; EXPECT_LINE("Special points"); GETLINE; EXPECT_LINE("{"); do { GETLINE; } while (line != "}"); } // special points // this must be the 'finished' line GETLINE; EXPECT_LINE("Finished"); } catch (string const& text) { if (text != "finished") throw; } // try { // current hands GETLINE; EXPECT_LINE("Current hands:"); GETLINE; EXPECT_LINE("{"); for (vector::const_iterator player = this->players_.begin(); player != this->players_.end(); player++) { GETLINE; #ifdef OUTDATED // the keyword is since 0.7.1 the whole line, not just the first word EXPECT_KEYWORD("Player"); #endif this->current_hands_.push_back(Hand(istr)); } // for (player \in this->game().player) // a '}' GETLINE; EXPECT_LINE("}"); } // current hands { // current trick GETLINE; EXPECT_LINE("Current trick:"); this->current_trick_ = new Trick(istr); } // current trick { // human actions GETLINE; EXPECT_LINE("Part human"); GETLINE; EXPECT_LINE("{"); do { // while (line != "}") GETLINE; if (line == "}") break; // old format string const adjusted_line = (string(line.begin(), std::find(line.begin(), line.end(), ':')) + ": Player 0" + string(std::find(line.begin(), line.end(), ':'), line.end()) ); GameplayAction const* action = GameplayAction::new_(adjusted_line, istr); #ifdef WORKAROUND if (!action) action = GameplayAction::new_(line, istr); #endif if (!action) { if (string(line, 0, strlen("Reservation:")) == "Reservation:") continue; if (string(line, 0, strlen("poverty:")) == "poverty:") { // remove 'Poverty:' DK::Utils::String::word_first_remove_with_blanks(line); if (string(line, 0, strlen("shifting")) == "shifting") { HandCards shifted_cards(istr); action = new GameplayAction::PovertyShift(0, shifted_cards); } } if (string(line, 0, strlen("Poverty:")) == "Poverty:") { // remove 'Poverty:' DK::Utils::String::word_first_remove_with_blanks(line); if (string(line, 0, strlen("denied")) == "denied") { action = new GameplayAction::PovertyDenied(0); } else if (string(line, 0, strlen("accepted:")) == "accepted:") { HandCards shifted_cards(istr); action = new GameplayAction::PovertyReturned(0, shifted_cards); } // if (accepted) } // if (keyword == "Poverty") } // if (!action) if (!action) { cerr << "BugReportReplay:\n" << " Unknown human action '" << line << "'\n" << " Ignoring it." << endl; SEGFAULT; continue; } this->human_actions_.push_back(action); } while (line != "}"); } // human actions } catch (int const status) { if (status == EOF) { // finished // return ; } else { throw; } } catch (ReadException const& read_exception) { cerr << "BugReportReplay::read_file()\n" << " read exception: " << read_exception.message() << endl; throw; } catch (...) { cerr << "BugReportReplay::read_file()\n" << " unknown exception" << endl; throw; } #undef GETLINE #undef READ_FROM_VALUE #undef EXPECT_LINE #undef EXPECT_KEYWORD this->loaded_ = true; return ; } // void BugReportReplay::read_file_pre_073() /** ** -> result ** ** @param - ** ** @return the current gameplay action ** ** @author Diether Knof ** ** @version 0.7.3 **/ GameplayAction const& BugReportReplay::current_action() const { DEBUG_ASSERTION((this->current_action_no() < this->actions().size()), "BugReportReplay::current_action()\n" " no further gameplay action"); return *this->actions()[this->current_action_no()]; } // GameplayAction BugReportReplay::current_action() const /** ** the current action has been processed ** ** @param discrepancy discrepancy of the processed and the saved action ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void BugReportReplay::action_processed(GameplayAction::Discrepancy const discrepancy) { if (this->check_action() == this->current_action_no()) { cout << "action check: " << this->current_action() << endl; cout << " discrepancy: " << discrepancy << endl; exit(discrepancy); } // save the discepancy and go to the next action // test for a human action if ( !this->human_finished() && (this->current_action() == this->current_human_action())) { this->human_actions_discrepancies_.push_back(discrepancy); this->current_human_action_no_ += 1; if (this->current_human_action_no() == this->human_actions().size()) this->human_finished_ = true; } // if (human action) this->actions_discrepancies_.push_back(discrepancy); this->current_action_no_ += 1; if (this->current_action_no() == this->actions().size()) this->end_reached(); // automatic actions for the human player if ( !this->finished() && (::game_status >= GAMESTATUS::GAME_PLAY)) this->handle_current_human_action(); return ; } // void BugReportReplay::action_processed(GameplayAction::Discrepancy discrepancy) /** ** -> result ** ** @param action_no number of the action ** ** @return the discrepancy for the given action ** ** @author Diether Knof ** ** @version 0.7.3 **/ GameplayAction::Discrepancy BugReportReplay::discrepancy(unsigned const action_no) const { if (action_no >= this->actions_discrepancies().size()) return GameplayAction::FUTURE; return this->actions_discrepancies()[action_no]; } // Discrepancy BugReportReplay::discrepancy(unsigned action_no) const /** ** -> result ** ** @param - ** ** @return the current human action ** ** @author Diether Knof ** ** @version 0.7.3 **/ GameplayAction const& BugReportReplay::current_human_action() const { DEBUG_ASSERTION((this->current_human_action_no() < this->human_actions().size()), "BugReportReplay::current_human_action()\n" " no further human action"); return *this->human_actions()[this->current_human_action_no()]; } // GameplayAction BugReportReplay::current_human_action() const /** ** -> result ** ** @param action_no number of the human action ** ** @return the discrepancy for the given human action ** ** @author Diether Knof ** ** @version 0.7.3 **/ GameplayAction::Discrepancy BugReportReplay::human_discrepancy(unsigned const action_no) const { if (action_no >= this->human_actions_discrepancies().size()) return GameplayAction::FUTURE; return this->human_actions_discrepancies()[action_no]; } // Discrepancy BugReportReplay::human_discrepancy(unsigned action_no) const /** ** handle the current human action ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void BugReportReplay::handle_current_human_action() { if ( this->finished() || this->human_finished() || (this->current_action() != this->current_human_action()) ) return ; // print the coming action this->print_current_human_action(); // partly execute the action automatically switch (this->current_human_action().type) { case GameplayAction::ANNOUNCEMENT: { GameplayAction::Announcement const& announcement_action = static_cast(this->current_human_action()); cout << "BugReportReplay:\n" << " making announcement '" << announcement_action.announcement << "'" << " for player " << announcement_action.player << endl; // make the announcement in place of the human and tell it the user if (!ANNOUNCEMENT::is_reply(announcement_action.announcement)) ::party.game().make_announcement(announcement_action.announcement, ::party.game().player(announcement_action.player)); } break; case GameplayAction::SWINES: { GameplayAction::Swines const& swines_action = static_cast(this->current_human_action()); cout << "BugReportReplay:\n" << " announce 'swines' for human player " << swines_action.player << endl; if (!::party.game().swines_announce(::party.game().player(swines_action.player))) cerr << "Error announcing 'swines' for human player" << swines_action.player << endl; } break; case GameplayAction::HYPERSWINES: { GameplayAction::Hyperswines const& hyperswines_action = static_cast(this->current_human_action()); cout << "BugReportReplay:\n" << " announce 'hyperswines' for human player " << hyperswines_action.player << endl; if (!::party.game().hyperswines_announce(::party.game().player(hyperswines_action.player))) cerr << "Error announcing 'hyperswines' for human player" << hyperswines_action.player << endl; } break; default: break; } // switch (this->current_human_action().type) } // void BugReportReplay::handle_current_human_action() /** ** print the next human action ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void BugReportReplay::print_current_human_action() const { switch (this->current_human_action().type) { case GameplayAction::CARD_PLAYED: { GameplayAction::CardPlayed const& card_played_action = static_cast(this->current_human_action()); cout << "BugReportReplay: Human\n" << " play " << card_played_action.card << endl; } break; case GameplayAction::RESERVATION: { GameplayAction::Reservation const& reservation_action = static_cast(this->current_human_action()); cout << "BugReportReplay: Human\n" << " gametype " << reservation_action.reservation.game_type << '\n'; if (reservation_action.reservation.game_type == GAMETYPE::MARRIAGE) cout << " marriage selector " << reservation_action.reservation.marriage_selector <<'\n'; if (reservation_action.reservation.swines) cout << " swines\n"; if (reservation_action.reservation.hyperswines) cout << " hyperswines\n"; } break; case GameplayAction::POVERTY_SHIFT: { GameplayAction::PovertyShift const& poverty_shift_action = static_cast(this->current_human_action()); cout << "BugReportReplay: Human\n" << " poverty: shift\n"; for (vector::const_iterator c = poverty_shift_action.cards.begin(); c != poverty_shift_action.cards.end(); ++c) cout << " " << *c << '\n'; } break; case GameplayAction::POVERTY_RETURNED: { GameplayAction::PovertyReturned const& poverty_returned_action = static_cast(this->current_human_action()); cout << "BugReportReplay: Human\n" << " poverty: return\n"; for (vector::const_iterator c = poverty_returned_action.cards.begin(); c != poverty_returned_action.cards.end(); ++c) cout << " " << *c << '\n'; } break; case GameplayAction::POVERTY_ACCEPTED: { cout << "BugReportReplay: Human\n" << " poverty: accept\n"; } break; case GameplayAction::POVERTY_DENIED: { cout << "BugReportReplay: Human\n" << " poverty: deny\n"; } break; case GameplayAction::POVERTY_DENIED_BY_ALL: case GameplayAction::TRICK_FULL: case GameplayAction::TRICK_TAKEN: case GameplayAction::MARRIAGE: // nothing to do break; case GameplayAction::ANNOUNCEMENT: case GameplayAction::SWINES: case GameplayAction::HYPERSWINES: // -> action_processed case GameplayAction::GENSCHER: // ToDo break; } // switch (this->current_human_action().type) return ; } // void BugReportReplay::print_current_human_action() const /** ** the bug report is inconsistent: ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void BugReportReplay::mismatch() { cerr << "BugReport mismatch!" << endl; return ; } // void BugReportReplay::mismatch() /** ** ** the bug report is finished. ** remove it from the global ui ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.7 ** **/ void BugReportReplay::end_reached() { this->finished_ = true; this->human_finished_ = true; cout << "BugReport: finished" << endl; cout << "Message:\n" << "{\n" << this->message() << "}" <(::ui)->remove(this); #endif return ; } // void BugReportReplay::end_reached() /** ** prints the header and the differences ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void BugReportReplay::print_header() const { // print the first lines cout << "BugReport\n" << "\n"; if (this->version_) cout << "version: " << this->version() << '\n'; else cout << "version: -\n"; cout << "compiled: " << this->compiled() << '\n' << "system: " << this->system() << '\n' << "time: " << this->time() << '\n' << "language: " << this->language() << '\n' << "seed: " << this->seed() << '\n' << "startplayer: " << this->startplayer_no() << '\n' << "trick: " << this->trickno() << '\n' << '\n' << "message:\n" << "{\n" << this->message() << "}\n"; return ; } // void BugReportReplay::print_header() const /** ** a party is opened ** ** @param party the party that is opened ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.7 **/ void BugReportReplay::party_open(Party const& party) { this->OS::party_open(party); this->finished_ = false; this->current_action_no_ = 0; this->current_human_action_no_ = 0; this->actions_discrepancies_.clear(); this->human_actions_discrepancies_.clear(); this->human_finished_ = true; if (!this->human_actions_.empty()) { for (vector::const_iterator player = this->players_.begin(); player != this->players_.end(); player++) if ((*player)->type() == Player::HUMAN) { this->human_finished_ = false; break; } } // if (!this->human_actions_.empty()) #ifndef OUTDATED ::ui->bug_report_replay_open(*this); #endif return ; } // void BugReportReplay::party_open(Party const& party) /** ** set the settings of the party ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void BugReportReplay::party_get_settings() { ::party.set_seed(this->seed()); ::party.set_startplayer(this->startplayer_no()); { // set the rules and print the rule differences // the hardcoded ruleset (the harddcoded) Rule const rule_hardcoded; // the default ruleset (taken from the private directory) Rule const rule_default = this->party().rule(); // change the rules of the party to that of the bug report ::party.rule() = this->rule(); cout << '\n' << "rule differences:\n" << "{\n"; cout << setw(38) << "" << setw(12) << "bug_report" << setw(12) << "default" << setw(12) << "hardcoded" << "\n"; for (unsigned i = Rule::FIRST; i <= Rule::LAST; i++) { if ( (i >= (Rule::BOOL_FIRST)) && (i <= Rule::BOOL_LAST)) { if ( (this->party().rule()(Rule::TypeBool(i)) != rule_hardcoded(Rule::TypeBool(i))) || (this->party().rule()(Rule::TypeBool(i)) != rule_default(Rule::TypeBool(i)))) { cout << setw(38) << name(Rule::TypeBool(i)) << setw(12) << this->party().rule()(Rule::TypeBool(i)) << setw(12) << rule_default(Rule::TypeBool(i)) << setw(12) << rule_hardcoded(Rule::TypeBool(i)) << "\n"; } // if (rule differs) } else if ( (i >= Rule::UNSIGNED_FIRST) && (i <= Rule::UNSIGNED_LAST)) { if ( (this->party().rule()(Rule::TypeUnsigned(i)) != rule_hardcoded(Rule::TypeUnsigned(i))) || (this->party().rule()(Rule::TypeUnsigned(i)) != rule_default(Rule::TypeUnsigned(i)))) { cout << setw(38) << name(Rule::TypeUnsigned(i)) << setw(12) << this->party().rule()(Rule::TypeUnsigned(i)) << setw(12) << rule_default(Rule::TypeUnsigned(i)) << setw(12) << rule_hardcoded(Rule::TypeUnsigned(i)) << "\n"; } // if (rule differs) } else if ( (i >= Rule::UNSIGNED_EXTRA_FIRST) && (i <= Rule::UNSIGNED_EXTRA_LAST)) { if ( (this->party().rule()(Rule::TypeUnsignedExtra(i)) != rule_hardcoded(Rule::TypeUnsignedExtra(i))) || (this->party().rule()(Rule::TypeUnsignedExtra(i)) != rule_default(Rule::TypeUnsignedExtra(i)))) { cout << setw(38) << name(Rule::TypeUnsignedExtra(i)) << setw(12) << this->party().rule()(Rule::TypeUnsignedExtra(i)) << setw(12) << rule_default(Rule::TypeUnsignedExtra(i)) << setw(12) << rule_hardcoded(Rule::TypeUnsignedExtra(i)) << "\n"; } // if (rule differs) } else if (i == Rule::COUNTING) { if ( (this->party().rule()(Rule::COUNTING) != rule_hardcoded(Rule::COUNTING) ) || (this->party().rule()(Rule::COUNTING) != rule_default(Rule::COUNTING) ) ){ cout << setw(38) << name(Rule::COUNTING) << setw(12) << this->party().rule()(Rule::COUNTING) << setw(12) << rule_default(Rule::COUNTING) << setw(12) << rule_hardcoded(Rule::COUNTING) << "\n"; } // if (rule differs) } else { // if (i: type unknown) DEBUG_ASSERTION(false, "BugReportReplay::party_open():\n" " unknown ruletype number " << i); } // if (i: type unknown) } // for (unsigned i = Rule::FIRST; i <= Rule::LAST; i++) cout << "}" << endl; { // set some specific rules list types; types.push_back(Rule::SHOW_ALL_HANDS); types.push_back(Rule::SHOW_KNOWN_TEAMS_IN_GAME); types.push_back(Rule::SHOW_SOLOPLAYER_IN_GAME); types.push_back(Rule::SHOW_TRICKS_IN_TRICKPILES); types.push_back(Rule::SHOW_TRICKPILES_POINTS); types.push_back(Rule::SHOW_IF_VALID); types.push_back(Rule::CARD_HINT); types.push_back(Rule::TAKE_BACK_TRICKS); for (list::const_iterator t = types.begin(); t != types.end(); ++t) { if (!this->party().rule()(*t)) { cout << "setting rule '" << ::name(*t) << "' to true." << endl; ::party.rule().set(*t, true); } // if (!this->party().rule()(*t)) } // for t \in types if (this->party().rule()(Rule::SHOW_TRICKS_NUMBER) < this->party().rule()(Rule::NUMBER_OF_TRICKS_IN_GAME)) { cout << "setting rule '" << ::name(Rule::SHOW_TRICKS_NUMBER) << "' to " << this->party().rule()(Rule::NUMBER_OF_TRICKS_IN_GAME) << "." << endl; ::party.rule().set(Rule::SHOW_TRICKS_NUMBER, this->party().rule()(Rule::NUMBER_OF_TRICKS_IN_GAME)); } // if (!this->party().rule()(Rule::SHOW_TRICKS_NUMBER)) } // set some specific rules } // set the rules and print the rule differences { // set the players for (unsigned n = 0; n < this->players().size(); ++n) ::party.set_player(this->players()[n]->clone(), n); } // set the players // repeat the message cout << '\n' << "Message:\n" << "{\n" << this->message() << "}\n"; return ; } // void BugReportReplay::party_get_settings() /** ** a gameplay action ** ** @param action the action ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void BugReportReplay::gameplay_action(GameplayAction const& action) { if (this->finished()) return ; // do handle all actions (see BugReport::gameplay_action(action) ) switch (action.type) { case GameplayAction::POVERTY_DENIED_BY_ALL: case GameplayAction::TRICK_TAKEN: // do not handle return ; case GameplayAction::RESERVATION: case GameplayAction::POVERTY_SHIFT: case GameplayAction::POVERTY_RETURNED: case GameplayAction::POVERTY_ACCEPTED: case GameplayAction::POVERTY_DENIED: case GameplayAction::CARD_PLAYED: case GameplayAction::TRICK_FULL: case GameplayAction::ANNOUNCEMENT: case GameplayAction::SWINES: case GameplayAction::HYPERSWINES: case GameplayAction::MARRIAGE: case GameplayAction::GENSCHER: // do handle break; } // switch (action.type) // check that this action conforms to the gameplay if (action == this->current_action()) { this->action_processed(GameplayAction::NONE); return ; } #ifndef OUTDATED if (::OLD_FORMAT) { // skip reservations if ( (action.type == GameplayAction::RESERVATION) || (action.type == GameplayAction::MARRIAGE) || (action.type == GameplayAction::GENSCHER) ) { return ; } } // if (::OLD_FORMAT) #endif // the action differs from the one of the bug report cerr << "BugReport different actions:\n" << " game: " << action << '\n' << " bug report: " << this->current_action() << endl; // if the type is equal there is only some other gameplay if (action.type == this->current_action().type) { if (dynamic_cast(&action)) { if (static_cast(action).player != static_cast(this->current_action()).player) { this->action_processed(GameplayAction::PLAYER); } else if ( (action.type == GameplayAction::CARD_PLAYED) && (static_cast(action).card != static_cast(this->current_action()).card) ) { this->action_processed(GameplayAction::CARD); } else { this->action_processed(GameplayAction::OTHER); } } else { this->action_processed(GameplayAction::OTHER); } return ; } // if (action.type == this->current_action().type) // skip announcements switch (this->current_action().type) { case GameplayAction::ANNOUNCEMENT: case GameplayAction::SWINES: case GameplayAction::HYPERSWINES: case GameplayAction::MARRIAGE: case GameplayAction::GENSCHER: if (action.type >= GameplayAction::CARD_PLAYED) { // just skip this->action_processed(GameplayAction::SKIPPED); this->gameplay_action(action); return ; } break; default: break; } // switch (this->current_action().type) switch (action.type) { case GameplayAction::CARD_PLAYED: if (this->current_action().type == GameplayAction::CARD_PLAYED) { this->action_processed(GameplayAction::SKIPPED); break; } // search the next card-played action while (!this->finished()) { this->action_processed(GameplayAction::SKIPPED); if (this->finished()) break ; if (this->current_action().type == GameplayAction::CARD_PLAYED) break ; } // while (!this->finished()) break; case GameplayAction::RESERVATION: case GameplayAction::POVERTY_SHIFT: case GameplayAction::POVERTY_ACCEPTED: case GameplayAction::POVERTY_RETURNED: case GameplayAction::POVERTY_DENIED: case GameplayAction::POVERTY_DENIED_BY_ALL: case GameplayAction::TRICK_FULL: case GameplayAction::TRICK_TAKEN: // ToDo break; case GameplayAction::ANNOUNCEMENT: case GameplayAction::MARRIAGE: case GameplayAction::GENSCHER: case GameplayAction::SWINES: case GameplayAction::HYPERSWINES: // ignore break; } // switch (action.type) return ; } // void BugReportReplay::gameplay_action(GameplayAction action) /** ** ask 'player' whether to accept the poverty ** ** @param player player that is asked ** @param cardno number of shifted cards ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void BugReportReplay::poverty_ask(Player const& player, unsigned const cardno) { this->OS::poverty_ask(player, cardno); this->handle_current_human_action(); return ; } // void BugReportReplay::poverty_ask(Player player, unsigned cardno); /** ** a new trick is opened ** ** @param trick trick that is opened ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void BugReportReplay::trick_open(Trick const& trick) { this->OS::trick_open(trick); // automatic actions for the human player // De facto this is only needed for the first trick so that the player // can announce before any other action. this->handle_current_human_action(); return ; } // void BugReportReplay::trick_open(Trick const& trick) /** ** -> result ** ** @param player player ** ** @return the next card played by the player ** ** @author Diether Knof ** ** @version 0.7.3 **/ HandCard BugReportReplay::next_card(Player const& player) const { if (this->human_finished()) return HandCard(); if (this->current_human_action().type == GameplayAction::GENSCHER) return HandCard(player, Card(Card::DIAMOND, Card::KING)); if (this->current_human_action().type != GameplayAction::CARD_PLAYED) { cerr << "Bug report: human action should be 'card played' but is:\n" << " " << this->current_human_action() << endl; return HandCard(); } // if (this->current_human_action().type != GameplayAction::CARD_PLAYED) return HandCard(player, static_cast(this->current_human_action()).card); } // HandCard BugReportReplay::next_card(Player const& player) const /** ** -> result ** ** @param - ** ** @return whether the current action should be executed automatically ** ** @author Diether Knof ** ** @version 0.7.3 **/ bool BugReportReplay::auto_action() const { if (this->finished()) return false; return (this->current_action_no() < this->auto_action_end()); } // bool BugReportReplay::auto_action() const } // namespace OS_NS