1 /*********************************************************************
2  *
3  *   FreeDoko a Doppelkopf-Game
4  *
5  *   Copyright (C) 2001 – 2018 by Diether Knof and Borg Enders
6  *
7  *   This program is free software; you can redistribute it and/or
8  *   modify it under the terms of the GNU General Public License as
9  *   published by the Free Software Foundation; either version 2 of
10  *   the License, or (at your option) any later version.
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *   You can find this license in the file 'gpl.txt'.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with this program; if not, write to the Free Software
20  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  *   MA  02111-1307  USA
22  *
23  *  Contact:
24  *    Diether Knof dknof@posteo.de
25  *
26  *********************************************************************/
27 
28 #include "constants.h"
29 
30 #include "bug_report_replay.h"
31 
32 #include "../player/ai/ai.h"
33 #include "../party/party.h"
34 #include "../game/game.h"
35 #include "../game/gameplay_actions.h"
36 #include "../game/gameplay_actions/check.h"
37 #include "../card/trick.h"
38 
39 #include "../ui/ui.h"
40 
41 #include "../misc/preferences.h"
42 #include "../misc/references_check.h"
43 
44 #include "../utils/string.h"
45 #include "../utils/file.h"
46 
47 #include <sstream>
48 #include <chrono>
49 
50 
51 // There can only be one 'bug report replay' instance.
52 // If a new is created, the old is removed.
53 unique_ptr<OS_NS::BugReportReplay> bug_report_replay;
54 
55 
56 namespace OS_NS {
57 
58 Signal<void(BugReportReplay&)> BugReportReplay::signal_open_;
59 
60 // returns the value from the line 'line'
61 static string get_value(string const& line);
62 // returns the keyword from the line 'line'
63 static string get_keyword(string const& line);
64 
65 
66 /** -> result
67  **
68  ** @param    line   the line
69  **
70  ** @return   the value from the given line
71  **/
72 string
get_value(string const & line)73   get_value(string const& line)
74   {
75     if (line.empty())
76       return "";
77 
78     string value = line;
79     String::word_first_remove_with_blanks(value);
80 
81     return value;
82   } // static string get_value(string line)
83 
84 /** -> result
85  **
86  ** @param    line   the line
87  **
88  ** @return   the keyword from the given line
89  **/
90 string
get_keyword(string const & line)91   get_keyword(string const& line)
92   {
93     if (line.empty())
94       return "";
95 
96     string const word
97       = (  (std::find(line.begin(), line.end(), ':')
98             != line.end())
99          ? string(line.begin(),
100                   std::find(line.begin(), line.end(), ':'))
101          : line
102         );
103 
104     return word;
105   } // static string get_keyword(string line)
106 
107 /** Constructor
108  ** (to be called from a child class)
109  **
110  ** @param    filename   the file with the bug report
111  **/
BugReportReplay(string const & filename,unsigned verbose)112 BugReportReplay::BugReportReplay(string const& filename,
113                                  unsigned verbose) :
114   filename_(filename),
115   verbose_(verbose)
116 {
117   if (!File::isfile(filename)) {
118     cerr << "Selected bug report '" << filename << "' is no file." << endl;
119     return ;
120   } // if (!File::isfile(filename))
121 
122   try {
123     this->read_file();
124   } catch (ReadException const& read_exception) {
125     return ;
126   }
127 
128   this->init();
129 
130   this->print_header();
131 } // BugReportReplay::BugReportReplay(string filename)
132 
133 /** Destructor
134  **/
~BugReportReplay()135 BugReportReplay::~BugReportReplay()
136 {
137   this->signal_close()();
138 } // BugReportReplay::~BugReportReplay()
139 
140 /** @return   signal when the party is opened
141  **/
142 Signal<void(BugReportReplay&)>&
signal_open()143 BugReportReplay::signal_open()
144 { return BugReportReplay::signal_open_; }
145 
146 /** @return   signal: the party is opened
147  **/
148 Signal<void()>&
signal_close()149 BugReportReplay::signal_close()
150 { return this->signal_close_; }
151 
152 
153 /** initializes the bug report
154  **/
155 void
init()156 BugReportReplay::init()
157 {
158   Party::signal_open().connect(*this, &BugReportReplay::party_open, this->disconnector_);
159   Game::signal_open().connect(*this, &BugReportReplay::game_open, this->disconnector_);
160 
161   ::preferences.set(Preferences::Type::save_party_changes, false);
162   ::preferences.set(Preferences::Type::additional_party_settings, true);
163   this->check_action_ = false;
164 
165   if (::game_status == GameStatus::party_new) {
166     this->BugReportReplay::party_open(*::party);
167     this->BugReportReplay::party_get_settings();
168   }
169 
170 #ifdef POSTPONED
171   // is called in 'party_open' so that this party is set
172   // (-> UI_GTKMM::BugReportReplay::update_info)
173   ::ui->bug_report_replay_open(*this);
174 #endif
175 } // void BugReportReplay::init()
176 
177 /** write the data of this bug report into 'ostr'
178  **
179  ** @param    ostr   output stream
180  **/
181 void
write(ostream & ostr) const182 BugReportReplay::write(ostream& ostr) const
183 {
184   ostr << "file: "      << this->filename_ << '\n';
185   if (this->version_)
186     ostr << "version: " << *this->version_ << '\n';
187   else
188     ostr << "version: -\n";
189   ostr << "compiled: "  << this->compiled_ << '\n';
190   ostr << "system: "    << this->system_ << '\n';
191   ostr << "time: "      << this->time_ << '\n';
192   ostr << "language: "  << this->language_ << '\n';
193   ostr << "trickno: "   << this->trickno_ << '\n';
194 
195   ostr << '\n';
196 
197   ostr << "message\n"
198     << "{\n"
199     << this->message_
200     << "}\n";
201 
202   ostr << '\n';
203 
204   ostr << "seed: "              << this->seed_ << '\n';
205   ostr << "startplayer_no: "    << this->startplayer_no_ << '\n';
206   ostr << "game_type: "         << this->game_type_ << '\n';
207   ostr << "marriage_selector: " << this->marriage_selector_ << '\n';
208   ostr << "soloplayer_no: "     << this->soloplayer_no_ << '\n';
209 
210   ostr << '\n';
211 
212   ostr << "rule\n"
213     << "{\n"
214     << this->rule_
215     << "}\n";
216 
217   ostr << '\n';
218 
219   ostr << "persons\n"
220     << "{\n";
221   for (auto const& p : this->persons_)
222     ostr << "{\n" << *p << "}\n";
223   ostr << "}";
224 
225   ostr << '\n';
226 
227   if (!this->poverty_cards_shifted_.empty()) {
228     ostr << "poverty cards shifted\n"
229       << "{\n";
230     for (auto const c : this->poverty_cards_shifted_)
231       ostr << c << '\n';
232     ostr << "}\n";
233   } else {
234     ostr << "poverty cards shifted: -\n";
235   }
236 
237   if (!this->poverty_cards_returned_.empty()) {
238     ostr << "poverty cards returned\n"
239       << "{\n";
240     for (auto const c : this->poverty_cards_returned_)
241       ostr << c << '\n';
242     ostr << "}\n";
243   } else {
244     ostr << "poverty cards returned: -\n";
245   }
246 
247   ostr << '\n';
248 
249   ostr << "hands\n"
250     << "{\n";
251   for (auto const& h : this->hands_)
252     ostr << "{\n" << h << "}\n";
253   ostr << "}\n";
254 
255   ostr << '\n';
256 
257   ostr << "swines: ";
258   if (this->swines_player_no_ == UINT_MAX)
259     ostr << '-';
260   else
261     ostr << this->swines_player_no_;
262   ostr << '\n';
263   ostr << "hyperswines: ";
264   if (this->hyperswines_player_no_ == UINT_MAX)
265     ostr << '-';
266   else
267     ostr << this->hyperswines_player_no_;
268   ostr << '\n';
269 
270 
271   ostr << '\n';
272 
273   ostr << "game actions\n"
274     << "{\n";
275   for (auto const& a : this->actions_)
276     ostr << *a << '\n';
277   ostr << "}\n";
278 
279   ostr << '\n';
280 
281   ostr << "full tricks\n"
282     << "{\n";
283   for (auto const& t : this->full_tricks_)
284     ostr << t << '\n';
285   ostr << "}\n";
286 
287   ostr << '\n';
288 
289   ostr << "game summary\n"
290     << "{\n";
291   if (this->game_summary_)
292     ostr << *this->game_summary_;
293   ostr << "}\n";
294 
295   ostr << '\n';
296 
297   ostr << "current hands\n"
298     << "{\n";
299   for (auto const& h : this->current_hands_)
300     ostr << "{\n" << h << "}\n";
301   ostr << "}\n";
302 
303   ostr << '\n';
304 
305   ostr << "current trick\n"
306     << this->current_trick_;
307 
308   ostr << '\n';
309 
310   ostr << "human actions\n"
311     << "{\n";
312   for (auto const& a : this->human_actions_)
313     ostr << *a << '\n';
314   ostr << "}\n";
315 } // void BugReportReplay::write(ostream& ostr) const
316 
317 /** test, whether the line is the expected
318  **
319  ** @param    read_line       line that was read
320  ** @param    expected_line   line that is read
321  **
322  ** @return   whether the line is the expected
323  **/
324 bool
expect_line(string const & read_line,string const & expected_line)325 BugReportReplay::expect_line(string const& read_line,
326                              string const& expected_line)
327 {
328   if (read_line != expected_line) {
329     cerr << "BugReportReplay:\n"
330       << "  expected line '" << expected_line << "', "
331       << "got: '" << read_line << "'"
332       << "\n";
333 
334     this->mismatch();
335     return false;
336   } // if (read_line != expected_line)
337 
338   return true;
339 } // bool BugReportReplay::expect_line(string read_line, string expected_line)
340 
341 /** test, whether the keyword is the expected
342  **
343  ** @param    read_keyword       keyword that was read
344  ** @param    expected_keyword   keyword that is read
345  **
346  ** @return   whether the keyword is the expected
347  **/
348 bool
expect_keyword(string const & read_keyword,string const & expected_keyword)349 BugReportReplay::expect_keyword(string const& read_keyword,
350                                 string const& expected_keyword)
351 {
352   if (read_keyword != expected_keyword) {
353     cerr << "BugReportReplay:\n"
354       << "  expected keyword '" << expected_keyword << "', "
355       << "got: '" << read_keyword << "'"
356       << "\n";
357 
358     this->mismatch();
359     return false;
360   } // if (read_keyword != expected_keyword)
361 
362   return true;
363 } // bool BugReportReplay::expect_keyword(string read_keyword, string expected_line)
364 
365 /** reads the whole file
366  **
367  ** @todo   poverty
368  ** @todo   game summary
369  **/
370 void
read_file()371 BugReportReplay::read_file()
372 {
373   cout << "Loading bug report " << this->filename() << endl;
374   ifstream istr(this->filename().c_str());
375   string line;    // the line read
376   string keyword; // the keyword read
377   string value;   // the value read
378 
379 #define GETLINE \
380   do { \
381     if (istr.eof()) { \
382       throw EOF; \
383     } \
384     std::getline(istr, line);  \
385     String::remove_blanks(line); \
386     if ( (line.size() > 0) && (*(line.end() - 1) == '\r') ) \
387     line.erase(line.end() - 1); \
388   } while (line.empty() && istr.good()) ; \
389   keyword = get_keyword(line); \
390   value = get_value(line); \
391 
392   // reads the variable from 'value'
393 #define READ_FROM_VALUE(var)  \
394   { \
395     value = get_value(line); \
396     istringstream istr(value); \
397     istr >> var; \
398   }
399 
400   // expect the keyword
401   // if it is not valid, return from the function
402 #define EXPECT_LINE(expected_line) \
403   if (true) { \
404     if (!this->expect_line(line, expected_line)) \
405     throw ReadException("expected line '" + string(line) + "'") ; \
406   } else
407 #if 0
408   ;
409 #endif
410 
411 #define EXPECT_KEYWORD(expected_keyword) \
412   if (true) { \
413     if (!this->expect_keyword(keyword, expected_keyword)) \
414     throw ReadException("expected keyword '" + string(keyword) + "'") ; \
415   } else
416 #if 0
417   ;
418 #endif
419 
420   try {
421     this->human_finished_ = true;
422     { // read the first lines (including message)
423       // the first line
424       GETLINE;
425       // Byte Order Mark for UTF-8
426       string bom = "...";
427       bom[0] = 0xef;
428       bom[1] = 0xbb;
429       bom[2] = 0xbf;
430 
431       if (   (line != "# FreeDoko Bugreport")
432           && (line != bom + "# FreeDoko Bugreport")
433           && (line != "# FreeDoko Reference")
434           && (line != bom + "# FreeDoko Reference") ) {
435         cerr << "BugReportReplay::read_first_lines()\n"
436           << "  The file is no bug report! First line does not match. Expecting\n"
437           << "# FreeDoko Bugreport\n"
438           << "  or\n"
439           << "# FreeDoko Reference\n"
440           << "  but got\n"
441           << line
442           << endl;
443         this->mismatch();
444         return ;
445       } // if (line != "# FreeDoko Bugreport")
446 
447       // read and print the next informations
448       // Example:
449       // Version: 0.6.7b  (2004-12-19)
450       // Compiled: Dec 19 2004, 19:36:18
451       // System: Windows
452       // Time: Fri Feb 25 22:55:22 2005
453       //
454       // Language: de
455       // Trick: 9
456       do { // while(!line.empty());
457         GETLINE;
458 
459         if (line == "") {
460         } else if (line == "/---------\\") {
461           break;
462         } else if (keyword == "version") {
463           auto version = Version::create(value);
464           if (version)
465             this->version_ = make_unique<Version>(*version);
466           else
467             this->version_ = {};
468           if (!this->version_) {
469             cerr << "BugReportReplay: "
470               << "could not read version: '" << value << "'\n"
471               << "ignoring it." << endl;
472 
473           }
474           if (   this->version_
475               && (*this->version_ <= Version(0, 6, 6)) ) {
476             cerr << "BugReportReplay: Too old version "
477               << *this->version_
478               << " < 0.6.6"
479               << endl;
480 #ifndef DKNOF
481             exit(EXIT_SUCCESS);
482 #endif
483           }
484         } else if (keyword == "compiled") {
485           this->compiled_  = value;
486         } else if (keyword == "system") {
487           this->system_    = value;
488         } else if (keyword == "time") {
489           this->time_      = value;
490         } else if (keyword == "language") {
491           this->language_  = value;
492         } else if (keyword == "trick") {
493           READ_FROM_VALUE(this->trickno_);
494         } else { // if !(keyword == ...)
495           cerr << "BugReportReplay: "
496             << "unknown keyword '" << keyword << "'\n"
497             << "ignoring it." << endl;
498         } // if !(keyword == ...)
499 
500       } while(line != "/---------\\");
501 
502       { // read the message
503 
504         // the header exists of three lines -- ignore them
505         GETLINE;
506         GETLINE;
507 
508         this->message_.clear();
509         do { // while (line != "/---------\\")
510           GETLINE;
511           if (line != "/--------\\")
512             this->message_ += "  " + line + "\n";
513 
514           if (istr.fail()) {
515             cerr << "BugReportReplay::read_first_lines()\n"
516               << "  (unexpected) Error reading the first lines.\n"
517               << "Aborting."
518               << endl;
519             exit(EXIT_FAILURE);
520           } // if (istr.fail())
521         } while(line != "/--------\\") ;
522 
523         // the foot exists of three lines -- ignore them (one is already ignored)
524         GETLINE;
525         GETLINE;
526       } // read the message
527       GETLINE;
528     } // read the first lines
529     if (line == "finished") {
530       throw EOF;
531     } // if (line == "finished")
532     { // seed, startplayer
533       EXPECT_KEYWORD("seed");
534       READ_FROM_VALUE(this->seed_);
535 
536       GETLINE;
537       EXPECT_KEYWORD("startplayer");
538       READ_FROM_VALUE(this->startplayer_no_);
539     } // seed, startplayer
540     { // rules, players
541       GETLINE;
542 
543       if (line == "party") {
544         EXPECT_LINE("party");
545         // a '{'
546         GETLINE;
547         EXPECT_LINE("{");
548         ::party->read(istr);
549         this->persons_.clear();
550         for (auto const& p : ::party->players())
551           this->persons_.push_back(p.clone());
552         GETLINE;
553         this->rule_ = ::party->rule();
554         EXPECT_LINE("}");
555 #ifdef WORKAROUND
556         // ToDo: this shouldn't be necessary
557         ::party->set_seed(this->seed_);
558 #endif
559       } else { // if !(line == "party")
560         EXPECT_LINE("rules");
561         this->rule_.read(istr);
562         // set the rules here, so that the default ai types see the setting of with_nines
563         this->set_party_rule();
564 
565         GETLINE;
566         EXPECT_LINE("players");
567 
568         // a '{'
569         GETLINE;
570         EXPECT_LINE("{");
571 
572         while (istr.peek() != '}') {
573           this->persons_.push_back(unique_ptr<Player>(Player::create(istr)));
574           if (   !istr.good()
575               || !this->persons_.back()) {
576             cerr << "BugReportReplay::read_file()\n"
577               << "  could not load player.\n"
578               << "Aborting."
579               << endl;
580             exit(EXIT_FAILURE);
581           } // if (!this->players.back())
582           while ((istr.peek() == '\n')
583                  || (istr.peek() == '\r'))
584             istr.get();
585         } // while (istr.peek() != '}')
586 
587         GETLINE;
588         EXPECT_LINE("}");
589       } // if !(line == "party")
590     } // players, players
591     { // gametype
592       GETLINE;
593       if (keyword == "hands") {
594         this->game_type_ = GameType::normal;
595       } else { // if !(keyword == "hands")
596         EXPECT_KEYWORD("gametype");
597         String::word_first_remove_with_blanks(line);
598         this->game_type_ = game_type_from_string(line);
599         { // special cases
600           if (   is_solo(this->game_type_)
601               || (this->game_type_ == GameType::thrown_nines)
602               || (this->game_type_ == GameType::thrown_kings)
603               || (this->game_type_ == GameType::thrown_nines_and_kings)
604               || (this->game_type_ == GameType::thrown_richness)
605               || (this->game_type_ == GameType::fox_highest_trump) ) {
606             GETLINE;
607             EXPECT_KEYWORD("soloplayer");
608             String::word_first_remove_with_blanks(line);
609             String::word_first_remove_with_blanks(line);
610             this->soloplayer_no_
611               = static_cast<unsigned>(stoi(line));
612           }
613           if (this->game_type_ == GameType::marriage) {
614             // read the selector and the bride
615             GETLINE;
616             EXPECT_KEYWORD("selector");
617             String::word_first_remove_with_blanks(line);
618             this->marriage_selector_ = marriage_selector_from_string(line);
619 
620             GETLINE;
621             EXPECT_KEYWORD("bride");
622             String::word_first_remove_with_blanks(line);
623             String::word_first_remove_with_blanks(line);
624             this->soloplayer_no_ = static_cast<unsigned>(stoi(line));
625           } // if (this->game_type() == GameType::marriage)
626           if (this->game_type_ == GameType::poverty) {
627             // ToDo
628           } // if (this->game_type == GameType::poverty)
629         } // special cases
630       } // if !(keyword == "hands")
631     } // gametype
632     { // hands
633       if (keyword != "hands")
634         GETLINE;
635       EXPECT_LINE("hands");
636       GETLINE;
637       EXPECT_LINE("{");
638 
639       for (auto const& person : this->persons_) {
640         (void)person;
641         // the player number
642         GETLINE;
643         this->hands_.emplace_back(istr);
644       } // for (player : this->game().player)
645       // a '}'
646       GETLINE;
647       EXPECT_LINE("}");
648     } // hands
649     { // actions, tricks
650       GETLINE;
651       EXPECT_LINE("gameplay actions");
652       GETLINE;
653       EXPECT_LINE("{");
654       do { // while (line != "}")
655         /* Format for a trick:
656          *
657          * Trick: 0
658          * Played: Player 0: spade ace
659          * Played: Player 1: spade nine
660          * Played: Player 2: spade ace
661          * Played: Player 3: spade nine
662          *
663          * Trick full: 1
664          * Startplayer: 0
665          * 0: spade ace
666          * 1: spade nine
667          * 2: spade ace
668          * 3: spade nine
669          * Winner: 0
670          */
671 
672         GETLINE;
673 
674         if (line == "}") {
675           break;
676         } else if (line[0] == '#') {
677           continue;
678         } else if (line == "stop") {
679           this->auto_action_end_ = this->actions_.size();
680           this->auto_start_party_ = true;
681           continue;
682         }
683 
684         auto action = GameplayAction::create(line, istr);
685         if (!action) {
686           cerr << "BugReportReplay:\n"
687             << "  Unknown action '" << line << "'\n"
688             << "  Ignoring it." << endl;
689 #ifdef DKNOF
690           SEGFAULT;
691 #endif
692           continue;
693         }
694 
695 #ifndef POSTPONED
696         if (action->type() == GameplayActions::Type::trick_full)
697           this->full_tricks_.push_back(dynamic_cast<GameplayActions::TrickFull*>(action.get())->trick);
698 #endif
699 #ifndef WORKAROUND
700         if (action->type() == GameplayActions::Type::trick_full) {
701           // Replace 'TrickFull' with 'TrickClosed',
702           // since the trick in 'TrickFull' needs a corresponding game.
703           action = make_unique<GameplayActions::TrickTaken>();
704         } // if (action->type == GameplayAction::trick_full)
705 #endif
706 
707         if (action->type() == GameplayActions::Type::check) {
708           this->auto_action_end_ = this->actions_.size() + 1;
709           this->auto_start_party_ = true;
710         }
711 
712         { // special behaviour in a poverty: save the shifted cards
713           if (action->type() == GameplayActions::Type::poverty_shift) {
714             this->poverty_cards_shifted_
715               = vector<Card>(dynamic_cast<GameplayActions::PovertyShift*>(action.get())->cards);
716           } else if (action->type() == GameplayActions::Type::poverty_returned) {
717             this->poverty_cards_returned_
718               = vector<Card>(dynamic_cast<GameplayActions::PovertyReturned*>(action.get())->cards);
719           } // if (action->type == GameplayAction::poverty_shift)
720         } // special behaviour in a poverty: save the shifted cards
721 
722         this->actions_.push_back(move(action));
723 
724       } while (line != "}");
725     } // actions, tricks
726     { // game summary
727       GETLINE;
728       if (line == "game summary") {
729         EXPECT_LINE("game summary");
730         this->game_summary_ = make_unique<GameSummary>(this->rule_, istr);
731         GETLINE;
732         if (line == "special points") {
733           // Just skip them -- they are contained in the game summary
734           EXPECT_LINE("special points");
735           GETLINE;
736           EXPECT_LINE("{");
737 
738           do {
739             GETLINE;
740           } while (line != "}");
741           GETLINE;
742         } // if (line == "special points")
743       } // if (line == "game summary")
744     } // game summary
745     if (istr.eof())
746       throw EOF;
747     { // current hands
748       EXPECT_LINE("current hands");
749       GETLINE;
750       EXPECT_LINE("{");
751 
752       for (auto const& person : this->persons_) {
753         (void)person;
754         GETLINE;
755         this->current_hands_.emplace_back(istr);
756       } // for (person : this->persons_)
757       // a '}'
758       GETLINE;
759       EXPECT_LINE("}");
760       GETLINE;
761     } // current hands
762     if (istr.eof())
763       throw EOF;
764     { // current trick
765       if (line == "current trick")
766       {
767         this->current_trick_ = Trick(istr);
768         GETLINE;
769       }
770     } // current trick
771     if (istr.eof())
772       throw EOF;
773     { // human actions
774       EXPECT_LINE("human actions");
775       GETLINE;
776       EXPECT_LINE("{");
777       do { // while (line != "}")
778 
779         GETLINE;
780         if (line == "}")
781           break;
782 
783         auto action = GameplayAction::create(line, istr);
784 
785         if (!action) {
786           cerr << "BugReportReplay:\n"
787             << "  Unknown human action '" << line << "'\n"
788             << "  Ignoring it." << endl;
789           continue;
790         }
791 
792         this->human_actions_.push_back(std::move(action));
793 
794       } while (line != "}");
795       this->human_finished_ = this->human_actions_.empty();
796     } // human actions
797 
798   } catch (int const status) {
799     if (status == EOF) {
800       // finished
801       // return ;
802     } else {
803       throw;
804     }
805   } catch (ReadException const& read_exception) {
806     cerr << "BugReportReplay::read_file()\n"
807       << "  read exception: " << read_exception.message()
808       << endl;
809     throw;
810   } catch (...) {
811     cerr << "BugReportReplay::read_file()\n"
812       << "  unknown exception" << endl;
813     throw;
814   }
815 
816 #undef GETLINE
817 #undef READ_FROM_VALUE
818 #undef EXPECT_LINE
819 #undef EXPECT_KEYWORD
820 
821   this->check_action_ = false;
822   this->loaded_ = true;
823 } // void BugReportReplay::read_file()
824 
825 /** @return   the current gameplay action
826  **/
827 GameplayAction const&
current_action() const828 BugReportReplay::current_action() const
829 {
830   DEBUG_ASSERTION((this->current_action_no_ < this->actions_.size()),
831                   "BugReportReplay::current_action()\n"
832                   "  no further gameplay action");
833   return *this->actions_[this->current_action_no_];
834 } // GameplayAction BugReportReplay::current_action() const
835 
836 /** @return   the number of remaining actions
837  **/
838 unsigned
remaining_actions() const839 BugReportReplay::remaining_actions() const
840 {
841   return this->actions_.size() - this->current_action_no_;
842 }
843 
844 /** @return   the current gameplay action
845  **/
846 GameplayAction const&
next_action() const847 BugReportReplay::next_action() const
848 {
849   DEBUG_ASSERTION((this->current_action_no_ + 1 < this->actions_.size()),
850                   "BugReportReplay::next_action()\n"
851                   "  no further gameplay action");
852   return *this->actions_[this->current_action_no_ + 1];
853 }
854 
855 /** the current action has been processed
856  **
857  ** @param    action        processed action
858  ** @param    discrepancy   discrepancy of the processed and the saved action
859  **/
860 void
action_processed(GameplayAction const & action,GameplayActions::Discrepancy const discrepancy)861 BugReportReplay::action_processed(GameplayAction const& action,
862                                   GameplayActions::Discrepancy const
863                                   discrepancy)
864 {
865   if (this->finished_)
866     return ;
867 
868   // check the action when requested
869   if (   this->check_action_
870       && (this->current_action().type() != GameplayActions::Type::check) ) {
871     DEBUG_ASSERTION(this->actions_[this->current_action_no_ - 1]->type()
872                     == GameplayActions::Type::check,
873                     "BugReportReplay::action_processed(discrepancy)\n"
874                     "  last action is not 'check'");
875     this->reference_check_game_action(action, discrepancy);
876     this->check_action_ = false;
877   } // if (this->check_action_)
878 
879   // test for a human action
880   if (!this->human_finished_
881       && this->current_action() == this->current_human_action()) {
882     this->human_actions_discrepancies_.push_back(discrepancy);
883     this->current_human_action_no_ += 1;
884     if (this->current_human_action_no_ == this->human_actions_.size()) {
885       this->human_finished_ = true;
886     }
887   } // if (human action)
888 
889   // save the discrepancy and go to the next action
890   this->actions_discrepancies_.push_back(discrepancy);
891   this->current_action_no_ += 1;
892 
893   if (this->current_action_no_ == this->actions_.size()) {
894     this->end_reached();
895     return ;
896   }
897 
898   // automatic actions for the human player
899   if (::game_status >= GameStatus::game_play)
900     this->handle_current_human_action();
901 
902   if (this->finished_)
903     return ;
904 
905   // test for printing to perform
906   if (this->current_action().type() == GameplayActions::Type::print) {
907     if (   (::game_status >= GameStatus::game_poverty_shift)
908         && (::game_status < GameStatus::game_finished) ) {
909       // in a running game
910       auto const& action = dynamic_cast<GameplayActions::Print const&>(this->current_action());
911       auto const& player = ::party->game().players().current_player();
912       auto ai = dynamic_cast<Ai const*>(&player);
913       if (action.info == "help") {
914         cout << "print teams: print the team information of the ai\n";
915         cout << "print cards: print the cards information of the ai\n";
916         cout << "print time:  print the current time\n";
917       } else if (action.info == "time") {
918         auto time =  std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
919         auto timeinfo = localtime(&time);
920         char s[20];
921         std::strftime(s, 19, "%H:%M:%S", timeinfo); // NOLINT
922         cout << s << '\n'; // NOLINT
923       } else if (ai && (action.info == "teams")) {
924         cout << '\n'
925           << ai->name() << ": "
926           << ai->team_information() << '\n';
927       } else if (ai && (action.info == "cards")) {
928         if (   (::game_status >= GameStatus::game_poverty_shift)
929             && (::game_status < GameStatus::game_finished) ) {
930           cout << '\n'
931             << ai->name() << ": "
932             << ai->cards_information() << '\n';
933         }
934       } else {
935         cout << action.info << '\n';
936       }
937     } // if (game_status ...)
938     this->action_processed(this->current_action(), GameplayActions::Discrepancy::none);
939   } // if (this->current_action().type() == GameplayAction::print)
940 
941   if (this->current_action().type() == GameplayActions::Type::quit) {
942     exit(0);
943   }
944 
945   // test for check to perform
946   if (this->current_action().type() == GameplayActions::Type::check) {
947     this->check_action_ = true;
948     this->action_processed(this->current_action(), GameplayActions::Discrepancy::none);
949   } // if (this->current_action().type() == GameplayAction::check)
950 } // void BugReportReplay::action_processed(GameplayAction action, GameplayAction::Discrepancy discrepancy)
951 
952 /** check the game action of a reference
953  ** Print and save the result.
954  ** End the game, if it is the last check
955  **
956  ** @param    action        processed action
957  ** @param    discrepancy   discrepancy of the processed and the saved action
958  **/
959 void
reference_check_game_action(GameplayAction const & action,GameplayActions::Discrepancy discrepancy)960 BugReportReplay::reference_check_game_action(GameplayAction const& action,
961                                              GameplayActions::Discrepancy
962                                              discrepancy)
963 {
964   DEBUG_ASSERTION(this->current_action_no_ > 0,
965                   "BugreportReplay::reference_check_game_action(action, discrepancy)\n"
966                   "  this is the first action in the bug report replay");
967 
968   DEBUG_ASSERTION((this->actions_[this->current_action_no_ - 1]->type()
969                    == GameplayActions::Type::check),
970                   "BugreportReplay::reference_check_game_action(action, discrepancy)\n"
971                   "  last action is no check");
972   auto const& check_action
973     = dynamic_cast<GameplayActions::Check const&>(*this->actions_[this->current_action_no_ - 1]);
974 
975   if (::references_check)
976     ::references_check->check(::party->game(),
977                               action, this->current_action(),
978                               check_action.comment,
979                               discrepancy,
980                               (this->auto_action_end_
981                                == this->current_action_no_) );
982 } // void BugReportReplay::reference_check_game_action(GameplayAction action, GameplayAction::Discrepancy discrepancy)
983 
984 /** -> result
985  **
986  ** @param    action_no   number of the action
987  **
988  ** @return   the discrepancy for the given action
989  **/
990 GameplayActions::Discrepancy
discrepancy(unsigned const action_no) const991 BugReportReplay::discrepancy(unsigned const action_no) const
992 {
993   if (action_no >= this->actions_discrepancies_.size())
994     return GameplayActions::Discrepancy::future;
995 
996   return this->actions_discrepancies_[action_no];
997 } // Discrepancy BugReportReplay::discrepancy(unsigned action_no) const
998 
999 /** @return   the current human action
1000  **/
1001 GameplayAction const&
current_human_action() const1002 BugReportReplay::current_human_action() const
1003 {
1004   DEBUG_ASSERTION((this->current_human_action_no_ < this->human_actions_.size()),
1005                   "BugReportReplay::current_human_action()\n"
1006                   "  no further human action");
1007   return *this->human_actions_[this->current_human_action_no_];
1008 } // GameplayAction BugReportReplay::current_human_action() const
1009 
1010 /** -> result
1011  **
1012  ** @param    action_no   number of the human action
1013  **
1014  ** @return   the discrepancy for the given human action
1015  **/
1016 GameplayActions::Discrepancy
human_discrepancy(unsigned const action_no) const1017 BugReportReplay::human_discrepancy(unsigned const action_no) const
1018 {
1019   if (action_no >= this->human_actions_discrepancies_.size())
1020     return GameplayActions::Discrepancy::future;
1021 
1022   return this->human_actions_discrepancies_[action_no];
1023 } // Discrepancy BugReportReplay::human_discrepancy(unsigned action_no) const
1024 
1025 /** handle the current human action
1026  **/
1027 void
handle_current_human_action()1028 BugReportReplay::handle_current_human_action()
1029 {
1030   if (   this->finished_
1031       || this->human_finished_
1032       || (this->current_action() != this->current_human_action()) )
1033     return ;
1034 
1035   // print the coming action
1036   this->print_current_human_action();
1037 
1038   auto& game = ::party->game();
1039 
1040   // partly execute the action automatically
1041   switch (this->current_human_action().type()) {
1042   case GameplayActions::Type::announcement: {
1043     auto const& announcement_action
1044       = dynamic_cast<GameplayActions::Announcement const&>(this->current_human_action());
1045     if (this->verbose_ & verbose_action)
1046       cout << "BugReportReplay:\n"
1047         << "  making announcement '" << announcement_action.announcement << "'"
1048         << " for player " << announcement_action.player
1049         << endl;
1050 
1051     // make the announcement in place of the human and tell it the user
1052     if (!is_reply(announcement_action.announcement))
1053       game.announcements().make_announcement(announcement_action.announcement,
1054                                              game.player(announcement_action.player));
1055   } break;
1056 
1057   case GameplayActions::Type::swines: {
1058     auto const& swines_action
1059       = dynamic_cast<GameplayActions::Swines const&>(this->current_human_action());
1060     if (this->verbose_ & verbose_action)
1061       cout << "BugReportReplay:\n"
1062         << "  announce 'swines' for human player "
1063         << swines_action.player
1064         << endl;
1065     if (!game.swines().swines_announce(game.player(swines_action.player)))
1066       cerr << "Error announcing 'swines' for human player"
1067         << swines_action.player
1068         << endl;
1069   } break;
1070 
1071   case GameplayActions::Type::hyperswines: {
1072     auto const& hyperswines_action
1073       = dynamic_cast<GameplayActions::Hyperswines const&>(this->current_human_action());
1074     if (this->verbose_ & verbose_action)
1075       cout << "BugReportReplay:\n"
1076         << "  announce 'hyperswines' for human player "
1077         << hyperswines_action.player
1078         << endl;
1079     if (!game.swines().hyperswines_announce(game.player(hyperswines_action.player)))
1080       cerr << "Error announcing 'hyperswines' for human player"
1081         << hyperswines_action.player
1082         << endl;
1083   } break;
1084 
1085   default:
1086     break;
1087   } // switch (this->current_human_action().type())
1088 } // void BugReportReplay::handle_current_human_action()
1089 
1090 /** print the next human action
1091  **/
1092 void
print_current_human_action() const1093 BugReportReplay::print_current_human_action() const
1094 {
1095   if (!(this->verbose_ & verbose_human_action))
1096     return ;
1097   switch (this->current_human_action().type()) {
1098   case GameplayActions::Type::card_played: {
1099     auto const& card_played_action
1100       = dynamic_cast<GameplayActions::CardPlayed const&>(this->current_human_action());
1101     cout << "BugReportReplay: Human\n"
1102       << "  play " << card_played_action.card
1103       << endl;
1104   } break;
1105 
1106   case GameplayActions::Type::reservation: {
1107     auto const& reservation_action
1108       = dynamic_cast<GameplayActions::Reservation const&>(this->current_human_action());
1109     cout << "BugReportReplay: Human\n"
1110       << "  gametype " << reservation_action.reservation.game_type << '\n';
1111     if (reservation_action.reservation.game_type == GameType::marriage)
1112       cout << "  marriage selector " << reservation_action.reservation.marriage_selector <<'\n';
1113     if (reservation_action.reservation.swines)
1114       cout << "  swines\n";
1115     if (reservation_action.reservation.hyperswines)
1116       cout << "  hyperswines\n";
1117   } break;
1118 
1119   case GameplayActions::Type::poverty_shift: {
1120     auto const& poverty_shift_action
1121       = dynamic_cast<GameplayActions::PovertyShift const&>(this->current_human_action());
1122     cout << "BugReportReplay: Human\n"
1123       << "  poverty: shift\n";
1124     for (auto const& c : poverty_shift_action.cards)
1125       cout << "    " << c << '\n';
1126   } break;
1127 
1128   case GameplayActions::Type::poverty_returned: {
1129     auto const& poverty_returned_action
1130       = dynamic_cast<GameplayActions::PovertyReturned const&>(this->current_human_action());
1131     cout << "BugReportReplay: Human\n"
1132       << "  poverty: return\n";
1133     for (auto const& c : poverty_returned_action.cards)
1134       cout << "    " << c << '\n';
1135   } break;
1136 
1137   case GameplayActions::Type::poverty_accepted: {
1138     cout << "BugReportReplay: Human\n"
1139       << "  poverty: accept\n";
1140   } break;
1141 
1142   case GameplayActions::Type::poverty_denied: {
1143     cout << "BugReportReplay: Human\n"
1144       << "  poverty: deny\n";
1145   } break;
1146 
1147   case GameplayActions::Type::poverty_denied_by_all:
1148   case GameplayActions::Type::trick_open:
1149   case GameplayActions::Type::trick_full:
1150   case GameplayActions::Type::trick_taken:
1151   case GameplayActions::Type::marriage:
1152   case GameplayActions::Type::check:
1153   case GameplayActions::Type::print:
1154   case GameplayActions::Type::quit:
1155     // nothing to do
1156     break;
1157 
1158   case GameplayActions::Type::announcement:
1159   case GameplayActions::Type::swines:
1160   case GameplayActions::Type::hyperswines:
1161     // -> action_processed
1162 
1163   case GameplayActions::Type::genscher:
1164     // ToDo
1165     break;
1166   } // switch (this->current_human_action().type())
1167 } // void BugReportReplay::print_current_human_action() const
1168 
1169 /** the bug report is inconsistent:
1170  **/
1171 void
mismatch()1172 BugReportReplay::mismatch()
1173 {
1174   cerr << "BugReport mismatch!" << endl;
1175 } // void BugReportReplay::mismatch()
1176 
1177 /** the bug report is finished.
1178  **/
1179 void
end_reached()1180 BugReportReplay::end_reached()
1181 {
1182   this->finished_ = true;
1183   this->human_finished_ = true;
1184 
1185   if (this->verbose_ & verbose_finish) {
1186     cout << "BugReport: finished" << endl;
1187     cout << "Message:\n"
1188       << "{\n"
1189       << this->message_
1190       << "}"
1191       <<endl;
1192   }
1193 } // void BugReportReplay::end_reached()
1194 
1195 /** prints the header and the differences
1196  **/
1197 void
print_header() const1198 BugReportReplay::print_header() const
1199 {
1200   if (!(this->verbose_ & verbose_header))
1201     return;
1202   // print the first lines
1203   cout << "BugReport\n"
1204     << "\n";
1205 
1206   if (this->version_)
1207     cout << "version:     " << *this->version_  << '\n';
1208   else
1209     cout << "version:     -\n";
1210   cout
1211     << "compiled:    " << this->compiled_        << '\n'
1212     << "system:      " << this->system_          << '\n'
1213     << "time:        " << this->time_            << '\n'
1214     << "language:    " << this->language_        << '\n'
1215     << "seed:        " << this->seed_            << '\n'
1216     << "startplayer: " << this->startplayer_no_  << '\n'
1217     << "trick:       " << this->trickno_         << '\n'
1218     << '\n'
1219     << "message:\n"
1220     << "{\n"
1221     <<   this->message_
1222     << "}\n";
1223 } // void BugReportReplay::print_header() const
1224 
1225 /** @return   the filename
1226  **/
1227 string const&
filename() const1228 BugReportReplay::filename() const
1229 { return this->filename_; }
1230 
1231 /** @return   wether the bug report was loaded successfully
1232  **/
1233 bool
loaded() const1234 BugReportReplay::loaded() const
1235 { return this->loaded_; }
1236 
1237 /** @return   whether the bug report replay is finished
1238  **/
1239 bool
finished() const1240 BugReportReplay::finished() const
1241 { return this->finished_; }
1242 
1243 /** @return   wether the party is automatically started
1244  **/
1245 bool
auto_start_party() const1246 BugReportReplay::auto_start_party() const
1247 { return this->auto_start_party_; }
1248 
1249 /** @return   the version
1250  **/
1251 Version const&
version() const1252 BugReportReplay::version() const
1253 { return *this->version_; }
1254 
1255 /** @return   the compiled string
1256  **/
1257 string const&
compiled() const1258 BugReportReplay::compiled() const
1259 { return this->compiled_; }
1260 
1261 /** @return   the system string
1262  **/
1263 string const&
system() const1264 BugReportReplay::system() const
1265 { return this->system_; }
1266 
1267 /** @return   the time string
1268  **/
1269 string const&
time() const1270 BugReportReplay::time() const
1271 { return this->time_; }
1272 
1273 /** @return   the language string
1274  **/
1275 string const&
language() const1276 BugReportReplay::language() const
1277 { return this->language_; }
1278 
1279 /** @return   the trick number
1280  **/
1281 unsigned
trickno() const1282 BugReportReplay::trickno() const
1283 { return this->trickno_; }
1284 
1285 /** @return   the message
1286  **/
1287 string const&
message() const1288 BugReportReplay::message() const
1289 { return this->message_; }
1290 
1291 /** @return   the game type
1292  **/
1293 GameType
game_type() const1294 BugReportReplay::game_type() const
1295 { return this->game_type_; }
1296 
1297 /** @return   the seed
1298  **/
1299 ::Seed
seed() const1300 BugReportReplay::seed() const
1301 { return this->seed_; }
1302 
1303 /** @return   the number of the startplayer
1304  **/
1305 unsigned
startplayer_no() const1306 BugReportReplay::startplayer_no() const
1307 { return this->startplayer_no_; }
1308 
1309 /** @return   the number of the soloplayer
1310  **/
1311 unsigned
soloplayer_no() const1312 BugReportReplay::soloplayer_no() const
1313 { return this->soloplayer_no_; }
1314 
1315 /** @return   the shifted cards
1316  **/
1317 vector<Card> const&
poverty_cards_shifted() const1318 BugReportReplay::poverty_cards_shifted() const
1319 { return this->poverty_cards_shifted_; }
1320 
1321 /** @return   the returned cards
1322  **/
1323 vector<Card> const&
poverty_cards_returned() const1324 BugReportReplay::poverty_cards_returned() const
1325 { return this->poverty_cards_returned_; }
1326 
1327 /** @return   the hands
1328  **/
1329 vector<Hand> const&
hands() const1330 BugReportReplay::hands() const
1331 { return this->hands_; }
1332 
1333 /** @return   the marriage selector
1334  **/
1335 MarriageSelector
marriage_selector() const1336 BugReportReplay::marriage_selector() const
1337 { return this->marriage_selector_; }
1338 
1339 /** @return   the number of the current action
1340  **/
1341 unsigned
current_action_no() const1342 BugReportReplay::current_action_no() const
1343 { return this->current_action_no_; }
1344 
1345 /** @return   the number of the current human action
1346  **/
1347 unsigned
current_human_action_no() const1348 BugReportReplay::current_human_action_no() const
1349 { return this->current_human_action_no_; }
1350 
1351 /** @return   the game summary
1352  **/
1353 unique_ptr<GameSummary> const&
game_summary() const1354 BugReportReplay::game_summary() const
1355 { return this->game_summary_; }
1356 
1357 /** @return   the actions
1358  **/
1359 vector<unique_ptr<GameplayAction>> const&
actions() const1360 BugReportReplay::actions() const
1361 { return this->actions_; }
1362 
1363 /** @return   the action discrepancies
1364  **/
1365 vector<GameplayActions::Discrepancy> const&
actions_discrepancies() const1366 BugReportReplay::actions_discrepancies() const
1367 { return this->actions_discrepancies_; }
1368 
1369 /** @return   the human actions
1370  **/
1371 vector<unique_ptr<GameplayAction>> const&
human_actions() const1372 BugReportReplay::human_actions() const
1373 { return this->human_actions_; }
1374 
1375 /** @return   the human action discrepancies
1376  **/
1377 vector<GameplayActions::Discrepancy> const&
human_actions_discrepancies() const1378 BugReportReplay::human_actions_discrepancies() const
1379 { return this->human_actions_discrepancies_; }
1380 
1381 /** a party is opened
1382  **
1383  ** @param    party   the party that is opened
1384  **/
1385 void
party_open(Party const & party)1386 BugReportReplay::party_open(Party const& party)
1387 {
1388   ::party->signal_get_settings().connect(*this, &BugReportReplay::party_get_settings, this->disconnector_);
1389   ::party->signal_loaded().connect(*this, &BugReportReplay::party_loaded, this->disconnector_);
1390   ::party->signal_start().connect(*this, &BugReportReplay::party_start, this->disconnector_);
1391 
1392   this->finished_ = false;
1393   this->current_action_no_ = 0;
1394   this->current_human_action_no_ = 0;
1395   this->actions_discrepancies_.clear();
1396   this->human_actions_discrepancies_.clear();
1397 
1398   this->human_finished_ = this->human_actions_.empty();
1399 
1400   BugReportReplay::signal_open()(*this);
1401 
1402   if (this->current_action_no_ == this->actions_.size()) {
1403     this->end_reached();
1404     return ;
1405   }
1406 
1407   if (this->current_action().type() == GameplayActions::Type::check) {
1408     this->check_action_ = true;
1409     this->action_processed(this->current_action(),
1410                            GameplayActions::Discrepancy::none);
1411   } // if (this->current_action().type == GameplayActions::Type::check)
1412 } // void BugReportReplay::party_open(Party party)
1413 
1414 /** set the settings of the party
1415  **/
1416 void
party_get_settings()1417 BugReportReplay::party_get_settings()
1418 {
1419   ::party->set_seed(this->seed_);
1420   ::party->set_startplayer(this->startplayer_no_);
1421 
1422   // set the rules and print the rule differences
1423   this->set_party_rule();
1424 
1425   { // set the players
1426     for (unsigned n = 0; n < this->persons_.size(); ++n) {
1427       ::party->players().set(n, this->persons_[n]->clone());
1428     }
1429   } // set the players
1430 
1431   if (this->verbose_ & verbose_message)
1432     // repeat the message
1433     cout << '\n'
1434       << "Message:\n"
1435       << "{\n"
1436       <<   this->message_
1437       << "}\n";
1438 } // void BugReportReplay::party_get_settings()
1439 
1440 /** the party is loaded
1441  **/
1442 void
party_loaded()1443 BugReportReplay::party_loaded()
1444 {
1445   // (re)set the rules and print the rule differences
1446   this->set_party_rule();
1447 
1448   if (this->verbose_ & verbose_message) {
1449     // repeat the message
1450     cout << '\n'
1451       << "Message:\n"
1452       << "{\n"
1453       <<   this->message_
1454       << "}\n";
1455   }
1456 } // void BugReportReplay::party_loaded()
1457 
1458 /** set the rule of the party
1459  **/
1460 void
set_party_rule()1461 BugReportReplay::set_party_rule()
1462 {
1463   // the hardcoded ruleset (the harddcoded)
1464   Rule const rule_hardcoded;
1465   // set the party rules
1466   auto& rule = ::party->rule();
1467   rule = this->rule_;
1468 
1469   if (this->verbose_ & verbose_rules) {
1470     cout << '\n'
1471       << "rule differences:\n"
1472       << "{\n";
1473     cout << setw(38) << ""
1474       << setw(12) << "bug_report"
1475       << setw(12) << "hardcoded"
1476       << "\n";
1477     for (unsigned i = Rule::Type::first; i <= Rule::Type::last; i++) {
1478       if (   (i >= (Rule::Type::bool_first))
1479           && (i <= Rule::Type::bool_last)) {
1480         if (   (rule(Rule::Type::Bool(i))
1481                 != rule_hardcoded(Rule::Type::Bool(i))) ) {
1482           cout << setw(38) << Rule::Type::Bool(i)
1483             << setw(12) << rule(Rule::Type::Bool(i))
1484             << setw(12) << rule_hardcoded(Rule::Type::Bool(i))
1485             << "\n";
1486         } // if (rule differs)
1487       } else if (   (i >= Rule::Type::unsigned_first)
1488                  && (i <= Rule::Type::unsigned_last)) {
1489         if (   (rule(Rule::Type::Unsigned(i))
1490                 != rule_hardcoded(Rule::Type::Unsigned(i))) ) {
1491           cout << setw(38) << Rule::Type::Unsigned(i)
1492             << setw(12) << rule(Rule::Type::Unsigned(i))
1493             << setw(12) << rule_hardcoded(Rule::Type::Unsigned(i))
1494             << "\n";
1495         } // if (rule differs)
1496       } else if (   (i >= Rule::Type::unsigned_extra_first)
1497                  && (i <= Rule::Type::unsigned_extra_last)) {
1498         if (   (rule(Rule::Type::UnsignedExtra(i))
1499                 != rule_hardcoded(Rule::Type::UnsignedExtra(i)))) {
1500           cout << setw(38) << Rule::Type::UnsignedExtra(i)
1501             << setw(12) << rule(Rule::Type::UnsignedExtra(i))
1502             << setw(12) << rule_hardcoded(Rule::Type::UnsignedExtra(i))
1503             << "\n";
1504         } // if (rule differs)
1505       } else if (i == Rule::Type::counting) {
1506         if (   (rule(Rule::Type::counting)
1507                 != rule_hardcoded(Rule::Type::counting)) ) {
1508           cout << setw(38) << Rule::Type::counting
1509             << setw(12) << rule(Rule::Type::counting)
1510             << setw(12) << rule_hardcoded(Rule::Type::counting)
1511             << "\n";
1512         } // if (rule differs)
1513       } else { // if (i: type unknown)
1514         DEBUG_ASSERTION(false,
1515                         "BugReportReplay::party_get_settings():\n"
1516                         "  unknown ruletype number " << i);
1517       } // if (i: type unknown)
1518     } // for (unsigned i = Rule::Type::first; i <= Rule::Type::last; i++)
1519     cout << "}" << endl;
1520   } // if (this->verbose_ & verbose_rules)
1521 } // void BugReportReplay::set_party_rule()
1522 
1523 /** the party is started
1524  **/
1525 void
party_start()1526 BugReportReplay::party_start()
1527 {
1528   this->auto_start_party_ = false;
1529 } // void BugReportReplay::party_start()
1530 
1531 /** the game is opened
1532  **
1533  ** @param    game   game that is opened
1534  **/
1535 void
game_open(Game const & game)1536 BugReportReplay::game_open(Game const& game)
1537 {
1538   {
1539     auto& game = ::party->game();
1540     game.signal_gameplay_action().connect(*this, &BugReportReplay::gameplay_action, this->disconnector_);
1541     game.poverty().signal_ask().connect(*this, &BugReportReplay::poverty_ask, this->disconnector_);
1542     game.tricks().signal_trick_open().connect(*this, &BugReportReplay::trick_open, this->disconnector_);
1543   }
1544 } // void BugReportReplay::game_open(Game game)
1545 
1546 /** a gameplay action
1547  **
1548  ** @param    action   the action
1549  **/
1550 void
gameplay_action(GameplayAction const & action)1551 BugReportReplay::gameplay_action(GameplayAction const& action)
1552 {
1553   if (this->finished_)
1554     return ;
1555 
1556   //auto const& game = ::party->game();
1557   // do handle all actions (see BugReport::gameplay_action(action) )
1558   switch (action.type()) {
1559   case GameplayActions::Type::poverty_denied_by_all:
1560   case GameplayActions::Type::trick_taken:
1561     // do not handle
1562     return ;
1563   case GameplayActions::Type::check:
1564   case GameplayActions::Type::print:
1565   case GameplayActions::Type::quit:
1566     // shall not happen
1567     DEBUG_ASSERTION(false,
1568                     "Gameplay action 'check'/'print'/'quit' should not be processed here");
1569     return ;
1570 
1571   case GameplayActions::Type::trick_open:
1572     return ; // replaced by trick_full
1573   case GameplayActions::Type::reservation:
1574   case GameplayActions::Type::poverty_shift:
1575   case GameplayActions::Type::poverty_returned:
1576   case GameplayActions::Type::poverty_accepted:
1577   case GameplayActions::Type::poverty_denied:
1578   case GameplayActions::Type::card_played:
1579   case GameplayActions::Type::trick_full:
1580   case GameplayActions::Type::announcement:
1581   case GameplayActions::Type::swines:
1582   case GameplayActions::Type::hyperswines:
1583   case GameplayActions::Type::marriage:
1584   case GameplayActions::Type::genscher:
1585     // do handle
1586     break;
1587   } // switch (action.type())
1588 
1589   // check that this action conforms to the gameplay
1590   if (   (action == this->current_action())
1591       || (   (action.type() == GameplayActions::Type::trick_open)
1592           && (this->current_action().type() == GameplayActions::Type::trick_full) )
1593      ) {
1594     this->action_processed(action, GameplayActions::Discrepancy::none);
1595     return ;
1596   }
1597   if (   this->remaining_actions() >= 2
1598       && action == this->next_action()) {
1599     cerr << "BugReport action skipped:\n"
1600       << "  bug report: " << this->current_action() << endl;
1601     this->action_processed(this->current_action(), GameplayActions::Discrepancy::skipped);
1602     this->action_processed(action, GameplayActions::Discrepancy::none);
1603     return ;
1604   }
1605   // the action differs from the one of the bug report
1606   cerr << "BugReport different actions:\n"
1607     << "  game:       " << action << '\n'
1608     << "  bug report: " << this->current_action() << endl;
1609 
1610   // if the type is equal there is only some other gameplay
1611   if (action.type() == this->current_action().type()) {
1612     switch (this->current_action().type()) {
1613     case GameplayActions::Type::card_played:
1614       if (dynamic_cast<GameplayActions::CardPlayed const&>(action).card
1615           != dynamic_cast<GameplayActions::CardPlayed const&>(this->current_action()).card) {
1616         this->action_processed(action, GameplayActions::Discrepancy::card);
1617         break;
1618       }
1619     default:
1620       this->action_processed(action, GameplayActions::Discrepancy::other);
1621       break;
1622     } // switch (this->current_action().type())
1623 
1624     return ;
1625   } // if (action.type() == this->current_action().type())
1626 
1627   // skip announcements
1628   switch (this->current_action().type()) {
1629   case GameplayActions::Type::announcement:
1630   case GameplayActions::Type::swines:
1631   case GameplayActions::Type::hyperswines:
1632   case GameplayActions::Type::marriage:
1633   case GameplayActions::Type::genscher:
1634 #ifdef OUTDATED
1635     // 2017-02-02: why skipping?
1636     if (action.type() >= GameplayAction::card_played) {
1637       // just skip
1638       this->action_processed(action, GameplayActions::Discrepancy::skipped);
1639       this->gameplay_action(action);
1640       return ;
1641     }
1642 #endif
1643     break;
1644 
1645   default:
1646     break;
1647   } // switch (this->current_action().type())
1648 
1649   switch (action.type()) {
1650   case GameplayActions::Type::card_played:
1651     if (this->current_action().type() == GameplayActions::Type::card_played) {
1652       this->action_processed(action, GameplayActions::Discrepancy::skipped);
1653       break;
1654     }
1655     // search the next card-played action
1656     while (!this->finished_) {
1657       this->action_processed(action, GameplayActions::Discrepancy::skipped);
1658       if (this->finished_)
1659         break ;
1660       if (this->current_action().type() == GameplayActions::Type::card_played)
1661         break ;
1662     } // while (!this->finished_)
1663     break;
1664 
1665   case GameplayActions::Type::reservation:
1666   case GameplayActions::Type::poverty_shift:
1667   case GameplayActions::Type::poverty_accepted:
1668   case GameplayActions::Type::poverty_returned:
1669   case GameplayActions::Type::poverty_denied:
1670   case GameplayActions::Type::poverty_denied_by_all:
1671   case GameplayActions::Type::trick_open:
1672   case GameplayActions::Type::trick_full:
1673   case GameplayActions::Type::trick_taken:
1674     // ToDo
1675     break;
1676 
1677   case GameplayActions::Type::announcement:
1678   case GameplayActions::Type::marriage:
1679   case GameplayActions::Type::genscher:
1680   case GameplayActions::Type::swines:
1681   case GameplayActions::Type::hyperswines:
1682   case GameplayActions::Type::check:
1683   case GameplayActions::Type::print:
1684   case GameplayActions::Type::quit:
1685     // ignore
1686     break;
1687   } // switch (action.type())
1688 
1689   return ;
1690 } // void BugReportReplay::gameplay_action(GameplayAction action)
1691 
1692 /** ask 'player' whether to accept the poverty
1693  **
1694  ** @param    player   player that is asked
1695  ** @param    cardno   number of shifted cards
1696  **/
1697 void
poverty_ask(Player const & player,unsigned const cardno)1698 BugReportReplay::poverty_ask(Player const& player,
1699                              unsigned const cardno)
1700 {
1701   this->handle_current_human_action();
1702 } // void BugReportReplay::poverty_ask(Player player, unsigned cardno);
1703 
1704 /** a new trick is opened
1705  **
1706  ** @param    trick   trick that is opened
1707  **/
1708 void
trick_open(Trick const & trick)1709 BugReportReplay::trick_open(Trick const& trick)
1710 {
1711   // automatic actions for the human player
1712   // De facto this is only needed for the first trick so that the player
1713   // can announce before any other action.
1714   this->handle_current_human_action();
1715 } // void BugReportReplay::trick_open(Trick const& trick)
1716 
1717 /** -> result
1718  **
1719  ** @param    player   player
1720  **
1721  ** @return   the next card played by the player
1722  **/
1723 HandCard
next_card(Player const & player) const1724 BugReportReplay::next_card(Player const& player) const
1725 {
1726   if (this->human_finished_) {
1727     if (this->finished_)
1728       return {};
1729     auto const& action = this->current_action();
1730     if (action.type() != GameplayActions::Type::card_played)
1731       return {};
1732     auto const& card_played = dynamic_cast<GameplayActions::CardPlayed const&>(action);
1733     if (card_played.player != player.no())
1734       return {};
1735     return HandCard(player.hand(), card_played.card);
1736   }
1737 
1738   auto const& action = this->current_human_action();
1739 
1740   if (action.type() == GameplayActions::Type::genscher)
1741     return HandCard(player.hand(), Card::diamond, Card::king);
1742 
1743   if (action.type() != GameplayActions::Type::card_played) {
1744     cerr << "Bug report: human action should be 'card played' but is:\n"
1745       << "  " << this->current_human_action() << endl;
1746     return {};
1747   } // if (this->current_human_action().type() != GameplayActions::Type::card_played)
1748 
1749   return HandCard(player.hand(), dynamic_cast<GameplayActions::CardPlayed const&>(this->current_human_action()).card);
1750 } // HandCard BugReportReplay::next_card(Player player) const
1751 
1752 /** set the auto action end
1753  **
1754  ** @param    auto_action_end   the end of the auto actions
1755  **/
1756 void
set_auto_action_end(unsigned const auto_action_end)1757 BugReportReplay::set_auto_action_end(unsigned const auto_action_end)
1758 {
1759   this->auto_action_end_ = auto_action_end;
1760   this->auto_start_party_ = true;
1761 } // void BugReportReplay::set_auto_action_end(unsigned auto_action_end)
1762 
1763 /** @return   whether the current action should be executed automatically
1764  **/
1765 bool
auto_action() const1766 BugReportReplay::auto_action() const
1767 {
1768   if (this->finished_)
1769     return false;
1770 
1771   return (!this->check_action_
1772           && (this->current_action_no_ < this->auto_action_end_));
1773 } // bool BugReportReplay::auto_action() const
1774 
1775 } // namespace OS_NS
1776