Properly sequence matches when there are more than two players in the tournament

This commit is contained in:
Eric Frias 2016-10-23 13:12:06 -04:00
parent 30874697cc
commit f078fed4fd
4 changed files with 91 additions and 20 deletions

View file

@ -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<account_id_type>& 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);

View file

@ -44,9 +44,8 @@ namespace graphene { namespace chain {
struct initiate_match
{
database& db;
vector<account_id_type> players;
initiate_match(database& db, const vector<account_id_type>& 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<account_id_type, unsigned> scores_by_player;
for (const flat_set<account_id_type>& game_winners : match.game_winners)
for (const account_id_type& account_id : game_winners)
++scores_by_player[account_id];
optional<account_id_type> 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<account_id_type>& 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)

View file

@ -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)

View file

@ -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<match_object>();
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<match_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<match_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 << "__";