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