diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index 0198bd0a..ca3eb01f 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -206,6 +206,8 @@ void database::initialize_indexes() add_index< primary_index >(); add_index< primary_index >(); + add_index< primary_index >(); + add_index< primary_index >(); //Implementation object indexes add_index< primary_index >(); diff --git a/libraries/chain/include/graphene/chain/protocol/rock_paper_scissors.hpp b/libraries/chain/include/graphene/chain/protocol/rock_paper_scissors.hpp index da43627e..3e27bc60 100644 --- a/libraries/chain/include/graphene/chain/protocol/rock_paper_scissors.hpp +++ b/libraries/chain/include/graphene/chain/protocol/rock_paper_scissors.hpp @@ -48,6 +48,13 @@ namespace graphene { namespace chain { uint32_t time_per_reveal_move; }; + struct rock_paper_scissors_game_details + { + + + }; + typedef fc::static_variant game_specific_details; + } } FC_REFLECT( graphene::chain::rock_paper_scissors_game_options, (insurance_enabled)(time_per_commit_move)(time_per_reveal_move) ) diff --git a/libraries/chain/include/graphene/chain/protocol/tournament.hpp b/libraries/chain/include/graphene/chain/protocol/tournament.hpp index 2da7d2ed..2d167f1f 100644 --- a/libraries/chain/include/graphene/chain/protocol/tournament.hpp +++ b/libraries/chain/include/graphene/chain/protocol/tournament.hpp @@ -173,6 +173,7 @@ FC_REFLECT( graphene::chain::tournament_join_operation, (fee) (payer_account_id) (player_account_id) + (tournament_id) (buy_in) (extensions)) FC_REFLECT( graphene::chain::tournament_create_operation::fee_parameters_type, (fee) ) diff --git a/libraries/chain/include/graphene/chain/tournament_object.hpp b/libraries/chain/include/graphene/chain/tournament_object.hpp index 6e902fdc..f98f25af 100644 --- a/libraries/chain/include/graphene/chain/tournament_object.hpp +++ b/libraries/chain/include/graphene/chain/tournament_object.hpp @@ -6,6 +6,15 @@ #include #include +namespace graphene { namespace chain { + class tournament_object; +} } + +namespace fc { + void to_variant(const graphene::chain::tournament_object& tournament_obj, fc::variant& v); + void from_variant(const fc::variant& v, graphene::chain::tournament_object& tournament_obj); +} //end namespace fc + namespace graphene { namespace chain { class database; using namespace graphene::db; @@ -24,6 +33,11 @@ namespace graphene { namespace chain { /// List of payers who have contributed to the prize pool flat_map payers; + + /// List of all matches in this tournament. When the tournament starts, all matches + /// are created. Matches in the first round will have players, matches in later + /// rounds will not be populated. + vector matches; }; enum class tournament_state @@ -86,6 +100,9 @@ namespace graphene { namespace chain { template friend Stream& operator>>( Stream& s, tournament_object& tournament_obj ); + friend void ::fc::to_variant(const graphene::chain::tournament_object& tournament_obj, fc::variant& v); + friend void ::fc::from_variant(const fc::variant& v, graphene::chain::tournament_object& tournament_obj); + void pack_impl(std::ostream& stream) const; void unpack_impl(std::istream& stream); @@ -106,10 +123,19 @@ namespace graphene { namespace chain { static const uint8_t space_id = protocol_ids; static const uint8_t type_id = match_object_type; + tournament_id_type tournament_id; /// - flat_set players; + vector players; + vector games; vector > game_winners; + + /// the time the match started + time_point_sec start_time; + /// If the match has ended, the time it ended + optional end_time; + + game_id_type start_next_game(database& db, match_id_type match_id); }; class game_object : public graphene::db::abstract_object @@ -118,7 +144,9 @@ namespace graphene { namespace chain { static const uint8_t space_id = protocol_ids; static const uint8_t type_id = game_object_type; - flat_set players; + match_id_type match_id; + + vector players; flat_set winners; }; @@ -150,6 +178,20 @@ namespace graphene { namespace chain { > tournament_details_object_multi_index_type; typedef generic_index tournament_details_index; + typedef multi_index_container< + match_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > > > + > match_object_multi_index_type; + typedef generic_index match_index; + + typedef multi_index_container< + game_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > > > + > game_object_multi_index_type; + typedef generic_index game_index; + template inline Stream& operator<<( Stream& s, const tournament_object& tournament_obj ) { @@ -206,7 +248,8 @@ namespace graphene { namespace chain { FC_REFLECT_DERIVED(graphene::chain::tournament_details_object, (graphene::db::object), (registered_players) - (payers)) + (payers) + (matches)) FC_REFLECT_TYPENAME(graphene::chain::tournament_object) // manually serialized FC_REFLECT_ENUM(graphene::chain::tournament_state, (accepting_registrations) @@ -215,41 +258,15 @@ FC_REFLECT_ENUM(graphene::chain::tournament_state, (registration_period_expired) (concluded)) -namespace fc { - // Manually reflect tournament_object to variant to properly reflect "state" - inline void to_variant(const graphene::chain::tournament_object& tournament_obj, fc::variant& v) - { - fc_elog(fc::logger::get("tournament"), "In tournament_obj to_variant"); - elog("In tournament_obj to_variant"); - fc::mutable_variant_object o; - o("id", tournament_obj.id) - ("creator", tournament_obj.creator) - ("options", tournament_obj.options) - ("start_time", tournament_obj.start_time) - ("end_time", tournament_obj.end_time) - ("prize_pool", tournament_obj.prize_pool) - ("registered_players", tournament_obj.registered_players) - ("tournament_details_id", tournament_obj.tournament_details_id) - ("state", tournament_obj.get_state()); - - v = o; - } - - // Manually reflect tournament_object to variant to properly reflect "state" - inline void from_variant(const fc::variant& v, graphene::chain::tournament_object& tournament_obj) - { - fc_elog(fc::logger::get("tournament"), "In tournament_obj from_variant"); - tournament_obj.id = v["id"].as(); - tournament_obj.creator = v["creator"].as(); - tournament_obj.options = v["options"].as(); - tournament_obj.start_time = v["start_time"].as >(); - tournament_obj.end_time = v["end_time"].as >(); - tournament_obj.prize_pool = v["prize_pool"].as(); - tournament_obj.registered_players = v["registered_players"].as(); - tournament_obj.tournament_details_id = v["tournament_details_id"].as(); - // TODO deserialize "State" - } -} //end namespace fc - - +FC_REFLECT_DERIVED(graphene::chain::match_object, (graphene::db::object), + (tournament_id) + (players) + (games) + (game_winners) + (start_time) + (end_time)) +FC_REFLECT_DERIVED(graphene::chain::game_object, (graphene::db::object), + (match_id) + (players) + (winners)) diff --git a/libraries/chain/protocol/tournament.cpp b/libraries/chain/protocol/tournament.cpp index 6f46f217..9a5cf074 100644 --- a/libraries/chain/protocol/tournament.cpp +++ b/libraries/chain/protocol/tournament.cpp @@ -27,8 +27,8 @@ namespace graphene { namespace chain { void tournament_options::validate() const { - FC_ASSERT( number_of_players >= 2 && (number_of_players & (number_of_players - 1)) == 0, - "Number of players must be a power of two" ); + //FC_ASSERT( number_of_players >= 2 && (number_of_players & (number_of_players - 1)) == 0, + // "Number of players must be a power of two" ); } share_type tournament_create_operation::calculate_fee(const fee_parameters_type& k)const diff --git a/libraries/wallet/include/graphene/wallet/wallet.hpp b/libraries/wallet/include/graphene/wallet/wallet.hpp index b226d9c5..07a16f4a 100644 --- a/libraries/wallet/include/graphene/wallet/wallet.hpp +++ b/libraries/wallet/include/graphene/wallet/wallet.hpp @@ -1425,6 +1425,11 @@ class wallet_api */ vector get_upcoming_tournaments(optional player_accounts, uint32_t limit); + /** Get specific information about a tournament + * @param tournament_id the ID of the tournament + */ + tournament_object get_tournament(tournament_id_type id); + void dbg_make_uia(string creator, string symbol); void dbg_make_mia(string creator, string symbol); void flood_network(string prefix, uint32_t number_of_transactions); @@ -1615,4 +1620,5 @@ FC_API( graphene::wallet::wallet_api, (tournament_create) (tournament_join) (get_upcoming_tournaments) + (get_tournament) ) diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 22ab9cef..5fd234af 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -2176,7 +2177,7 @@ public: { const vector tournaments = result.as >(); std::stringstream ss; - ss << tournaments.size() << " upcoming tournaments:\n"; + ss << "ID GAME BUY IN PLAYERS\n"; ss << "====================================================================================\n"; for( const tournament_object& tournament_obj : tournaments ) { @@ -2221,6 +2222,80 @@ public: } return ss.str(); }; + m["get_tournament"] = [this](variant result, const fc::variants& a) + { + std::stringstream ss; + + tournament_object tournament = result.as(); + tournament_details_object tournament_details = _remote_db->get_objects({result["tournament_details_id"].as()})[0].as(); + tournament_state state = tournament.get_state(); + if (state == tournament_state::awaiting_start) + { + ss << "Tournament starts at " << tournament.start_time->to_iso_string() << "\n"; + ss << "Players:\n"; + for (const account_id_type& player : tournament_details.registered_players) + ss << "\t" << get_account(player).name << "\n"; + } + else if (state == tournament_state::in_progress || + state == tournament_state::concluded) + { + unsigned num_matches = tournament_details.matches.size(); + uint32_t num_rounds = boost::multiprecision::detail::find_msb(tournament_details.matches.size() + 1); + unsigned num_rows = (num_matches + 1) * 2 - 1; + for (unsigned row = 0; row < num_rows; ++row) + { + for (unsigned round = 0; round <= num_rounds; ++round) + { + unsigned row_offset = (1 << round) - 1; + unsigned row_vertical_spacing = 1 << (round + 1); + if (row >= row_offset && + (row - row_offset) % row_vertical_spacing == 0) + { + unsigned player_number_in_round = (row - row_offset) / row_vertical_spacing; + unsigned first_player_in_round = (num_matches - (num_matches >> round)) * 2; + unsigned player_number = first_player_in_round + player_number_in_round; + + unsigned match_number = player_number / 2; + unsigned player_in_match = player_number % 2; + idump((match_number)(player_in_match)); + + 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 (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 << "__"; + } + else + ss << " "; + + if (round != num_rounds) + { + unsigned round_horizontal_spacing = 1 << round; + unsigned next_row_vertical_spacing = 1 << (round + 2); + for (unsigned i = 0; i < round_horizontal_spacing; ++i) + { + if ((row - 1 - i - row_offset) % next_row_vertical_spacing == 0) + ss << "\\"; + else if ((row - row_vertical_spacing + i - row_offset) % next_row_vertical_spacing == 0) + ss << "/"; + else + ss << " "; + } + } + } + ss << "\n"; + } + } + return ss.str(); + }; return m; } @@ -4200,6 +4275,11 @@ vector wallet_api::get_upcoming_tournaments(fc::optional_remote_db->get_upcoming_tournaments(player_account_id, limit); } +tournament_object wallet_api::get_tournament(tournament_id_type id) +{ + return my->_remote_db->get_objects({id})[0].as(); +} + // default ctor necessary for FC_REFLECT signed_block_with_info::signed_block_with_info() {