/*********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *
 *   Copyright (C) 2001-2004  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 "../party/party.h"
#include "../party/rule.h"
#include "../game/game.h"
#include "../player/player.h"
#include "../player/aiconfig.h"

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

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

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

extern bool RAND_OLD;

// echo the lines
#ifndef RELEASE
#ifdef DKNOF
//#define ECHO_LINES
#endif // #ifdef DKNOF
#endif // #ifndef RELEASE

// get a line (remove a '\r' at the end of the line (DOS-EOL) )
#ifdef ECHO_LINES
#define GETLINE do { \
  std::getline(*(this->istr), this->line); \
    if (*(this->line.end() - 1) == '\r') \
      this->line.erase(this->line.end() - 1); \
	COUT << "line: " << this->line << endl; \
} while (this->line.empty() && this->istr->good()) ; \
cout << "line: " << this->line << endl; \
  this->test_for_end()
#define GETLINE_HUMAN std::getline(*(this->istr_human), this->line_human); \
if (*(this->line_human.end() - 1) == '\r') \
  this->line_human.erase(this->line_human.end() - 1); \
  cout << "line human: " << this->line_human << endl
#else
#define GETLINE do { \
  if (this->istr->eof()) { \
    cerr << "BugReportReplay:\n" \
      << "  EOF reached." \
      << endl; \
    this->inconsistent(); \
    this->finished(); \
    return ; \
  } \
  std::getline(*(this->istr), this->line);  \
    if (*(this->line.end() - 1) == '\r') \
      this->line.erase(this->line.end() - 1); \
} while (this->line.empty() && this->istr->good()) ; \
this->test_for_end()
#define GETLINE_HUMAN std::getline(*(this->istr_human), this->line_human);  \
if (*(this->line_human.end() - 1) == '\r') \
  this->line_human.erase(this->line_human.end() - 1);
#endif
// expect the keyword
// if it is not valid, return from the function
#define EXPECT_LINE(line) \
if (true) { \
  if (!this->line_expect(line)) \
    DEBUG_RETURNING(VOID, \
		    INFO_OS_BUG_REPORT_REPLAY, \
		    "BugReportReplay:: ()" \
		    " - line inconsistent"); \
} else
#if 0
;
#endif

#define EXPECT_KEYWORD(keyword) \
if (true) { \
  if (!this->keyword_expect(keyword)) \
    DEBUG_RETURNING(VOID, \
		    INFO_OS_BUG_REPORT_REPLAY, \
		    "BugReportReplay:: ()" \
		    " - keyword inconsistent"); \
} else
#if 0
;
#endif

#define EXPECT_VALUE(value) \
if (true) { \
  if (!this->value_expect(value)) \
    DEBUG_RETURNING(VOID, \
		    INFO_OS_BUG_REPORT_REPLAY, \
		    "BugReportReplay:: ()" \
		    " - value inconsistent"); \
} else
#if 0
;
#endif

namespace OS_NS {

  // There can only be one 'bug report replay' instance.
  // If a new is created, the old is removed.
  BugReportReplay* BugReportReplay::bug_report_replay = NULL;

  /**
   **
   ** Constructor
   ** (to be called from a child class)
   **
   ** @param	filename	the file with the bug report
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   **/
  BugReportReplay::BugReportReplay(string const& filename) :
    OS(OS_TYPE::OS_BUG_REPORT),
  filename(filename),
  istr(NULL),
  line(),
  istr_human(NULL),
  line_human(),
  message(),
  seed(UINT_MAX),
  startplayer_no(UINT_MAX),
  game_type(GAMETYPE::NORMAL)
  {
    DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		  "BugReportReplay::BugReportReplay(filename)");

    if (BugReportReplay::bug_report_replay != NULL) {
      static_cast<UI_Wrap*>(::ui)->remove(BugReportReplay::bug_report_replay);
      delete BugReportReplay::bug_report_replay;
      BugReportReplay::bug_report_replay = NULL;
    } // if (BugReportReplay::bug_report_replay != NULL)
    if (DK::Utils::File::isfile(filename)) {
      BugReportReplay::bug_report_replay = this;
      ::setting.set(Setting::SAVE_PARTY_CHANGES, false);
    } else { // if !(DK::Utils::File::isfile(filename))
      cerr << "Selected bug report '" << filename << "' is no file." << endl;
    } // if !(DK::Utils::File::isfile(filename))

    DEBUG_RETURNING(VOID,
		    INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::BugReportReplay(filename)");
  } // BugReportReplay::BugReportReplay(string const& filename)

  /**
   **
   ** Destructor
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   **/
  BugReportReplay::~BugReportReplay()
  {
    DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		  "BugReportReplay::~BugReportReplay()");

    if (this->istr) {
      static_cast<ifstream*>(this->istr)->close();
      delete this->istr;
      static_cast<ifstream*>(this->istr_human)->close();
      delete this->istr_human;
    } // if (this->istr)

    if (BugReportReplay::bug_report_replay != NULL)
      static_cast<UI_Wrap*>(::ui)->remove(bug_report_replay);

    DEBUG_RETURNING(VOID,
		    INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::~BugReportReplay()");
  } // BugReportReplay::~BugReportReplay()

  /**
   **
   ** the bug report is inconsistent:
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  void
    BugReportReplay::inconsistent()
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::inconsistent()");

      cerr << "BugReport inconsistent!" << endl;

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::inconsistent()");
    } // void BugReportReplay::inconsistent()

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	the keyword from the line read
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   **/
  string
    BugReportReplay::keyword() const
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::keyword()");

      if (this->line.empty())
	DEBUG_RETURNING("",
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::keyword()");

      string const word
	= ((std::find(this->line.begin(), this->line.end(), ':')
	    != this->line.end())
	   ? string(this->line.begin(),
		    std::find(this->line.begin(), this->line.end(), ':'))
	   : DK::Utils::String::word_first(this->line)
	  );

#if 0
      // if the last character is a ':', ignore it
      if (word[word.length() - 1] == ':')
	DEBUG_RETURNING(string(word, 0, word.length() - 1),
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::keyword()");
#endif

      DEBUG_RETURNING(word,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::keyword()");
    } // string BugReportReplay::keyword() const

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	the value from the line read
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   **/
  string
    BugReportReplay::value() const
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::value()");

      if (this->line.empty())
	DEBUG_RETURNING("",
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::value()");

      string value = this->line;
      DK::Utils::String::word_first_remove_with_blanks(value);

      DEBUG_RETURNING(value,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::value()");
    } // string BugReportReplay::value() const

  /**
   **
   ** test, whether the line is the expected
   **
   ** @param	line		expected line
   **
   ** @return	whether the line is the expected
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   **/
  bool
    BugReportReplay::line_expect(string const& line)
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::line_expect(line)");

#ifdef OUTDATED
      if (string(this->line, 1) != string(line, 1)) {
#if 0
      }
#endif
#else
      if (this->line != line) {
#endif
	cerr << "BugReportReplay:\n"
	  << "  expected line '" << line << "', "
	  << "got: '" << this->line << "'"
	  << "\n";

	this->inconsistent();
	DEBUG_RETURNING(false,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::line_expect(line)"
			" = false");
      } // if (this->line != line)

      DEBUG_RETURNING(true,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::line_expect(line)"
		      " = true");
    } // bool BugReportReplay::line_expect(string const& line)

  /**
   **
   ** test, whether the keyword is the expected
   **
   ** @param	keyword		expected keyword
   **
   ** @return	whether the keyword is the expected
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   **/
  bool
    BugReportReplay::keyword_expect(string const& keyword)
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::keyword_expect(keyword)");

#ifdef OUTDATED
      if (string(this->keyword(), 1) != string(keyword, 1)) {
#if 0
      }
#endif
#else
      if (this->keyword() != keyword) {
#endif
	cerr << "BugReportReplay:\n"
	  << "  expected keyword '" << keyword << "', "
	  << "got: '" << this->keyword() << "'"
	  << "\n";

	this->inconsistent();
	DEBUG_RETURNING(false,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::keyword_expect(keyword)"
			" = false");
      } // if (this->keyword() != keyword)

      DEBUG_RETURNING(true,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::keyword_expect(keyword)"
		      " = true");
    } // bool BugReportReplay::keyword_expect(string const& keyword)

  /**
   **
   ** test, whether the value is the expected
   **
   ** @param	value		expected value
   **
   ** @return	whether the value is the expected
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   **/
  bool
    BugReportReplay::value_expect(string const& value)
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::value_expect(value)");

      if (this->value() != value) {
	cerr << "BugReportReplay:\n"
	  << "  '" << this->keyword() << "': "
	  << "expected value '" << value << "', "
	  << "got: '" << this->value() << "'"
	  << "\n";

	this->inconsistent();
	DEBUG_RETURNING(false,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::value_expect(value)"
			" = false");
      } // if (this->value() != value)

      DEBUG_RETURNING(true,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::value_expect(value)"
		      " = true");
    } // bool BugReportReplay::value_expect(string const& value)

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	the keyword from the human line read
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   **/
  string
    BugReportReplay::keyword_human() const
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::keyword_human()");

      if (this->line_human.empty())
	DEBUG_RETURNING("",
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::keyword_human()");

      string const word = DK::Utils::String::word_first(this->line_human);

      // if the last character is a ':', ignore it
      if (word[word.length() - 1] == ':')
	DEBUG_RETURNING(string(word, 0, word.length() - 1),
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::keyword_human()");

      DEBUG_RETURNING(word,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::keyword_human()");
    } // string BugReportReplay::keyword_human() const

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	the value from the line read
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   **/
  string
    BugReportReplay::value_human() const
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::value_human()");

      if (this->line.empty())
	DEBUG_RETURNING("",
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::value_human()");

      string value = this->line;
      DK::Utils::String::word_first_remove(value);

      DEBUG_RETURNING(value,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::value_human()");
    } // string BugReportReplay::value_human() const

  /**
   **
   ** read the first lines from the file
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   **/
  void
    BugReportReplay::read_first_lines()
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::read_first_lines()");

      // the first line
      GETLINE;

      if (this->line != "# FreeDoko Bugreport") {
	cerr << "BugReportReplay::read_first_lines():\n"
	  << "  The file is no bug report!"
	  << endl;
	this->inconsistent();
	DEBUG_RETURNING(VOID,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::read_first_lines()");
      } // if (this->line != "# FreeDoko Bugreport")

      cout << "BugReport\n"
	<< "\n";

      // read the next informations:
      // - version		is printed
      // - system		is printed
      // - date		is printed
      do { // while(!this->line.empty()); 
	GETLINE;
	// test whether to use the old random function
	if (this->keyword() == "Version")
	  // memory leak
	  ::RAND_OLD = (*(DK::Utils::new_Version(this->value()))
		      < DK::Utils::Version(0, 6, 3));

	if (this->line != "/--------\\")
	  cout << this->line << endl;
      } while(this->line != "/--------\\");

      { // read the message

	// the header exists of three lines -- ignore them
	GETLINE;
	GETLINE;

	cout << "\n"
	  << "Message:\n"
	  << "{\n";
#ifdef POSTPHONED
	// the g++-2.95 des not understand this
	this->message.clear();
#else
	this->message = string();
#endif
	do { // while (this->line != "/--------\\")
	  GETLINE;
	  if (this->line != "/-------\\") {
	    cout << "  " << this->line << "\n";
	    this->message += "  " + this->line + "\n";
	  } // if (this->line != "/-------\\")

	  if (istr->fail()) {
	    cerr << "BugReportReplay::read_first_lines():\n"
	      << "  (unexpected) Error reading the first lines.\n"
	      << "Aborting."
	      << endl;
	    exit(1);
	  } // if (istr->fail())
	} while(this->line != "/-------\\") ;
	cout << "}\n";
	cout.flush();

	// the foot exists of three lines -- ignore them (one is already ignored)
	GETLINE;
	GETLINE;
      } // read the message
      GETLINE;

      // now read the lines till the part of 'human'
      do { // while (this->istr_human->good())
	GETLINE_HUMAN;
#ifdef OUTDATED
	if ((this->line_human == "Part Human")
	    || (this->line_human == "Part human")) {
#if 0
	}
#endif
#else
	if (this->line_human == "Part human") {
#endif
	  GETLINE_HUMAN;
	  break;
	}
      } while (this->istr_human->good()) ;

      if ( !this->istr_human->good()
	   || (this->line_human != "{") ) {
	cerr << "BugReportReplay:\n"
	  << "  Did not found the human part.\n"
	  << "Aborting."
	  << endl;
	exit(1);
      } // if (human part not found)
      GETLINE_HUMAN;

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::read_first_lines()");
    } // void BugReportReplay::read_first_lines()

  /**
   **
   ** test, whether the bug report is finished
   **
   ** @param	-
   **
   ** @return	whether the bug report is finished
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  bool
    BugReportReplay::test_for_end()
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::test_for_end()");

#ifdef OUTDATED
      if ((this->line == "finished")
	  || (this->line == "Finished")) {
#if 0
      }
#endif
#else
      if (this->line == "Finished") {
#endif
	this->finished();

	DEBUG_RETURNING(true,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::test_for_end() = true");
      } // if (this->line == "Part Human")


      DEBUG_RETURNING(false,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::test_for_end() = false");
    } // bool BugReportReplay::test_for_end()

  /**
   **
   ** the bug report is finished.
   ** remove it from the global ui
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  void
    BugReportReplay::finished()
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::finished()");

      cout << "BugReport: finished" << endl;
      cout << "Message:\n"
	<< "{\n"
	<< this->message
	<< "}"
	<<endl;

      if (this->istr)
	static_cast<ifstream*>(this->istr)->close();
      if (this->istr_human)
	static_cast<ifstream*>(this->istr_human)->close();
      static_cast<UI_Wrap*>(::ui)->remove(this);

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::finished()");
    } // void BugReportReplay::finished()

  /**
   **
   ** a party is opened
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.2
   **
   **/
  void
    BugReportReplay::party_open()
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::party_open()");

      delete this->istr;
      this->istr = new ifstream(this->filename.c_str());

      if (this->istr->fail()) {
	cerr << "Error opening the bug report file '" << this->filename << "'.\n"
	  << "You have to use a normal text viewer :-(."
	  << endl;

	this->inconsistent();
	static_cast<UI_Wrap*>(::ui)->remove(bug_report_replay);
	delete BugReportReplay::bug_report_replay;
	BugReportReplay::bug_report_replay = NULL;

	DEBUG_RETURNING(VOID,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::party_open()");
      } // if (this->istr->fail())

      delete this->istr_human;
      this->istr_human = new ifstream(this->filename.c_str());

      this->read_first_lines();

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::party_open()");
    } // void BugReportReplay::party_open()

  /**
   **
   ** get the settings for the party:
   **   rules
   **   players
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  void
    BugReportReplay::party_get_settings()
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::party_get_settings()");

      // get the seed and set it
      EXPECT_KEYWORD("Seed");
      this->seed = atoi(this->value().c_str());
      ::party.seed_first_set(this->seed);

      // get the startplayer and set it
      GETLINE;
      EXPECT_KEYWORD("Startplayer");
      this->startplayer_no = atoi(this->value().c_str());

      ::party.startplayer_first_set(this->startplayer_no);

      // overread empty lines
      while (   (this->istr->peek() == '\n')
		|| (this->istr->peek() == '\r'))
	this->istr->get();

      { // read the rules

	GETLINE;
	EXPECT_LINE("Rules");

	// save the hardcoded and the default ruleset

	// the hardcoded ruleset (the harddcoded)
	Rule const rule_hardcoded;
	// the default ruleset (taken from the private directory)
	Rule const rule_default = ::party.rule();
	// change the rules of the party to that of the bugreport
	::party.rule().load(*(this->istr));

	// print the differences of the ruleset
	cout << "Rule differences:\n"
	  << "{\n";
	cout << setw(32) << ""
	  << 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 (   (::party.rule()(Rule::TypeBool(i))
		    != rule_hardcoded(Rule::TypeBool(i)))
		   || (::party.rule()(Rule::TypeBool(i))
		       != rule_default(Rule::TypeBool(i)))) {
	      cout << setw(32) << name(Rule::TypeBool(i))
		<< setw(12) << ::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 (   (::party.rule()(Rule::TypeUnsigned(i))
		    != rule_hardcoded(Rule::TypeUnsigned(i)))
		   || (::party.rule()(Rule::TypeUnsigned(i))
		       != rule_default(Rule::TypeUnsigned(i)))) {
	      cout << setw(32) << name(Rule::TypeUnsigned(i))
		<< setw(12) << ::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 (   (::party.rule()(Rule::TypeUnsignedExtra(i))
		    != rule_hardcoded(Rule::TypeUnsignedExtra(i)))
		   || (::party.rule()(Rule::TypeUnsignedExtra(i))
		       != rule_default(Rule::TypeUnsignedExtra(i)))) {
	      cout << setw(32) << name(Rule::TypeUnsignedExtra(i))
		<< setw(12) << ::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: type unknown)
	    DEBUG_ASSERTION(false,
			    "BugReportReplay::party_get_settings():\n"
			    "  unknown ruletype number " << i);
	  } // if (i: type unknown)
	} // for (unsigned i = Rule::FIRST; i <= Rule::LAST; i++)
	cout << "}" << endl;
	if (!::party.rule()(Rule::SHOW_ALL_HANDS)) {
	  cout << "setting rule '" << ::name(Rule::SHOW_ALL_HANDS)
	    << "' to true." << endl;
	  ::party.rule().set(Rule::SHOW_ALL_HANDS, true);
	} // if (!::party.rule()(Rule::SHOW_ALL_HANDS))
	if (::party.rule()(Rule::SHOW_TRICKS_NUMBER)
	    < ::party.rule()(Rule::NUMBER_OF_TRICKS_IN_GAME)) {
	  cout << "setting rule '" << ::name(Rule::SHOW_TRICKS_NUMBER)
	    << "' to " << ::party.rule()(Rule::NUMBER_OF_TRICKS_IN_GAME)
	    << "." << endl;
	  ::party.rule().set(Rule::SHOW_TRICKS_NUMBER, true);
	} // if (!::party.rule()(Rule::SHOW_TRICKS_NUMBER))
      } // read the rules

      // overread empty lines
      while (   (this->istr->peek() == '\n')
		|| (this->istr->peek() == '\r'))
	this->istr->get();

      { // read the players
	GETLINE;
	EXPECT_LINE("Players");

	// a '{'
	GETLINE;
	EXPECT_LINE("{");

	for (unsigned n = 0;
	     (this->istr->peek() != '}');
	     n++) {
	  Player* player = Player::new_(*(this->istr));
	  if (player == NULL) {
	    cerr << "BugReportReplay::party_get_settings():\n"
	      << "  could not load player.\n"
	      << "Aborting."
	      << endl;
	    exit(1);
	  } // if (player == NULL)
	  ::party.player_set(player, n);

	  // overread empty lines
	  while (   (this->istr->peek() == '\n')
		    || (this->istr->peek() == '\r'))
	    this->istr->get();
	} // for (n, this->istr->peek() != '}')

	GETLINE;
	EXPECT_LINE("}");

	{ // output differences of the aiconfigs of the players to the hardcoded
	  // print the differences of the ruleset
	  cout << "Aiconfig differences:\n"
	    << "{\n";
	  cout << setw(26) << ""
	    << setw(10) << "hardcoded";
	  for (unsigned p = 0; p < ::party.playerno(); p++)
	    if (dynamic_cast<Aiconfig const*>(&(::party.player(p))) != NULL)
	      cout << setw(9) << "player " << p;
	  cout << "\n";

	  Aiconfig const aiconfig; // reference ai
	  unsigned p = 0; // playerno
	  for (p = 0; p < ::party.playerno(); p++)
	    if ((dynamic_cast<Aiconfig const*>(&(::party.player(p))) != NULL)
		&& (dynamic_cast<Aiconfig const&>(::party.player(p)).aitype()
		    != aiconfig.aitype())
	       )
	      break;
	  if (p < ::party.playerno()) {
	    cout << setw(26) << "aitype";
	    {
	      string name;
	      switch(aiconfig.aitype()) {
	      case AITYPE::NO_CHOOSEBESTCARD:
		name = "no choose";
		break;
	      case AITYPE::VIRTUAL_GAMES:
		name = "virtual g";
		break;
	      case AITYPE::GAMETREE:
		name = "gametree";
		break;
	      case AITYPE::GAMETREE_WITH_HEURISTICS:
		name = "gt heu";
		break;
	      case AITYPE::MONTE_CARLO:
		name = "monte c";
		break;
	      default:
		name = dynamic_cast<Aiconfig const&>(::party.player(p)).aitype();
		break;
	      } // switch(aitype)
	      cout << setw(10) << name;
	    }
	    for (p = 0; p < ::party.playerno(); p++)
	      if (dynamic_cast<Aiconfig const*>(&(::party.player(p))) != NULL) {
		string name;
		switch(dynamic_cast<Aiconfig const&>(::party.player(p)).aitype()) {
		case AITYPE::NO_CHOOSEBESTCARD:
		  name = "no choose";
		  break;
		case AITYPE::VIRTUAL_GAMES:
		  name = "virtual g";
		  break;
		case AITYPE::GAMETREE:
		  name = "gametree";
		  break;
		case AITYPE::GAMETREE_WITH_HEURISTICS:
		  name = "gt heu";
		  break;
		case AITYPE::MONTE_CARLO:
		  name = "monte c";
		  break;
		default:
		  name = dynamic_cast<Aiconfig const&>(::party.player(p)).aitype();
		  break;
		} // switch(aitype)
		cout << setw(10) << name;
	      } // if (dynamic_cast<Aiconfig const*>(&(::party.player(p))) != NULL)
	    cout << '\n';
	  } // if (p < ::party.playerno())
	  for (int t = Aiconfig::BOOL_FIRST; t <= Aiconfig::BOOL_LAST; t++) {
	    for (p = 0; p < ::party.playerno(); p++)
	      if ((dynamic_cast<Aiconfig const*>(&(::party.player(p))) != NULL)
		  && (dynamic_cast<Aiconfig const&>(::party.player(p)).value(Aiconfig::TypeBool(t))
		      != aiconfig.value(Aiconfig::TypeBool(t)))
		 )
		break;
	    if (p < ::party.playerno()) {
	      cout << setw(26) << ::name(Aiconfig::TypeBool(t));
	      cout << setw(10) << aiconfig.value(Aiconfig::TypeBool(t));
	      for (p = 0; p < ::party.playerno(); p++)
		if (dynamic_cast<Aiconfig const*>(&(::party.player(p))) != NULL)
		  cout << setw(10) << dynamic_cast<Aiconfig const&>(::party.player(p)).value(Aiconfig::TypeBool(t));
	      cout << '\n';
	    } // if (p < ::party.playerno())
	  } // for (t \in AiConfig::BOOL)
	  for (int t = Aiconfig::UNSIGNED_FIRST; t <= Aiconfig::UNSIGNED_LAST; t++) {
	    for (p = 0; p < ::party.playerno(); p++)
	      if ((dynamic_cast<Aiconfig const*>(&(::party.player(p))) != NULL)
		  && (dynamic_cast<Aiconfig const&>(::party.player(p)).value(Aiconfig::TypeUnsigned(t))
		      != aiconfig.value(Aiconfig::TypeUnsigned(t)))
		 )
		break;
	    if (p < ::party.playerno()) {
	      cout << setw(26) << ::name(Aiconfig::TypeUnsigned(t));
	      cout << setw(10) << aiconfig.value(Aiconfig::TypeUnsigned(t));
	      for (p = 0; p < ::party.playerno(); p++)
		if (dynamic_cast<Aiconfig const*>(&(::party.player(p))) != NULL)
		  cout << setw(10) << dynamic_cast<Aiconfig const&>(::party.player(p)).value(Aiconfig::TypeUnsigned(t));
	      cout << '\n';
	    } // if (p < ::party.playerno())
	  } // for (t \in AiConfig::UNSIGNED)
	  for (int t = Aiconfig::CARD_FIRST; t <= Aiconfig::CARD_LAST; t++) {
	    for (p = 0; p < ::party.playerno(); p++)
	      if ((dynamic_cast<Aiconfig const*>(&(::party.player(p))) != NULL)
		  && (dynamic_cast<Aiconfig const&>(::party.player(p)).value(Aiconfig::TypeCard(t))
		      != aiconfig.value(Aiconfig::TypeCard(t)))
		 )
		break;
	    if (p < ::party.playerno()) {
	      cout << setw(26) << ::name(Aiconfig::TypeCard(t));
	      { // output of 'aiconfig.value()'
		Card const card = aiconfig.value(Aiconfig::TypeCard(t));
		string card_string;
		switch (card.color()) {
		case Card::CLUB:
		  card_string = "C";
		  break;
		case Card::SPADE:
		  card_string = "S";
		  break;
		case Card::HEART:
		  card_string = "H";
		  break;
		case Card::DIAMOND:
		  card_string = "D";
		  break;
		case Card::TRUMP:
		  card_string = "T";
		  break;
		case Card::NOCARDCOLOR:
		  card_string = "X";
		  break;
		} // switch(card.color())
		card_string += ::name(card.value());
		cout << setw(10) << card_string;
	      } // output of 'aiconfig.value()'
	      for (p = 0; p < ::party.playerno(); p++)
		if (dynamic_cast<Aiconfig const*>(&(::party.player(p))) != NULL) {
		  Card const card =  dynamic_cast<Aiconfig const&>(::party.player(p)).value(Aiconfig::TypeCard(t));
		  string card_string;
		  switch (card.color()) {
		  case Card::CLUB:
		    card_string = "C";
		    break;
		  case Card::SPADE:
		    card_string = "S";
		    break;
		  case Card::HEART:
		    card_string = "H";
		    break;
		  case Card::DIAMOND:
		    card_string = "D";
		    break;
		  case Card::TRUMP:
		    card_string = "T";
		    break;
		  case Card::NOCARDCOLOR:
		    card_string = "X";
		    break;
		  } // switch(card.color())
		  card_string += ::name(card.value());
		  cout << setw(10) << card_string;
		}
	      cout << '\n';
	    } // if (p < ::party.playerno())
	  } // for (t \in AiConfig::BOOL)
	  cout << "}" << endl;
	} // output differences of the aiconfigs of the players to the default
      } // read the players
      GETLINE;

      // write the message again
      cout << "Message:\n"
	<< "{\n"
	<< this->message
	<< "}\n"
	<< endl;
      if (::RAND_OLD)
	cout << "!!! Using old random function !!!" << endl;

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::party_get_settings()");
    } // void BugReportReplay::party_get_settings()

  /**
   **
   ** the party is started
   ** (p.e. show the announcements of the other players)
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   **/
  void
    BugReportReplay::party_start()
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::party_start()");

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::party_start()");
    } // void BugReportReplay::party_start()


  /**
   **
   ** the party is finished
   ** (p.e. show the winner and the points of the party)
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   ** @todo	all
   **
   **/
  void
    BugReportReplay::party_finish()
    { 
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::party_finish()");

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::party_finish()");
    } // void BugReportReplay::party_finish()

  /**
   **
   ** the party will close
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   ** @todo	all
   **
   **/
  void
    BugReportReplay::party_close()
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::party_close()");

      if (this->istr) {
	static_cast<ifstream*>(this->istr)->close();
	delete this->istr;
	this->istr = NULL;
      } // if (this->istr)

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::party_close()");
    } // void BugReportReplay::party_close()


  /**
   **
   ** a game is opened
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   **/
  void
    BugReportReplay::game_open()
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::game_open()");

      // testing the seed
      if (::party.game().seed() != this->seed) {
	cerr << "BugReportReplay:\n"
	  << "  the seed '" << ::party.game().seed() << "' "
	  << "is not the same as in the Bugreport: '" << this->seed << "'."
	  << endl;
	this->inconsistent();
	this->finished();
      } // if (::party.seed() != this->seed)

      while (this->keyword_human() == "Reservation") {
	cout << "BugReportReplay: Human: "
	  << this->line_human << endl;
	GETLINE_HUMAN;
      } // while (this->keyword_human() == "Reservation")

      if (this->keyword_human() == "Marriage") {
	// output the selector
	cout << "BugReportReplay: Human: "
	  << this->line_human << endl;
	GETLINE_HUMAN;
      } // if (this->keyword_human() == "Marriage")

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::game_open()");
    } // void BugReportReplay::game_open()

  /**
   **
   ** the game is started
   ** (p.e. show the announcements of the other players)
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  void
    BugReportReplay::game_start()
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::game_start()");

      EXPECT_KEYWORD("Gametype");
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      if (!this->line_expect(::name(::party.game().type()))) {
	cerr << "BugReportReplay:\n"
	  << "  gametype is '" << ::name(::party.game().type()) << "',\n"
	  << "  but the bug report says '" << this->line << "'\n"
	  << "ending bug report."
	  << endl;
	this->finished();
	DEBUG_RETURNING(VOID,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::game_start()");
      } // if (!this->line_expect(::party.game().type()))

      while ( (this->keyword_human() == "Reservation")
      || (this->keyword_human() == "Marriage") ) {
	cout << "BugReportReplay: Human: "
	  << this->line_human << endl;
	GETLINE_HUMAN;
      } // while (this->keyword_human() == "Reservation")

      switch(::party.game().type()) {
      case GAMETYPE::NORMAL: 
	break;
      case GAMETYPE::FIVE_NINES:
      case GAMETYPE::SIX_KINGS:
      case GAMETYPE::FOX_HIGHEST_TRUMP:
	// *** ??? can this happen ???
	cerr << "BugReportReplay:\n"
	  << "  gametype '" << ::party.game().type() << "' "
	  << "not supported (yet).\n";
	this->inconsistent();
	DEBUG_RETURNING(VOID,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::game_start()");
	break;
      case GAMETYPE::POVERTY:
	break;
      case GAMETYPE::GENSCHER:
	break;
      case GAMETYPE::MARRIAGE:
      case GAMETYPE::MARRIAGE_SOLO:
	GETLINE;
	EXPECT_KEYWORD("Selector");
	DK::Utils::String::word_first_remove_with_blanks(this->line);
	if (this->line != ::name(::party.game().marriage_selector())) {
	  cerr << "BugReportReplay:\n"
	    << "  marriage selector is '" << ::name(::party.game().marriage_selector()) << "',\n"
	    << "  but the bug report says '" << this->line << "'"
	    << endl;
	  this->inconsistent();
	} // if (this->line != ::name(::party.game().marriage_selector()))

      case GAMETYPE::MARRIAGE_SILENT:
	// next comes the bride: 'bride: player 1'
	GETLINE;
	EXPECT_KEYWORD("Bride");
	DK::Utils::String::word_first_remove_with_blanks(this->line);
	DK::Utils::String::word_first_remove_with_blanks(this->line);
	if (::party.game().soloplayer().no()
	    != static_cast<unsigned>(atoi(this->line.c_str()))) {
	  cerr << "BugReportReplay:\n"
	    << "  bride is '" << ::name(::party.game().marriage_selector()) << "',\n"
	    << "  but the bug report says '" << this->line << "'"
	    <<endl;
	  this->inconsistent();
	} // if (soloplayer().no() != this->line)
	break;
      case GAMETYPE::SOLO_MEATLESS:
      case GAMETYPE::SOLO_JACK:
      case GAMETYPE::SOLO_QUEEN:
      case GAMETYPE::SOLO_KING:
      case GAMETYPE::SOLO_QUEEN_JACK:
      case GAMETYPE::SOLO_KING_JACK:
      case GAMETYPE::SOLO_KING_QUEEN:
      case GAMETYPE::SOLO_KOEHLER:
      case GAMETYPE::SOLO_CLUB:
      case GAMETYPE::SOLO_HEART:
      case GAMETYPE::SOLO_SPADE:
      case GAMETYPE::SOLO_DIAMOND:
	// testing the soloplayer
	GETLINE;
	EXPECT_KEYWORD("Soloplayer");
	DK::Utils::String::word_first_remove_with_blanks(this->line);
	DK::Utils::String::word_first_remove_with_blanks(this->line);
	unsigned const soloplayerno = static_cast<unsigned>(atoi(this->line.c_str()));
	if (::party.game().soloplayer().no() != soloplayerno) {
	  cerr << "BugReportReplay:\n"
	    << "  soloplayer is '" << ::party.game().soloplayer().no() << "',\n"
	    << "  but the bug report says '" << soloplayerno << "'"
	    <<endl;
	  this->inconsistent();
	} // if (soloplayer().no() != this->line)
	break;
      } // switch(::party.game().type())

      { // test hands
	GETLINE;
	EXPECT_LINE("Hands:");

	// a '{'
	GETLINE;
	EXPECT_LINE("{");


	for (vector<Player*>::const_iterator
	     player = ::party.game().player_begin();
	     player != ::party.game().player_end();
	     player++) {
	  GETLINE;
	  EXPECT_KEYWORD("Player");

	  Hand hand(*(this->istr));
	  if (hand != (*player)->hand()) {
	    cerr << "BugReportReplay:\n"
	      << "  Hand of player " << (*player)->no() << " is wrong:\n"
	      << "player:\n"
	      << (*player)->hand()
	      << "BugReport:\n"
	      << hand
	      << endl;
	    this->inconsistent();
	  } // if (hand != (*player)->hand())

	} // for (player \in ::party.game().player)
	// a '}'
	GETLINE;
	EXPECT_LINE("}");
      } // test hands


      // from now on the 'bug report replay' is one line (= action) ahead
      // so it can act in place of the human player (announcements)
      GETLINE;
      this->test_for_announcement();

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::game_start()");
    } // void BugReportReplay::game_start()


  /**
   **
   ** the game is finished
   ** (p.e. show the winner and the points of the game)
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   ** @todo	all
   **
   **/
  void
    BugReportReplay::game_finish()
    { 
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::game_finish()");

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::game_finish()");
    } // void BugReportReplay::game_finish()

  /**
   **
   ** the game will close
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   ** @todo	all
   **
   **/
  void
    BugReportReplay::game_close()
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::game_close()");

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::game_close()");
    } // void BugReportReplay::game_close()

  /**
   **
   ** the human 'player' has to play a card
   **
   ** @param	player	the player, who has to play a card
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   **/
  void
    BugReportReplay::card_get(Player const& player)
    { 
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::card_get(player)");

      if (this->line_human == "}") {
	this->finished();
	DEBUG_RETURNING(VOID,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::card_get(player)");
      } // if (this->line_human == "}")

#ifdef OUTADTED
      DEBUG_ASSERTION((this->keyword_human() == "played")
		      || (this->keyword_human() == "Played"),
		      "BugReportReplay::card_get(player):\n"
		      "  not found 'played:' in the human part: "
		      << this->line_human);
#else
      DEBUG_ASSERTION((this->keyword_human() == "Played"),
		      "BugReportReplay::card_get(player):\n"
		      "  not found 'played:' in the human part: "
		      << this->line_human);
#endif

      cout << "BugReport: Human: " << this->line_human << endl;
      GETLINE_HUMAN;

#ifndef POSTPHONED
      while (this->keyword_human() == "announcement:")
	GETLINE_HUMAN;
#endif

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::card_get(player)");
    } // void BugReportReplay::card_get(player)

  /**
   **
   ** The player 'player' plays the card 'card'.
   ** The line is like:
   **   played: player 1: club ace
   **
   ** @param	card  	the played card
   ** @param	player	the player, who has played a card
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  void
    BugReportReplay::card_played(Card const& card, Player const& player)
    { 
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::card_played(card, player)");

#ifdef OUTDATED
      if ((this->keyword() != "played")
	  && (this->keyword() != "Played")) {
#if 0
      }
#endif
#else
      if (this->keyword() != "Played") {
#endif
	cerr << "BugReportReplay:\n"
	  << "  a card should be played, but got:\n"
	  << "  " << this->line << endl;
	this->inconsistent();
	GETLINE;
	this->card_played(card, player);

	DEBUG_RETURNING(VOID,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::card_played(card, player)");
      } // if (this->keyword() != "played")

      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // the next word is 'player', remove it also
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // next comes the player number, followed by a ':'
      unsigned const playerno = static_cast<unsigned>(atoi(this->line.c_str()));
      if (playerno != player.no()) {
	cerr << "BugReportReplay:\n"
	  << "  Player '" << player.no() << "' has played the card,\n"
	  << "  the bug report says player '" << playerno << "'"
	  << endl;
	this->inconsistent();
      } // if (playerno != player.no())
      // remove the player number
      DK::Utils::String::word_first_remove_with_blanks(this->line);

      // the last part is the card
      Card card_br = Card(this->line);
      if (card_br != card) {
	cerr << "BugReportReplay:\n"
	  << "  Played card is '" << card << "',\n"
	  << "  the bug report says '" << card_br << "'"
	  << endl;
	this->inconsistent();
      } // if (card_br != card)

      GETLINE;
      this->test_for_announcement();

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::card_played(card, player)");
    } // void BugReportReplay::card_played(card, player)


  /**
   **
   ** Test for an announcement and make it, if the player is human
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  bool
    BugReportReplay::test_for_announcement()
    { 
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::test_for_announcement()");

#ifdef OUTDATED
      if ((this->keyword() != "announcement")
	  && (this->keyword() != "Announcement"))
#if 0
	;
#endif
#else
      if (this->keyword() != "Announcement")
#endif
	DEBUG_RETURNING(false,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::test_for_announcement() = false");

      // there is an announcement, look, which player has made it
      string line = this->line;

      DK::Utils::String::word_first_remove_with_blanks(line);
      DK::Utils::String::word_first_remove_with_blanks(line);
      unsigned const playerno = static_cast<unsigned>(atoi(line.c_str()));

      // test, whether a human player has made the announcement
      if (::party.game().player(playerno).type() == Player::HUMAN) {
	// read the announcement
	DK::Utils::String::word_first_remove_with_blanks(line);
	Announcement announcement = ANNOUNCEMENT::from_name(line);

	cout << "BugReportReplay:\n"
	  << "  making announcement '" << announcement << "'"
	  << " for player " << playerno
	  << endl;

	// make the announcement in place of the human and tell it the user
	::party.game().announcement_make(announcement,
					 ::party.game().player(playerno));
      } // if (player(playerno).type() == HUMAN)

      DEBUG_RETURNING(true,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::test_for_announcement() = true");
    } // bool BugReportReplay::test_for_announcement()

  /**
   **
   ** The announcement 'announcement' has been made by player 'player'.
   ** The line is like:
   **   played: player 1: club ace
   **
   ** @param	announcement	the announcement
   ** @param	player		the player, who has made the announcement
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  void
    BugReportReplay::announcement_made(Announcement const& announcement,
				       Player const& player)
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::announcement_made(announcement, player)");

#ifdef OUTDATED
      if ((this->keyword() != "announcement")
	  && (this->keyword() != "Announcement")) {
#if 0
      }
#endif
#else
      if (this->keyword() != "Announcement") {
#endif
	cerr << "BugReportReplay:\n"
	  << "  Announcement should be made, but got:\n"
	  << "  " << this->line << endl;
	this->inconsistent();

	DEBUG_RETURNING(VOID,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::announcement_made(announcement, player)");
      } // if (this->keyword() != "Announcement")

      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // the next word is 'player', remove it also
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // next comes the player number, followed by a ':'
      unsigned const playerno = static_cast<unsigned>(atoi(this->line.c_str()));
      if (playerno != player.no()) {
	cerr << "BugReportReplay:\n"
	  << "  Player '" << player.no() << "' has made the announcement,\n"
	  << "  the bug report says player '" << playerno << "'"
	  << endl;
	this->inconsistent();
      } // if (playerno != player.no())
      // remove the player number
      DK::Utils::String::word_first_remove_with_blanks(this->line);

      // the last part is the announcement
      Announcement announcement_br = ANNOUNCEMENT::from_name(this->line);
      if (announcement_br != announcement) {
	cerr << "BugReportReplay:\n"
	  << "  Announcement is '" << announcement << "',\n"
	  << "  the bug report says '" << announcement_br << "'"
	  << endl;
	this->inconsistent();
      } // if (announcement_br != announcement)

      if (player.type() == Player::HUMAN)
	GETLINE_HUMAN;

      GETLINE;
      this->test_for_announcement();

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::announcement_made(announcement, player)");
    } // void BugReportReplay::announcement_made(Announcement const& announcement, Player const& player)

  /**
   **
   ** The player has swines.
   ** The line is like:
   **   Swines: player 1
   **
   ** @param	player		the player with the swines
   **
   ** @return	-
   **
   ** @version	0.5.4
   **
   ** @author	Diether Knof
   **
   **/
  void
    BugReportReplay::swines_announced(Player const& player)
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::swines_announced(player)");

#ifdef POSTPHONED
      if (this->keyword() != "Swines")
	DEBUG_RETURNING(VOID,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::swines_announced(player)");
#else
      if (this->keyword() != "Swines") {
	cerr << "BugReportReplay:\n"
	  << "  Swines should be announced, but got:\n"
	  << "  " << this->line << endl;
	this->inconsistent();

	DEBUG_RETURNING(VOID,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::swines_announced(player)");
      } // if (this->keyword() != "Swines")

      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // the next word is 'player', remove it also
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // next comes the player number, followed by a ':'
      unsigned const playerno = static_cast<unsigned>(atoi(this->line.c_str()));
      if (playerno != player.no()) {
	cerr << "BugReportReplay:\n"
	  << "  Player '" << player.no() << "' has announced swines,\n"
	  << "  the bug report says player '" << playerno << "'"
	  << endl;
	this->inconsistent();
      } // if (playerno != player.no())
#endif // #ifndef POSTPHONED

      GETLINE;
      this->test_for_announcement();

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::swines_announced(player)");
    } // void BugReportReplay::swines_announced(Player const& player)

  /**
   **
   ** the player has hyperswines
   ** The line is like:
   **   Hyperswines: player 1
   **
   ** @param	player		the player with the swines
   **
   ** @return	-
   **
   ** @version	0.5.4
   **
   ** @author	Diether Knof
   **
   **/
  void
    BugReportReplay::hyperswines_announced(Player const& player)
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::hyperswines_announced(player)");

#ifndef POSTPHONED
      if (this->keyword() != "Hyperswines")
	DEBUG_RETURNING(VOID,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::swines_announced(player)");
#else
      if (this->keyword() != "Hyperswines") {
	cerr << "BugReportReplay:\n"
	  << "  Hyperswines should be announced, but got:\n"
	  << "  " << this->line << endl;
	this->inconsistent();

	DEBUG_RETURNING(VOID,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::hypeswines_announced(player)");
      } // if (this->keyword() != "Hyperswines")

      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // the next word is 'player', remove it also
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // next comes the player number, followed by a ':'
      unsigned const playerno = static_cast<unsigned>(atoi(this->line.c_str()));
      if (playerno != player.no()) {
	cerr << "BugReportReplay:\n"
	  << "  Player '" << player.no() << "' has announced hyperswines,\n"
	  << "  the bug report says player '" << playerno << "'"
	  << endl;
	this->inconsistent();
      } // if (playerno != player.no())
#endif // #ifndef POSTPHONED

      GETLINE;
      this->test_for_announcement();


      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::hyperswines_announced(player)");
    } // void BugReportReplay::hyperswines_announced(Player const& player)

  /**
   **
   ** 'player' shifts 'cardno' cards:
   ** The lines are like:
   **   Poverty: player 0 shifts 2 cards:
   **   0 diamond king
   **   1 heart jack
   **
   ** @param	player	the player who shifts the cards
   ** @param	cardno	the number of cards that are shifted
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  void
    BugReportReplay::poverty_shift(Player const& player, unsigned const cardno)
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::poverty_shift(player, cardno)");

      if (this->keyword() != "Poverty") {
	cerr << "BugReportReplay:\n"
	  << "  keyword 'Poverty' expected, but got:\n"
	  << "  " << this->line << endl;
	this->inconsistent();

	DEBUG_RETURNING(VOID,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::poverty_shift(player, cardno)");
      } // if (this->keyword() != "Poverty")

      // remove 'Poverty:'
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // the next word is 'player', remove it also
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // next comes the player number,

      unsigned const playerno = static_cast<unsigned>(atoi(this->line.c_str()));
      if (playerno != player.no()) {
	cerr << "BugReportReplay:\n"
	  << "  Player '" << player.no() << "' has a poverty,\n"
	  << "  the bug report says player '" << playerno << "'"
	  << endl;
	this->inconsistent();
      } // if (playerno != player.no())

      // remove the playerno
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // the next word is 'shifts', remove it also
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // next comes the cards number,

      unsigned const cardno_br = static_cast<unsigned>(atoi(this->line.c_str()));
      if (cardno_br != cardno) {
	cerr << "BugReportReplay:\n"
	  << "  Player '" << player.no() << "' shifts '"
	  << cardno << "' cards,\n"
	  << "  the bug report says '" << cardno_br << "' cards."
	  << endl;
	this->inconsistent();
      } // if (cardno_br != cardno)

      // next come the shifted cards
      Hand const shifted_cards = Hand(*(this->istr));

      if (shifted_cards != *(player.game().poverty_cards())) {
	cerr << "BugReportReplay:\n"
	  << "Player '" << player.no() << "' shifts the following cards:\n"
	  << *(player.game().poverty_cards())
	  << ", but the bugreport says:\n"
	  << shifted_cards;
	this->inconsistent();
      } // if (shifted_cards != *(player.game().poverty_cards()))
      GETLINE;

      if (player.type() == Player::HUMAN) {
	// next come the shifted cards
	Hand const shifted_cards = Hand(*(this->istr_human));

	cout << "BugReport: Human: shift the following cards:\n"
	  << shifted_cards;

	GETLINE_HUMAN;
      } // if (player.type() == Player::HUMAN)

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::poverty_shift(player, cardno)");
    } // void BugReportReplay::poverty_shift(Player const& player, unsigned const cardno)

  /**
   **
   ** 'player' shifts 'cardno' cards:
   ** The lines are like:
   **   Poverty: player 0 shifts 2 cards:
   **   0 diamond king
   **   1 heart jack
   **
   ** @param	player	the player who shifts the cards
   ** @param	cardno	the number of cards that are shifted
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   ** @todo	all
   **
   **/
  void
    BugReportReplay::poverty_ask(Player const& player, unsigned const cardno)
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::poverty_ask(player, cardno)");

      if (player.type() == Player::HUMAN) {
	if (this->keyword_human() != "Poverty") {
	  cerr << "BugReportReplay: Part Human:\n"
	    << "  keyword 'Poverty' expected, but got:\n"
	    << "  " << this->line_human << endl;
	  this->inconsistent();

	  DEBUG_RETURNING(VOID,
			  INFO_OS_BUG_REPORT_REPLAY,
			  "BugReportReplay::poverty_ask(player, cardno)");
	} // if (this->keyword_human() != "Poverty")

	// remove 'Poverty:'
	DK::Utils::String::word_first_remove_with_blanks(this->line_human);
	// the next word is either 'accepted:' or 'denied'

	if (this->keyword_human() == "denied") {
	  cout << "BugReport: Human: denied poverty" << endl;
	} else if (this->keyword_human() == "accepted") {
	  // next come the returned cards
	  Hand const returned_cards = Hand(*(this->istr_human));

	  cout << "BugReport: Human: accept the cards and return the following:\n"
	    << returned_cards;

	} else { // if (this->keyword_human() == "...")
	  cerr << "BugReportReplay:\n"
	    << "  Expected keyword 'accepted:' or 'denied', but got:\n"
	    << "  " << this->line_human << endl;
	  this->inconsistent();
	} // if (this->keyword_human() == "...")
	GETLINE_HUMAN;
      } // if (player.type() == Player::HUMAN)

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::poverty_ask(player, cardno)");
    } // void BugReportReplay::poverty_ask(Player const& player, unsigned const cardno)

  /**
   **
   ** the player has denied to take the cards
   **
   ** @param	player	player who has denied the cards
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  void
    BugReportReplay::poverty_take_denied(Player const& player)
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::poverty_take_denied(player)");

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::poverty_take_denied(player)");
    } // void BugReportReplay::take_denied(Player const& player)

  /**
   **
   ** all players have denied to take the cards
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.2
   **
   **/
  void
    BugReportReplay::poverty_take_denied_by_all()
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::poverty_take_denied_by_all()");

      if (this->line != "Poverty: all players denied") {
	cerr << "BugReportReplay:\n"
	  << "  all playes denied the poverty,\n"
	  << "  the bug report says: " << this->line
	  << endl;
	this->inconsistent();
      } // if (line_expect("Poverty: all players denied"))

      GETLINE;

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::poverty_take_denied_by_all()");
    } // void BugReportReplay::take_denied_by_all()

  /**
   **
   ** 'player' accepts to take the shifted cards
   ** and returns 'cardno' cards with 'trumpno' trumps
   ** The lines are like:
   **   Poverty: player 0 accepts and returns 3 cards with 0 trumps.
   **   0 diamond king
   **   1 heart jack
   **
   ** @param	player	the player who has denied to take the cards
   ** @param	cardno	number of cards that are given back
   ** @param	trumpno	number of trumps of the cards
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  void
    BugReportReplay::poverty_take_accepted(Player const& player,
					   unsigned const cardno,
					   unsigned const trumpno)
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::poverty_take_denied_by_all(player, cardno, trumpno)");

      if (this->keyword() != "Poverty") {
	cerr << "BugReportReplay:\n"
	  << "  keyword 'Poverty' expected, but got:\n"
	  << "  " << this->line << endl;
	this->inconsistent();

	DEBUG_RETURNING(VOID,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::poverty_shift(player, cardno)");
      } // if (this->keyword() != "Poverty")

      // remove 'Poverty:'
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // the next is 'player', remove it also
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // next comes the player number,

      unsigned const playerno = static_cast<unsigned>(atoi(this->line.c_str()));
      if (playerno != player.no()) {
	cerr << "BugReportReplay:\n"
	  << "  Player '" << player.no() << "' has accepted the poverty,\n"
	  << "  the bug report says player '" << playerno << "'"
	  << endl;
	this->inconsistent();
      } // if (playerno != player.no())

      // remove the playerno
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // the next word are 'accepts and returns', remove them also
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // next comes the cards number,

      unsigned const cardno_br = static_cast<unsigned>(atoi(this->line.c_str()));
      if (cardno_br != cardno) {
	cerr << "BugReportReplay:\n"
	  << "  Player '" << player.no() << "' returns '"
	  << cardno << "' cards,\n"
	  << "  the bug report says '" << cardno_br << "' cards."
	  << endl;
	this->inconsistent();
      } // if (cardno_br != cardno)

      // remove the cardno
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // the next word are 'cards with', remove them also
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // next comes the trump number
      unsigned const trumpno_br = static_cast<unsigned>(atoi(this->line.c_str()));
      if (trumpno_br != trumpno) {
	cerr << "BugReportReplay:\n"
	  << "  Player '" << player.no() << "' returns '"
	  << trumpno << "' trumps,\n"
	  << "  the bug report says '" << trumpno_br << "' cards."
	  << endl;
	this->inconsistent();
      } // if (trumpno_br != trumpno)


      // next come the returnd cards
      Hand const returned_cards = Hand(*(this->istr));

      if (returned_cards != *(player.game().poverty_cards())) {
	cerr << "BugReportReplay:\n"
	  << "Player '" << player.no() << "' shifts the following cards:\n"
	  << *(player.game().poverty_cards())
	  << ", but the bugreport says:\n"
	  << returned_cards;
	this->inconsistent();
      } // if (returned_cards != *(player.game().poverty_cards()))

      GETLINE;

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::poverty_take_accept(player, cardno, trumpno)");
    } // void BugReportReplay::poverty_take_accepted(Player const& player, unsigned const cardno, unsigned const trumpno)

  /**
   **
   ** genscher: the human must select a partner
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  void
    BugReportReplay::genscher_partner()
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::genscher_partner()");

      if (this->keyword_human() != "Genscher") {
	cerr << "BugReportReplay: Human\n"
	  << "  Genscher expected, but got:\n"
	  << "  " << this->line_human << endl;
	this->inconsistent();

	DEBUG_RETURNING(VOID,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::genscher_partner()");
      } // if (this->keyword_human() != "Genscher")

      cout << "BugReport: Human: " << this->line_human << endl;
      GETLINE_HUMAN;

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::genscher_partner()");
    } // void BugReportReplay::genscher_partner()

  /**
   **
   ** genscher: Information of the new team
   ** The line is like:
   **   genscher: player 0 selects player 3
   **
   ** @param	player	genscher player
   ** @param	partner	partner of the gensher player
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  void
    BugReportReplay::genscher(Player const& player,
			      Player const& partner)
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::genscher(player, partner)");

      if (this->keyword() != "Genscher") {
	cerr << "BugReportReplay:\n"
	  << "  Genscher expected, but got:\n"
	  << "  " << this->line << endl;
	this->inconsistent();

	DEBUG_RETURNING(VOID,
			INFO_OS_BUG_REPORT_REPLAY,
			"BugReportReplay::genscher(player, partner)");
      } // if (this->keyword() != "Genscher")

      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // the next word is 'player', remove it also
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      // next comes the player number, followed by a ':'
      unsigned const playerno = static_cast<unsigned>(atoi(this->line.c_str()));
      if (playerno != player.no()) {
	cerr << "BugReportReplay:\n"
	  << "  Player '" << player.no() << "' has the genscher,\n"
	  << "  the bug report says player '" << playerno << "'"
	  << endl;
	this->inconsistent();
      } // if (playerno != player.no())
      // remove the player number
      DK::Utils::String::word_first_remove_with_blanks(this->line);

      // the next two words are 'selects player'
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      DK::Utils::String::word_first_remove_with_blanks(this->line);

      // the last part is the selected partner
      unsigned const partnerno = static_cast<unsigned>(atoi(this->line.c_str()));
      if (partnerno != partner.no()) {
	cerr << "BugReportReplay:\n"
	  << "  Player '" << partner.no() << "' is selected as partner,\n"
	  << "  the bug report says player '" << partnerno << "'"
	  << endl;
	this->inconsistent();
      } // if (playerno != player.no())
      GETLINE;
      this->test_for_announcement();

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::genscher(player, partner)");
    } // void BugReportReplay::genscher(Player const& player, Player const& partner)

  /**
   **
   ** a new trick is opened
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  void
    BugReportReplay::trick_open()
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::trick_open()");

      EXPECT_LINE("Trick: "
		  + DK::Utils::String::to_string(::party.game().trick_current_no()));
      GETLINE;
      this->test_for_announcement();

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::trick_open()");
    } // void BugReportReplay::trick_open()

  /**
   **
   ** the trick 'trick' is full
   **
   ** @param	trick	the full trick
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.5.4
   **
   **/
  void
    BugReportReplay::trick_full(Trick const& trick)
    {
      DEBUG_CALLING(INFO_OS_BUG_REPORT_REPLAY,
		    "BugReportReplay::trick_full(trick)");

#ifndef POSTPHONED
      // till Version 0.6.0
      EXPECT_KEYWORD("Trick full");
#else
      EXPECT_LINE("Trick full: "
		  + DK::Utils::String::to_string(::party.game().trick_current_no()));
#endif
#ifdef OUTDATED
      // till version 0.5.3, here comes the line 'Trick:'
      if (this->istr->peek() == 'T') {
	GETLINE;
	EXPECT_LINE("Trick:");
      }
#endif
      Trick trick_br(*(this->istr));
      if (trick_br != trick) {
	cerr << "BugReportReplay:\n"
	  << "  Full trick not valid:\n"
	  << "Game:\n"
	  << trick
	  << "BugReport:\n"
	  << trick_br
	  << endl;
	this->inconsistent();
      } // if (trick_br != trick)

      GETLINE;
      this->test_for_announcement();

      DEBUG_RETURNING(VOID,
		      INFO_OS_BUG_REPORT_REPLAY,
		      "BugReportReplay::trick_full(trick)");
    } // void BugReportReplay::trick_full(trick)

} // namespace OS_NS
