@@ -14,6 +14,7 @@ namespace golf {
14
14
using namespace cards ;
15
15
16
16
using absl::InvalidArgumentError;
17
+ using absl::Status;
17
18
using absl::StatusOr;
18
19
19
20
using std::deque;
@@ -23,21 +24,28 @@ using std::vector;
23
24
static const std::string allowedChars =
24
25
" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-" ;
25
26
26
- StatusOr<string> GameManager::registerUser (const string& username) {
27
- if (username .size () < 4 || username .size () > 15 ) {
27
+ auto validate_user_id (const string& user_id) -> Status {
28
+ if (user_id .size () < 4 || user_id .size () > 15 ) {
28
29
return InvalidArgumentError (" username length must be between 4 and 15 chars" );
29
30
}
30
31
31
- if (username .find_first_not_of (allowedChars) != string::npos) {
32
+ if (user_id .find_first_not_of (allowedChars) != string::npos) {
32
33
return InvalidArgumentError (" only alphanumeric, underscore, or dash allowed in username" );
33
34
}
35
+ return absl::OkStatus ();
36
+ }
34
37
35
- if (usersOnline.find (username) != usersOnline.end ()) {
36
- return InvalidArgumentError (" username taken" );
38
+ StatusOr<string> GameManager::registerUser (const string& user_id) {
39
+ auto validate_status = validate_user_id (user_id);
40
+ if (!validate_status.ok ()) {
41
+ return validate_status;
37
42
}
38
43
39
- usersOnline.insert (username);
40
- return username;
44
+ auto save_status = game_store_->AddUser (user_id);
45
+ if (save_status.ok ()) {
46
+ return user_id;
47
+ }
48
+ return save_status;
41
49
}
42
50
43
51
deque<Card> GameManager::shuffleNewDeck () {
@@ -84,23 +92,21 @@ std::string GameManager::generateRandomAlphanumericString(std::size_t len) const
84
92
std::optional<std::string> GameManager::generateUnusedRandomId () const {
85
93
for (int i = 0 ; i < 10 ; i++) {
86
94
auto attempt = generateRandomAlphanumericString (12 );
87
- if (gamesById.find (attempt) == gamesById.end ()) {
95
+ auto existing_game = game_store_->ReadGame (attempt);
96
+ if (!existing_game.ok ()) {
88
97
return attempt;
89
98
}
90
99
}
91
100
return {};
92
101
}
93
102
94
- // TODO: generate random game id
95
- // TODO: support multiple decks
96
- StatusOr<GameStatePtr> GameManager::newGame (const string& username, int numberOfPlayers) {
97
- if (usersOnline.find (username) == usersOnline.end ()) {
98
- return InvalidArgumentError (" unregistered username" );
99
- }
100
- if (gameIdsByUser.find (username) != gameIdsByUser.end ()) {
101
- return InvalidArgumentError (" already in game" );
103
+ // TODO: support multiple decks for many players?
104
+ StatusOr<GameStatePtr> GameManager::newGame (const string& user_id, int number_of_players) {
105
+ if (!game_store_->UserExists (user_id)) {
106
+ return InvalidArgumentError (" unknown user" );
102
107
}
103
- if (numberOfPlayers < 2 || numberOfPlayers > 5 ) {
108
+
109
+ if (number_of_players < 2 || number_of_players > 5 ) {
104
110
return InvalidArgumentError (" 2 to 5 players" );
105
111
}
106
112
@@ -112,22 +118,22 @@ StatusOr<GameStatePtr> GameManager::newGame(const string& username, int numberOf
112
118
deque<Card> mutableDrawPile = shuffleNewDeck ();
113
119
114
120
vector<Card> allDealt{};
115
- for (int i = 0 ; i < numberOfPlayers * 4 ; i++) {
121
+ for (int i = 0 ; i < number_of_players * 4 ; i++) {
116
122
allDealt.push_back (mutableDrawPile.back ());
117
123
mutableDrawPile.pop_back ();
118
124
}
119
125
120
126
vector<Player> mutablePlayers;
121
127
122
128
// two up, two down
123
- int halfway = numberOfPlayers * 2 ;
124
- for (int i = 0 ; i < numberOfPlayers ; i++) {
129
+ int halfway = number_of_players * 2 ;
130
+ for (int i = 0 ; i < number_of_players ; i++) {
125
131
auto & tl = allDealt.at (2 * i);
126
132
auto & tr = allDealt.at (2 * i + 1 );
127
133
auto & bl = allDealt.at (2 * i + halfway);
128
134
auto & br = allDealt.at (2 * i + halfway + 1 );
129
135
if (i == 0 ) {
130
- mutablePlayers.emplace_back (username , tl, tr, bl, br);
136
+ mutablePlayers.emplace_back (user_id , tl, tr, bl, br);
131
137
} else {
132
138
mutablePlayers.emplace_back (tl, tr, bl, br);
133
139
}
@@ -141,29 +147,22 @@ StatusOr<GameStatePtr> GameManager::newGame(const string& username, int numberOf
141
147
const deque<Card> drawPile = std::move (mutableDrawPile);
142
148
const deque<Card> discardPile = std::move (mutableDiscardPile);
143
149
144
- auto emplaceWorked = gamesById.emplace (
145
- gameId,
146
- std::make_shared<GameState>(GameState{drawPile, discardPile, players, false , 0 , -1 , gameId}));
147
- if (!emplaceWorked.second ) {
148
- return InvalidArgumentError (" could not generate unused game id" );
149
- }
150
-
151
- usersByGame.insert (std::make_pair (gameId, std::unordered_set<string>{username}));
152
- gameIdsByUser[username] = gameId;
153
- return gamesById.at (gameId);
150
+ auto game_state =
151
+ std::make_shared<GameState>(GameState{drawPile, discardPile, players, false , 0 , -1 , gameId});
152
+ return game_store_->NewGame (game_state);
154
153
}
155
154
156
- StatusOr<GameStatePtr> GameManager::joinGame (const string& gameId , const string& username ) {
157
- if (usersOnline. find (username) == usersOnline. end ( )) {
155
+ StatusOr<GameStatePtr> GameManager::joinGame (const string& game_id , const string& user_id ) {
156
+ if (!game_store_-> UserExists (user_id )) {
158
157
return InvalidArgumentError (" unregistered username" );
159
158
}
160
159
161
- auto gameIter = gamesById. find (gameId );
162
- if (gameIter == gamesById. end ()) {
160
+ auto game_read_status = game_store_-> ReadGame (game_id );
161
+ if (!game_read_status. ok ()) {
163
162
return InvalidArgumentError (" unknown game id" );
164
163
}
165
164
166
- auto oldGameState = gameIter-> second ;
165
+ auto oldGameState = *game_read_status ;
167
166
168
167
if (oldGameState->allPlayersPresent ()) {
169
168
return InvalidArgumentError (" no spots available" );
@@ -177,28 +176,21 @@ StatusOr<GameStatePtr> GameManager::joinGame(const string& gameId, const string&
177
176
updatedPlayers.push_back (p);
178
177
} else {
179
178
// safe because we know player is not already claimed
180
- updatedPlayers.emplace_back (*p.claimHand (username ));
179
+ updatedPlayers.emplace_back (*p.claimHand (user_id ));
181
180
playerAdded = true ;
182
181
}
183
182
}
184
183
185
- gamesById.erase (gameId);
186
- gamesById.emplace (gameId, std::make_shared<GameState>(oldGameState->withPlayers (updatedPlayers)));
187
- usersByGame.at (gameId).insert (username);
188
- gameIdsByUser[username] = gameId;
189
-
190
- return gamesById.at (gameId);
184
+ auto updated_game = std::make_shared<GameState>(oldGameState->withPlayers (updatedPlayers));
185
+ return game_store_->UpdateGame (updated_game);
191
186
}
192
187
193
- StatusOr<GameStatePtr> GameManager::getGameStateForUser (const string& username) const {
194
- if (usersOnline.find (username) == usersOnline.end ()) {
195
- return InvalidArgumentError (" unregistered username" );
196
- }
197
- if (gameIdsByUser.find (username) == gameIdsByUser.end ()) {
198
- return InvalidArgumentError (" user not in game" );
188
+ StatusOr<GameStatePtr> GameManager::getGameStateForUser (const string& user_id) const {
189
+ if (!game_store_->UserExists (user_id)) {
190
+ return InvalidArgumentError (" unknown user" );
199
191
}
200
192
201
- return gamesById. at (gameIdsByUser. at (username) );
193
+ return game_store_-> ReadGameByUserId (user_id );
202
194
}
203
195
204
196
StatusOr<GameStatePtr> GameManager::updateGameState (StatusOr<GameState> updateResult,
@@ -207,10 +199,8 @@ StatusOr<GameStatePtr> GameManager::updateGameState(StatusOr<GameState> updateRe
207
199
return InvalidArgumentError (updateResult.status ().message ());
208
200
}
209
201
210
- gamesById.erase (gameId);
211
- gamesById.emplace (gameId, std::make_shared<GameState>(*updateResult));
212
-
213
- return gamesById.at (gameId);
202
+ auto game_state = std::make_shared<GameState>(*updateResult);
203
+ return game_store_->UpdateGame (game_state);
214
204
}
215
205
216
206
StatusOr<GameStatePtr> GameManager::peekAtDrawPile (const string& username) {
@@ -273,4 +263,44 @@ StatusOr<GameStatePtr> GameManager::knock(const string& username) {
273
263
return updateGameState (game->knock (playerIndex), game->getGameId ());
274
264
}
275
265
266
+ std::unordered_set<string> GameManager::getUsersOnline () const {
267
+ auto read_users_status = game_store_->GetUsers ();
268
+ if (!read_users_status.ok ()) {
269
+ return {}; // TODO: bubble status to caller
270
+ }
271
+ return *read_users_status;
272
+ }
273
+
274
+ std::unordered_set<GameStatePtr> GameManager::getGames () const {
275
+ return game_store_->ReadAllGames ();
276
+ }
277
+
278
+ std::unordered_map<string, string> GameManager::getGameIdsByUserId () const {
279
+ auto games_result = game_store_->ReadAllGames ();
280
+ std::unordered_map<string, string> game_ids_by_user{};
281
+ for (auto g : games_result) {
282
+ auto game_id = g->getGameId ();
283
+ for (auto p : g->getPlayers ()) {
284
+ if (p.isPresent () && p.getName ().has_value ()) {
285
+ game_ids_by_user[p.getName ().value ()] = game_id;
286
+ }
287
+ }
288
+ }
289
+ return game_ids_by_user;
290
+ }
291
+
292
+ std::unordered_set<string> GameManager::getUsersByGameId (const string& game_id) const {
293
+ auto game_maybe = game_store_->ReadGame (game_id);
294
+ if (!game_maybe.ok ()) {
295
+ return {};
296
+ }
297
+ std::unordered_set<string> users{};
298
+ for (auto p : (*game_maybe)->getPlayers ()) {
299
+ if (p.isPresent () && p.getName ().has_value ()) {
300
+ users.insert (p.getName ().value ());
301
+ }
302
+ }
303
+ return users;
304
+ }
305
+
276
306
} // namespace golf
0 commit comments