#pragma once #include #include #include #include #include #include #include #include "../Player.hh" struct Lobby; namespace Episode3 { // The comment in Server.hh does not apply to this file (and Tournament.cc). // TODO: We should build a way to save tournament state to a file, so it can // persist across server restarts. class Tournament : public std::enable_shared_from_this { public: enum class State { REGISTRATION = 0, IN_PROGRESS, COMPLETE, }; struct Team : public std::enable_shared_from_this { std::weak_ptr tournament; size_t index; size_t max_players; std::set player_serial_numbers; std::set> com_decks; std::string name; std::string password; size_t num_rounds_cleared; bool is_active; Team( std::shared_ptr tournament, size_t index, size_t max_players); std::string str() const; void register_player( uint32_t serial_number, const std::string& team_name, const std::string& password); bool unregister_player(uint32_t serial_number); }; struct Match : public std::enable_shared_from_this { enum class WinnerTeam { A = 0, B = 1, }; std::weak_ptr tournament; std::shared_ptr preceding_a; std::shared_ptr preceding_b; std::weak_ptr following; std::shared_ptr winner_team; size_t round_num; Match( std::shared_ptr tournament, std::shared_ptr preceding_a, std::shared_ptr preceding_b); Match( std::shared_ptr tournament, std::shared_ptr winner_team); std::string str() const; bool resolve_if_no_players(); void on_winner_team_set(); void set_winner_team(std::shared_ptr team); std::shared_ptr opponent_team_for_team(std::shared_ptr team) const; }; Tournament( std::shared_ptr data_index, uint8_t number, const std::string& name, std::shared_ptr map, const Rules& rules, size_t num_teams, bool is_2v2); ~Tournament() = default; void init(); std::shared_ptr get_data_index() const; uint8_t get_number() const; const std::string& get_name() const; std::shared_ptr get_map() const; const Rules& get_rules() const; bool get_is_2v2() const; State get_state() const; const std::vector>& all_teams() const; std::shared_ptr get_team(size_t index) const; std::shared_ptr get_winner_team() const; std::shared_ptr next_match_for_team(std::shared_ptr team) const; std::shared_ptr get_final_match() const; std::shared_ptr team_for_serial_number(uint32_t serial_number) const; void start(); void print_bracket(FILE* stream) const; private: PrefixedLogger log; std::shared_ptr data_index; uint8_t number; std::string name; std::shared_ptr map; Rules rules; size_t num_teams; bool is_2v2; State current_state; std::set all_player_serial_numbers; std::unordered_set> pending_matches; // This vector contains all teams in the original starting order of the // tournament (that is, all teams in the first round). The order within this // vector determines which team will play against which other team in the // first round: [0] will play against [1], [2] will play against [3], etc. std::vector> teams; // The tournament begins with a "zero round", in which each team automatically // "wins" a match, putting them into the first round. This is just to make the // data model easier to manage, so we don't have to have a type of match with // no preceding round. std::vector> zero_round_matches; std::shared_ptr final_match; }; class TournamentIndex { public: TournamentIndex() = default; ~TournamentIndex() = default; std::vector> all_tournaments() const; std::shared_ptr create_tournament( std::shared_ptr data_index, const std::string& name, std::shared_ptr map, const Rules& rules, size_t num_teams, bool is_2v2); void delete_tournament(uint8_t number); std::shared_ptr get_tournament(uint8_t number) const; std::shared_ptr get_tournament(const std::string& name) const; std::shared_ptr team_for_serial_number( uint32_t serial_number) const; private: std::shared_ptr tournaments[0x20]; }; } // namespace Episode3