C ++ std :: map with custom mapping for match tournaments

I would like to save the results of the tournament in some kind of container. For each match I need to store the names of the players and the number of points. For instance:

map["player1:player2"] = {2,4}; 

I want to extract from this container not only the key "player1:player2" , but even the reverse key "player2:player1" , and I would like to get the opposite results.

I am going to use std::map and create some kind of smart shell around it. Perhaps there is some trick using custom mappings, custom extract and save functions.

Is std::map good choice for this or is something even better?

EDIT:

I summarized these comments in a solution that looks like this:

 struct Match { std::string player1; std::string player2; int pointsPlayer1; int pointsPlayer2; std::string getKey() { return player1 + ":" + player2; } Match reverse() { Match reversed; reversed.player1 = player2; reversed.player2 = player1; reversed.pointsPlayer1 = pointsPlayer2; reversed.pointsPlayer2 = pointsPlayer1; return reversed; } }; class Tournament { std::map<std::string, Match> _games; public: void insert(Match match); }; void Tournament::insert(Match match) { _games.insert({ match.getKey(), match }); Match reversed = match.reverse(); _games.insert({ reversed.getKey(), reversed }); } 

I choose a simpler approach, and I do not mind that every result is there twice, because the insert function replaces both coinciding each time, and encapsulation can guarantee this (it does not set a pointer, just a struct).

+6
source share
3 answers

Firstly, using std::map cannot work. The simple reason is that you want to insert map["player1:player2"] = {2, 4}; but from now on you need to return {4, 2} when you ask for it map["player2:player1"] . Thus, you do not need only different keys to refer to the same data (which std::map can give you using a special comparator), but you also need the same data in a different format depending on the order in the keys which std::map can't.

Now how to solve it? First, think about the interface you need. At the moment, you have functions for inserting and querying tournament results. My crystal ball also tells me that you will want to repeat all the results in the tournament, ask if there was a match and, possibly, reset the contents of the table. So, first go in and write down the interfaces for these functions and write down their behavior, especially for angular ones.

Then think about how to implement this. The easiest way is to use map<pair<string,string>, pair<int,int>> to store points. Now, when you insert, you either save the results redundantly (i.e., Store both points for "player1: player2" and "player2: player1"), which then give the correct results when searching with any option. Alternatively, normalize the order (sort the players lexicographically) and extract, optionally, invert the order of both keys before searching and the results subsequently to get the correct order.

Notes:

  • There is an alternative approach: if you indicate how many points player X scored against player Y, you have the same information. The corresponding data structure is map<string, map<string, int>> . To insert a match result, simply do res["player1"]["player2"] = 2; and res["player2"]["player1"] = 4; . I would not do this, except perhaps for the implementation behind the interface described above.
  • I prefer a pair over the string "player1: player2", even if I usually had to display it as a string. The simple reason is that it does not mix the view with the data, which gives you cleaner code. For the same reason, for example, I would not store, for example. 3% as a string, or as an integer value of 3, but rather as a floating point value of 0.03, since it is more computable (excluding problems with floating point inaccuracies).
+5
source

Perhaps this would help:

 #include <map> #include <array> #include <string> class Player { private: std::string m_strName; unsigned m_uScore; public: Player() : m_uScore( 0 ) {} explicit Player( const std::string& strName, unsigned score = 0 ) : m_strName( strName ), m_uScore( score ) {} const std::string& getName() const { return m_strName; } const unsigned getScore() const { return m_uScore; } void setName( const std::string& strName ) { m_strName = strName; } void setScore( const unsigned& uScore ) { m_uScore = uScore; } bool operator==( const Player& p ) const { if ( m_uScorescore == p.m_uScore && m_strName == p.strName ) { return true; } return false; } bool operator!=( const Player& p ) const { return !operator==( p ); } bool operator<( const Player& p ) const { return m_uScore < p.m_uScore; } bool operator> ( const Player& p ) const { return m_uScore > p.m_uScore; } const Player& lessThan( const Player& p ) const { return m_uScore < p.m_uScore ? *this : p; } const Player& greaterThan( const Player& p ) const { return m_uScore > p.m_uScore ? *this : p; } }; // Player int main() { unsigned uMatch = 1; // Since you need to compare 2 for each match I did this but isn't necessary // You can choose which ever method is necessary. But since this // Has only 2 of the same type of object then this made sense to me. // I Initialized the std::array with the first results it isn't needed // but used to show how 2 players are grouped to one entity but yet // remaining individual objects. std::array<Player,2> aPlayerResults = { Player( "Player1", 20 ), Player( "Player2", 10 ) }; // This map then would hold every game-round( match results ) std::map<unsigned, std::array<Player,2>> mMatches; mMatches[uMatch] = aPlayerResults; // Now Just Update The Values uMatch = 2; aPlayerResults.at(0).setName( std::string( "Player3" ) ); aPlayerResults.at(0).setScore( 19 ); aPlayerResults.at(1).setName( std::string( "Player4" ) ); aPlayerResults.at(1).setScore( 17 ); // Add To Map mMatches[uMatch] = aPlayerResults; return 0; } 

As you can see that a player, which is a class object that associates a player’s name with an account and maintains data confidentiality, can use auxiliary functions to set and receive values, there is a default constructor to create an empty game object to fill in later, plus an explicit constructor that with the exception of at least the std string for the name, and evaluation is an option. If the score is left empty, you have a player who exists before any match, and as soon as the match ends, you can use the setScore method. There are operators to check if Player1 == Player2 or! = And <and> operators that return true or false for if statements. And if you want to compare two players and return one player or another based on if the rating is equal to> or <there are two functions that do this for you. All the tools are in this nice little class, and you can even expand it.

I used std :: array, since we know that there should be only 2 players in a match, for example, in a chess game. Now, if you need more players, you can either increase the number of elements in the array, if the value is small, say less than 10 or 20 per max, if there are more players, then you want to switch std :: array to std :: vector of Player objects or std :: vector pointers to Player objects awaiting your needs. The map simply binds unsigned as a key or index using std :: array or std :: vector players. The index on the map makes it enjoyable, since each match or game is unique, and normal maps do not allow duplicate keys. In addition, using unsigned makes the map [] an assignment designator much more readable.

Of course, you can always create a function that will be used either in two Players using the permalink or two player pointers, and they will install these players on the map for you.

You can also create your own map in advance with a bunch of player’s objects, everyone has unique names without points, and then after each match you can go through the for loop using an iterator to update all the points, and then create another function to go through through your entire map to sort each match by the highest value of the first or second, etc. This is just a recommendation on how you can achieve what you have stated. There is never an exact "right path", since there are many ways to achieve the desired results when writing a program.

+2
source

you can write a simple function for this to work:

 string getResult(map<string, string> scores, string key) { vecor<string> splited = split(key, ":"); if (scores.containsKey(key)) return scores.get(key); else return scored.get(splited[1] + ":" + splited[0]); } 
+1
source

Source: https://habr.com/ru/post/986432/


All Articles