diff --git a/libraries/chain/include/graphene/chain/match_object.hpp b/libraries/chain/include/graphene/chain/match_object.hpp index 6f8423f4..3fe5f697 100644 --- a/libraries/chain/include/graphene/chain/match_object.hpp +++ b/libraries/chain/include/graphene/chain/match_object.hpp @@ -89,7 +89,7 @@ namespace graphene { namespace chain { void pack_impl(std::ostream& stream) const; void unpack_impl(std::istream& stream); - void on_initiate_match(database& db, const vector& players); + void on_initiate_match(database& db); void on_game_complete(database& db, const game_object& game); game_id_type start_next_game(database& db, match_id_type match_id); diff --git a/libraries/chain/match_object.cpp b/libraries/chain/match_object.cpp index 8c39c1b3..3ea3d250 100644 --- a/libraries/chain/match_object.cpp +++ b/libraries/chain/match_object.cpp @@ -44,9 +44,8 @@ namespace graphene { namespace chain { struct initiate_match { database& db; - vector players; - initiate_match(database& db, const vector& players) : - db(db), players(players) + initiate_match(database& db) : + db(db) {} }; @@ -80,7 +79,6 @@ namespace graphene { namespace chain { fc_ilog(fc::logger::get("tournament"), "Match ${id} is now in progress", ("id", match.id)); - match.players = event.players; match.number_of_wins.resize(match.players.size()); match.start_time = event.db.head_block_time(); @@ -96,6 +94,24 @@ namespace graphene { namespace chain { "Match ${id} is complete", ("id", match.id)); + std::map scores_by_player; + for (const flat_set& game_winners : match.game_winners) + for (const account_id_type& account_id : game_winners) + ++scores_by_player[account_id]; + + optional high_scoring_account; + unsigned high_score = 0; + for (const auto& value : scores_by_player) + if (value.second > high_score) + { + high_score = value.second; + high_scoring_account = value.first; + } + + if (high_scoring_account) + match.match_winners.insert(*high_scoring_account); + + match.end_time = event.db.head_block_time(); const tournament_object& tournament_obj = match.tournament_id(event.db); event.db.modify(tournament_obj, [&](tournament_object& tournament) { tournament.on_match_completed(event.db, match); @@ -108,11 +124,13 @@ namespace graphene { namespace chain { fc_ilog(fc::logger::get("tournament"), "Match ${id} is complete, it was a buy", ("id", match)); - match.players = event.players; match.number_of_wins.resize(match.players.size()); - boost::copy(event.players, std::inserter(match.match_winners, match.match_winners.end())); + boost::copy(match.players, std::inserter(match.match_winners, match.match_winners.end())); match.start_time = event.db.head_block_time(); match.end_time = event.db.head_block_time(); + // NOTE: when the match is a buy, we don't send a match completed event to + // the tournament_obj, because it is already in the middle of handling + // an event; it will figure out that the match has completed on its own. } }; typedef waiting_on_previous_matches initial_state; @@ -137,7 +155,7 @@ namespace graphene { namespace chain { bool match_is_a_buy(const initiate_match& event) { - return event.players.size() < 2; + return match_obj->players.size() < 2; } void record_completed_game(const game_complete& event) @@ -294,9 +312,9 @@ namespace graphene { namespace chain { ia >> my->state_machine; } - void match_object::on_initiate_match(database& db, const vector& players) + void match_object::on_initiate_match(database& db) { - my->state_machine.process_event(initiate_match(db, players)); + my->state_machine.process_event(initiate_match(db)); } void match_object::on_game_complete(database& db, const game_object& game) diff --git a/libraries/chain/tournament_object.cpp b/libraries/chain/tournament_object.cpp index cad37c5c..52fb7ba3 100644 --- a/libraries/chain/tournament_object.cpp +++ b/libraries/chain/tournament_object.cpp @@ -172,7 +172,8 @@ namespace graphene { namespace chain { if (paired_players[2 * i + 1] != account_id_type()) players.emplace_back(paired_players[2 * i + 1]); event.db.modify(matches[i](event.db), [&](match_object& match) { - match.on_initiate_match(event.db, players); + match.players = players; + match.on_initiate_match(event.db); }); } event.db.modify(tournament_details_obj, [&](tournament_details_object& tournament_details_obj){ @@ -181,11 +182,50 @@ namespace graphene { namespace chain { } void on_entry(const match_completed& event, tournament_state_machine_& fsm) { + tournament_object& tournament = *fsm.tournament_obj; fc_ilog(fc::logger::get("tournament"), - "Tournament ${id} is still in progress, maybe should start a new match here", - ("id", fsm.tournament_obj->id)); + "Match ${match_id} in tournament tournament ${tournament_id} is still in progress", + ("match_id", event.match.id)("tournament_id", tournament.id)); + + // this wasn't the final match that just finished, so figure out if we can start the next match. + // The next match can start if both this match and the previous match have completed + const tournament_details_object& tournament_details_obj = fsm.tournament_obj->tournament_details_id(event.db); + unsigned num_matches = tournament_details_obj.matches.size(); + auto this_match_iter = std::find(tournament_details_obj.matches.begin(), tournament_details_obj.matches.end(), event.match.id); + assert(this_match_iter != tournament_details_obj.matches.end()); + unsigned this_match_index = std::distance(tournament_details_obj.matches.begin(), this_match_iter); + // TODO: we currently create all matches at startup, so they are numbered sequentially. We could get the index + // by subtracting match.id as long as this behavior doesn't change + + unsigned next_round_match_index = (this_match_index + num_matches + 1) / 2; + assert(next_round_match_index < num_matches); + const match_object& next_round_match = tournament_details_obj.matches[next_round_match_index](event.db); + + // each match will have two players, match.players[0] and match.players[1]. + // for consistency, we want to feed the winner of this match into the correct + // slot in the next match + unsigned winner_index_in_next_match = (this_match_index + num_matches + 1) % 2; + unsigned other_match_index = num_matches - ((num_matches - next_round_match_index) * 2 + winner_index_in_next_match); + const match_object& other_match = tournament_details_obj.matches[other_match_index](event.db); + + // the winners of the matches event.match and other_match will play in next_round_match + + assert(event.match.match_winners.size() <= 1); + + event.db.modify(next_round_match, [&](match_object& next_match_obj) { + if (!event.match.match_winners.empty()) // if there is a winner + { + if (winner_index_in_next_match == 0) + next_match_obj.players.insert(next_match_obj.players.begin(), *event.match.match_winners.begin()); + else + next_match_obj.players.push_back(*event.match.match_winners.begin()); + } + if (other_match.get_state() == match_state::match_complete) + next_match_obj.on_initiate_match(event.db); + }); } }; + struct registration_period_expired : public msm::front::state<> { void on_entry(const registration_deadline_passed& event, tournament_state_machine_& fsm) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index b4a19c6f..ed7f2ea6 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -2525,16 +2525,29 @@ public: unsigned match_number = player_number / 2; unsigned player_in_match = player_number % 2; - match_object match = _remote_db->get_objects({tournament_details.matches[match_number]})[0].as(); std::string player_name; - if (round != num_rounds && - !match.players.empty()) + if (round == num_rounds) { - if (player_in_match < match.players.size()) - player_name = get_account(match.players[player_in_match]).name; - else - player_name = "[bye]"; + match_object match = get_object(tournament_details.matches[num_matches - 1]); + if (match.get_state() == match_state::match_complete && + !match.match_winners.empty()) + { + assert(match.match_winners.size() == 1); + player_name = get_account(*match.match_winners.begin()).name; + } } + else + { + match_object match = get_object(tournament_details.matches[match_number]); + if (!match.players.empty()) + { + if (player_in_match < match.players.size()) + player_name = get_account(match.players[player_in_match]).name; + else + player_name = "[bye]"; + } + } + ss << "__"; ss << std::setfill('_') << std::setw(10) << player_name.substr(0,10); ss << "__";