/*********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *
 *   Copyright (C) 2001-2006  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 <sstream>

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)) \
    return ; \
  } else
#if 0
;
#endif

#define EXPECT_KEYWORD(keyword) \
  if (true) { \
    if (!this->keyword_expect(keyword)) \
    return ; \
  } else
#if 0
;
#endif

#define EXPECT_VALUE(value) \
  if (true) { \
    if (!this->value_expect(value)) \
    return ; \
  } else
#if 0
;
#endif

// 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 {

  /**
   ** 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),
    filename(filename),
    istr(NULL),
    line(),
    istr_human(NULL),
    line_human(),
    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(),
    full_tricks(),
    game_summary(NULL),
    current_hands(),
    current_trick(NULL),
    human_actions()
  {
    if (::bug_report_replay) {
      static_cast<UI_Wrap*>(::ui)->remove(::bug_report_replay);
      delete ::bug_report_replay;
      ::bug_report_replay = NULL;
    } // if (::bug_report_replay != NULL)
    if (!DK::Utils::File::isfile(filename)) {
      cerr << "Selected bug report '" << filename << "' is no file." << endl;
      return ;
    } // if (!DK::Utils::File::isfile(filename))

    ::bug_report_replay = this;
    ::setting.set(Setting::SAVE_PARTY_CHANGES, false);

    this->read_file();

    ::ui->bug_report_replay_open(*this);

    return ;
  } // BugReportReplay::BugReportReplay(string const& filename)

  /**
   **
   ** Destructor
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **
   **/
  BugReportReplay::~BugReportReplay()
  {
    ::ui->bug_report_replay_close();

    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)

    delete this->poverty_cards_shifted;
    delete this->poverty_cards_returned;
    delete this->game_summary;
    delete this->current_trick;

    if (this == ::bug_report_replay) {
      static_cast<UI_Wrap*>(::ui)->remove(::bug_report_replay);
      ::bug_report_replay = NULL;
    }

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

  /**
   ** 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"
	  << *this->poverty_cards_shifted
	  << "}\n";
      } else {
	ostr << "poverty cards shifted: -\n";
      }

      if (this->poverty_cards_returned) {
	ostr << "poverty cards returned\n"
	  << "{\n"
	  << *this->poverty_cards_returned
	  << "}\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< ::GameplayAction*>::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< ::GameplayAction*>::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

  /**
   **
   ** the bug report is inconsistent:
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **
   **/
  void
    BugReportReplay::inconsistent()
    {
      cerr << "BugReport inconsistent!" << endl;

      return ;
    } // void BugReportReplay::inconsistent()

  /**
   ** -> result
   **
   ** @param	-
   **
   ** @return	the keyword from the line read
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **/
  string
    BugReportReplay::keyword() const
    {
      if (this->line.empty())
	return "";

      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(), ':'))
	   : this->line
#ifdef OUTDATED
	   // 0.7.1
	   : DK::Utils::String::word_first(this->line)
#endif
	  );

      return word;
    } // string BugReportReplay::keyword() const

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	the value from the line read
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **
   **/
  string
    BugReportReplay::value() const
    {
      if (this->line.empty())
	return "";

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

      return 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.6.7
   **
   **/
  bool
    BugReportReplay::line_expect(string const& line)
    {
      if (this->line != line) {
	cerr << "BugReportReplay:\n"
	  << "  expected line '" << line << "', "
	  << "got: '" << this->line << "'"
	  << "\n";

	this->inconsistent();
	return false;
      } // if (this->line != line)

      return 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.6.7
   **
   **/
  bool
    BugReportReplay::keyword_expect(string const& keyword)
    {
      if (this->keyword() != keyword) {
	cerr << "BugReportReplay:\n"
	  << "  expected keyword '" << keyword << "', "
	  << "got: '" << this->keyword() << "'"
	  << "\n";

	this->inconsistent();
	return false;
      } // if (this->keyword() != keyword)

      return 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.6.7
   **
   **/
  bool
    BugReportReplay::value_expect(string const& value)
    {
      if (this->value() != value) {
	cerr << "BugReportReplay:\n"
	  << "  '" << this->keyword() << "': "
	  << "expected value '" << value << "', "
	  << "got: '" << this->value() << "'"
	  << "\n";

	this->inconsistent();
	return false;
      } // if (this->value() != value)

      return true;
    } // bool BugReportReplay::value_expect(string const& value)

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	the keyword from the human line read
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **
   **/
  string
    BugReportReplay::keyword_human() const
    {
      if (this->line_human.empty())
	return "";

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

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

      return word;
    } // string BugReportReplay::keyword_human() const

  /**
   **
   ** -> result
   **
   ** @param	-
   **
   ** @return	the value from the line read
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **
   **/
  string
    BugReportReplay::value_human() const
    {
      if (this->line.empty())
	return "";

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

      return value;
    } // string BugReportReplay::value_human() const

  /**
   **
   ** read the first lines from the file
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **
   **/
  void
    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();
	return ;
      } // if (this->line != "# FreeDoko Bugreport")

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

      // 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(!this->line.empty()); 
	GETLINE;
	string const keyword = this->keyword();

	if (this->line == "") {
	} else if (this->line == "/--------\\") {
	  break;
	} else if (keyword == "Version") {
	  this->version = DK::Utils::new_Version(this->value());
	  if (!this->version) {
	    cerr << "could not read version: '" << this->value() << "'\n"
	      << "ignoring it." << endl;

	  }
	  if (this->version
	      && (*this->version
		  < DK::Utils::Version(0, 6, 3,
				       DK::Utils::Date(2004, 5, 5))) ) {
	    cerr << "Too old version "
	      << *this->version
	      << " < 0.6.3  (2004-05-05)"
	      << endl;
#ifndef DKNOF
	    exit(0);
#endif
	  }
	  ::RAND_OLD = (this->version
			&& (*this->version
			    < DK::Utils::Version(0, 6, 6,
						 DK::Utils::Date(2004, 8, 29))) );
	} else if (keyword == "Compiled") {
	  this->compiled = this->value();
	} else if (keyword == "System") {
	  this->system = this->value();
	} else if (keyword == "Time") {
	  this->time = this->value();
	} else if (keyword == "Language") {
	  this->language = this->value();
	} else if (keyword == "Trick") {
	  std::istringstream istr(this->value());
	  istr >> this->trickno;
	} else { // if !(keyword == ...)
	  cerr << "unknown keyword '" << keyword << "'\n"
	    << "ignoring it." << endl;
	} // if !(keyword == ...)

      } while(this->line != "/--------\\");

      { // read the message

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

	cout << "\n"
	  << "Message:\n"
	  << "{\n";
	this->message.clear();
	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;
	if (this->line_human == "Part human") {
	  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;

      return ;
    } // 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.6.7
   **
   **/
  bool
    BugReportReplay::test_for_end()
    {
      if (this->line == "Finished") {
	this->finished();

	return true;
      } // if (this->line == "Part Human")


      return false;
    } // bool BugReportReplay::test_for_end()

  /**
   **
   ** the bug report is finished.
   ** remove it from the global ui
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **
   **/
  void
    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);

      return ;
    } // void BugReportReplay::finished()

  /**
   **
   ** 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);

      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 ::bug_report_replay;
	::bug_report_replay = NULL;

	return ;
      } // if (this->istr->fail())

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

      this->read_first_lines();
#ifndef OUTDATED
      ::ui->bug_report_replay_open(*this);
#endif

      return ;
    } // void BugReportReplay::party_open(Party const& party)

  /**
   **
   ** get the settings for the party:
   **   rules
   **   players
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **
   **/
  void
    BugReportReplay::party_get_settings()
    {
      // get the seed and set it
      EXPECT_KEYWORD("Seed");
      {
	istringstream istr(this->value());
	istr >> this->seed;
      }
      ::party.set_seed_first(this->seed);

      // get the startplayer and set it
      GETLINE;
      EXPECT_KEYWORD("Startplayer");
      {
	istringstream istr(this->value());
	istr >> this->startplayer_no;
      }

      ::party.set_startplayer_first(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 = this->party().rule();
	// change the rules of the party to that of the bugreport
	::party.rule().read(*(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 (   (this->party().rule()(Rule::TypeBool(i))
		    != rule_hardcoded(Rule::TypeBool(i)))
		|| (this->party().rule()(Rule::TypeBool(i))
		    != rule_default(Rule::TypeBool(i)))) {
	      cout << setw(32) << 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(32) << 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(32) << 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(32) << 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_get_settings():\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<Rule::TypeBool> 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<Rule::TypeBool>::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, true);
	  } // if (!this->party().rule()(Rule::SHOW_TRICKS_NUMBER))
	} // set some specific rules
      } // 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.set_player(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 < this->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 (unsigned t = 0;
	       t < ::party.rule()(Rule::MAX_NUMBER_OF_TRICKS_IN_GAME);
	       ++t) {
	    for (p = 0; p < this->party().playerno(); p++)
	      if ((dynamic_cast<Aiconfig const*>(&(::party.player(p))) != NULL)
		  && (dynamic_cast<Aiconfig const&>(::party.player(p)).aitype(t)
		      != aiconfig.aitype(t))
		 )
		break;
	    if (p < this->party().playerno()) {
	      cout << setw(22) << "aitype" << setw(4) << t;
	      {
		string name;
		switch(aiconfig.aitype(t)) {
		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(t);
		  break;
		} // switch(aitype)
		cout << setw(10) << name;
	      }
	      for (p = 0; p < this->party().playerno(); p++)
		if (dynamic_cast<Aiconfig const*>(&(::party.player(p))) != NULL) {
		  string name;
		  switch(dynamic_cast<Aiconfig const&>(::party.player(p)).aitype(t)) {
		  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(t);
		    break;
		  } // switch(aitype)
		  cout << setw(10) << name;
		} // if (dynamic_cast<Aiconfig const*>(&(::party.player(p))) != NULL)
	      cout << '\n';
	    }
	  } // if (p < this->party().playerno())
	  for (int t = Aiconfig::BOOL_FIRST; t <= Aiconfig::BOOL_LAST; t++) {
	    for (p = 0; p < this->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 < this->party().playerno()) {
	      cout << setw(26) << ::name(Aiconfig::TypeBool(t));
	      cout << setw(10) << aiconfig.value(Aiconfig::TypeBool(t));
	      for (p = 0; p < this->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 < this->party().playerno())
	  } // for (t \in AiConfig::BOOL)
	  for (int t = Aiconfig::UNSIGNED_FIRST; t <= Aiconfig::UNSIGNED_LAST; t++) {
	    for (p = 0; p < this->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 < this->party().playerno()) {
	      cout << setw(26) << ::name(Aiconfig::TypeUnsigned(t));
	      cout << setw(10) << aiconfig.value(Aiconfig::TypeUnsigned(t));
	      for (p = 0; p < this->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 < this->party().playerno())
	  } // for (t \in AiConfig::UNSIGNED)
	  for (int t = Aiconfig::CARD_FIRST; t <= Aiconfig::CARD_LAST; t++) {
	    for (p = 0; p < this->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 < this->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 < this->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 < this->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;

      return ;
    } // void BugReportReplay::party_get_settings()

  /**
   **
   ** the party will close
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **
   ** @todo	all
   **
   **/
  void
    BugReportReplay::party_close()
    {
      this->OS::party_close();

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

      return ;
    } // void BugReportReplay::party_close()


  /**
   **
   ** a game is opened
   **
   ** @param	game	game that is opened
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **
   **/
  void
    BugReportReplay::game_open(Game const& game)
    {
      this->OS::game_open(game);

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

      return ;
    } // void BugReportReplay::game_open(Game const& game)

  /**
   **
   ** the game is started
   ** (p.e. show the announcements of the other players)
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **
   **/
  void
    BugReportReplay::game_start()
    {
      EXPECT_KEYWORD("Gametype");
      DK::Utils::String::word_first_remove_with_blanks(this->line);
      if (!this->line_expect(::name(this->game().type()))) {
	cerr << "BugReportReplay:\n"
	  << "  gametype is '" << ::name(this->game().type()) << "',\n"
	  << "  but the bug report says '" << this->line << "'\n"
	  << "ending bug report."
	  << endl;
	this->finished();
	return ;
      } // if (!this->line_expect(this->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(this->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 '" << this->game().type() << "' "
	  << "not supported (yet).\n";
	this->inconsistent();
	return ;
	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(this->game().marriage_selector())) {
	  cerr << "BugReportReplay:\n"
	    << "  marriage selector is '" << ::name(this->game().marriage_selector()) << "',\n"
	    << "  but the bug report says '" << this->line << "'"
	    << endl;
	  this->inconsistent();
	} // if (this->line != ::name(this->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 (this->game().soloplayer().no()
	    != static_cast<unsigned>(atoi(this->line.c_str()))) {
	  cerr << "BugReportReplay:\n"
	    << "  bride is '" << ::name(this->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 (this->game().soloplayer().no() != soloplayerno) {
	  cerr << "BugReportReplay:\n"
	    << "  soloplayer is '" << this->game().soloplayer().no() << "',\n"
	    << "  but the bug report says '" << soloplayerno << "'"
	    <<endl;
	  this->inconsistent();
	} // if (soloplayer().no() != this->line)
	break;
      } // switch(this->game().type())

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

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


	for (vector<Player*>::const_iterator
	     player = this->game().players_begin();
	     player != this->game().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

	  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 this->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();

      return ;
    } // void BugReportReplay::game_start()

  /**
   ** the human 'player' has to play a card
   **
   ** @param	player	the player, who has to play a card
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **/
  void
    BugReportReplay::card_get(Player const& player)
    { 
      if (this->line_human == "}") {
	this->finished();
	return ;
      } // 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

      return ;
    } // 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
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **/
  void
    BugReportReplay::card_played(HandCard const& card)
    { 
      if (this->keyword() != "Played") {
	cerr << "BugReportReplay:\n"
	  << "  a card should be played, but got:\n"
	  << "  " << this->line << endl;
	this->inconsistent();
	GETLINE;
	this->card_played(card);

	return ;
      } // 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 != card.player().no()) {
	cerr << "BugReportReplay:\n"
	  << "  Player '" << card.player().no() << "' has played the card,\n"
	  << "  the bug report says player '" << playerno << "'"
	  << endl;
	this->inconsistent();
      } // if (playerno != card.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();

      return ;
    } // void BugReportReplay::card_played(HandCard card)


  /**
   ** Test for an announcement and make it, if the player is human
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **/
  bool
    BugReportReplay::test_for_announcement()
    { 
      if (this->keyword() != "Announcement")
	return 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 (this->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)

      return 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.6.7
   **
   **/
  void
    BugReportReplay::announcement_made(Announcement const& announcement,
				       Player const& player)
    {
      if (this->keyword() != "Announcement") {
	cerr << "BugReportReplay:\n"
	  << "  Announcement should be made, but got:\n"
	  << "  " << this->line << endl;
	this->inconsistent();

	return ;
      } // 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();

      return ;
    } // 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	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **/
  void
    BugReportReplay::swines_announced(Player const& player)
    {
#ifdef POSTPHONED
      if (this->keyword() != "Swines")
	return ;
#else
      if (this->keyword() != "Swines") {
	cerr << "BugReportReplay:\n"
	  << "  Swines should be announced, but got:\n"
	  << "  " << this->line << endl;
	this->inconsistent();

	return ;
      } // 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();

      return ;
    } // 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.6.7
   **
   ** @author	Diether Knof
   **
   **/
  void
    BugReportReplay::hyperswines_announced(Player const& player)
    {
#ifndef POSTPHONED
      if (this->keyword() != "Hyperswines")
	return ;
#else
      if (this->keyword() != "Hyperswines") {
	cerr << "BugReportReplay:\n"
	  << "  Hyperswines should be announced, but got:\n"
	  << "  " << this->line << endl;
	this->inconsistent();

	return ;
      } // 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();


      return ;
    } // 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.6.7
   **
   **/
  void
    BugReportReplay::poverty_shift(Player const& player, unsigned const cardno)
    {
      if (this->keyword() != "Poverty") {
	cerr << "BugReportReplay:\n"
	  << "  keyword 'Poverty' expected, but got:\n"
	  << "  " << this->line << endl;
	this->inconsistent();

	return ;
      } // 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)

      HandCards shifted_cards(*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
	HandCards const shifted_cards(*this->istr_human);

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

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

      return ;
    } // 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.6.7
   **
   ** @todo	all
   **
   **/
  void
    BugReportReplay::poverty_ask(Player const& player, unsigned const 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();

	  return ;
	} // 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
	  HandCards const returned_cards(*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)

      return ;
    } // void BugReportReplay::poverty_ask(Player const& player, unsigned const cardno)

  /**
   **
   ** all players have denied to take the cards
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **
   **/
  void
    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;

      return ;
    } // 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.6.7
   **
   **/
  void
    BugReportReplay::poverty_take_accepted(Player const& player,
					   unsigned const cardno,
					   unsigned const trumpno)
    {
      if (this->keyword() != "Poverty") {
	cerr << "BugReportReplay:\n"
	  << "  keyword 'Poverty' expected, but got:\n"
	  << "  " << this->line << endl;
	this->inconsistent();

	return ;
      } // 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
      HandCards const returned_cards(*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;

      return ;
    } // 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.6.7
   **
   **/
  void
    BugReportReplay::genscher_partner()
    {
      if (this->keyword_human() != "Genscher") {
	cerr << "BugReportReplay: Human\n"
	  << "  Genscher expected, but got:\n"
	  << "  " << this->line_human << endl;
	this->inconsistent();

	return ;
      } // if (this->keyword_human() != "Genscher")

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

      return ;
    } // 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.6.7
   **
   **/
  void
    BugReportReplay::genscher(Player const& player,
			      Player const& partner)
    {
      if (this->keyword() != "Genscher") {
	cerr << "BugReportReplay:\n"
	  << "  Genscher expected, but got:\n"
	  << "  " << this->line << endl;
	this->inconsistent();

	return ;
      } // 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();

      return ;
    } // void BugReportReplay::genscher(Player const& player, Player const& partner)

  /**
   **
   ** a new trick is opened
   **
   ** @param	trick	trick that is opened
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **
   **/
  void
    BugReportReplay::trick_open(Trick const& trick)
    {
      this->OS::trick_open(trick);

      this->test_for_announcement();

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

      return ;
    } // void BugReportReplay::trick_open(Trick const& trick)

  /**
   **
   ** the trick 'trick' is full
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.7
   **
   **/
  void
    BugReportReplay::trick_full()
    {
#ifndef POSTPHONED
      // till Version 0.6.0
      EXPECT_KEYWORD("Trick full");
#else
      EXPECT_LINE("Trick full: "
		  + DK::Utils::String::to_string(this->game().trick_current_no()));
#endif
      Trick trick_br(*(this->istr));
      if (trick_br != this->trick()) {
	cerr << "BugReportReplay:\n"
	  << "  Full trick not valid:\n"
	  << "Game:\n"
	  << this->trick()
	  << "BugReport:\n"
	  << trick_br
	  << endl;
	this->inconsistent();
      } // if (trick_br != trick)

      GETLINE;
      this->test_for_announcement();

      return ;
    } // void BugReportReplay::trick_full()

  /**
   ** -> result
   **
   ** @param	player	player
   **
   ** @return	the next card played by the player
   **
   ** @author	Diether Knof
   **
   ** @version	0.6.8
   **
   **/
  HandCard
    BugReportReplay::next_card(Player const& player) const
    {
      if (this->keyword() != "Played") {
	cerr << "BugReportReplay::next_card()\n"
	  << "  a card should be played, but got:\n"
	  << "  " << this->line << endl;

	return HandCard();
      } // if (this->keyword() != "played")

      string line = this->line;
      DK::Utils::String::word_first_remove_with_blanks(line);
      // the next word is 'player', remove it also
      DK::Utils::String::word_first_remove_with_blanks(line);
      // next comes the player number, followed by a ':'
      unsigned const playerno = static_cast<unsigned>(atoi(line.c_str()));
      if (playerno != player.no()) {
	cerr << "BugReportReplay::next_card():\n"
					       << "  Player '" << player.no() << "' askes for a card to play,\n"
						 << "  but it is the turn of player '" << playerno << "'"
						 << endl;
	return HandCard();
      } // if (playerno != card.player().no())
      // remove the player number
      DK::Utils::String::word_first_remove_with_blanks(line);

      // the last part is the card
      HandCard card = HandCard(player.hand(), Card(line));
      if (!card) {
	cerr << "BugReportReplay::next_card():\n"
					       << "  Player '" << player.no() << "' has no card to play.\n"
						 << "  line: " << this->line;
      }

      return card;
    } // HandCard BugReportReplay::next_card(Player player) const

  /**
   ** reads the whole file
   **
   ** @param	-
   **
   ** @return	-
   **
   ** @author	Diether Knof
   **
   ** @version	0.7.1
   **
   ** @todo	game summary
   **/
  void
    BugReportReplay::read_file()
    {
      ifstream istr(this->filename.c_str());
      string& line = this->line;

#ifndef OUTDATED
#ifdef GETLINE
#undef GETLINE
#endif
#endif

#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");

      // reads the variable from 'this->value()'
#define READ_FROM_VALUE(var)  \
      { \
	istringstream istr(this->value()); \
	istr >> var; \
      }

      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->inconsistent();
	      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;
	      string const keyword = this->keyword();

	      if (line == "") {
	      } else if (line == "/--------\\") {
		break;
	      } else if (keyword == "Version") {
		this->version = DK::Utils::new_Version(this->value());
		if (!this->version) {
		  cerr << "BugReportReplay: "
		    << "could not read version: '" << this->value() << "'\n"
		    << "ignoring it." << endl;

		}
		if (this->version
		    && (*this->version
			< DK::Utils::Version(0, 6, 3,
					     DK::Utils::Date(2004, 5, 5))) ) {
		  cerr << "BugReportReplay: Too old version "
		    << *this->version
		    << " < 0.6.3  (2004-05-05)"
		    << endl;
#ifndef DKNOF
		  exit(0);
#endif
		}
		::RAND_OLD = (this->version
			      && (*this->version
				  < DK::Utils::Version(0, 6, 6,
						       DK::Utils::Date(2004, 8, 29))) );
	      } else if (keyword == "Compiled") {
		this->compiled = this->value();
	      } else if (keyword == "System") {
		this->system = this->value();
	      } else if (keyword == "Time") {
		this->time = this->value();
	      } else if (keyword == "Language") {
		this->language = this->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_(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
	  { // poverty
	    string const keyword = this->keyword();
	    if (keyword == "Poverty") {
	      // ToDo

	      return ;


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

	      this->poverty_cards_shifted = new HandCards(istr);
	      this->actions.push_back(new GameplayAction::PovertyShift(this->soloplayer_no, *this->poverty_cards_shifted));

	      this->poverty_cards_returned = new HandCards(istr);
	      // ToDo: insert the player who has returned the cards
	      this->actions.push_back(new GameplayAction::PovertyReturned(this->soloplayer_no, *this->poverty_cards_returned));
	    } // if (keyword == "Poverty")

	  } // poverty
	  GETLINE;
	  { // swines
	    if (this->keyword() == "Swines") {
	      READ_FROM_VALUE(this->swines_player_no);
	      GETLINE;
	      if (this->keyword() == "Hyperswines") {
		READ_FROM_VALUE(this->hyperswines_player_no);
		GETLINE;
	      } // if (this->keyword() == "Hyperswines)
	    } // if (this->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<GameType>(t)) == line) {
		this->game_type = static_cast<GameType>(t);
		COUT << "gametype: " << this->game_type << endl;
		break;
	      }
	    // ToDo: special cases like marriage with selector
	    { // special cases
	      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<unsigned>(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<Player*>::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 (this->keyword() == "Trick")
		continue;
#ifndef OUTDATED
#ifndef OUTDATED
	      if (this->keyword() == "Game summary:") {
		// skip all until 'finished'
		do {
		  GETLINE;
		  // note: in reality when 'Finished' is reached by 'GETLINE'
		  //       an exception is thrown
		} while (this->keyword() != "Finished");
		break;
	      }
#endif
	      if (this->keyword() == "Game summary")
		break;
#endif

	      GameplayAction* const action
		= GameplayAction::new_(line, istr);
	      if (!action) {
		cerr << "BugReportReplay:\n"
		  << "  Unknown action '" << line << "'\n"
		  << "  Ignoring it." << endl;
		continue;
	      }

	      this->actions.push_back(action);

	      if (action->type == GameplayAction::TRICK_FULL)
		this->full_tricks.push_back(static_cast<GameplayAction::TrickFull*>(action)->trick);

	    } 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<Player*>::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;

#ifndef OUTDATED
	    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);
#else
	    GameplayAction* const action
	      = GameplayAction::new_(line, istr);
#endif

	    if (!action) {
	      cerr << "BugReportReplay:\n"
		<< "  Unknown human action '" << line << "'\n"
		<< "  Ignoring it." << endl;
	      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

#ifdef DKNOF
#if 0
      for (vector< ::GameplayAction*>::const_iterator a = this->actions.begin();
	   a != this->actions.end();
	   ++a)
	COUT << **a << endl;
#endif

      //this->write(COUT);

      COUT << "BugReportReplay::read_file(): klappt" << endl;
      //exit(EXIT_SUCCESS);
#endif

      return ;
    } // void BugReportReplay::read_file()

} // namespace OS_NS
