nsnake
Classic snake game for the terminal
Loading...
Searching...
No Matches
ScoreFile.cpp
1#include <Game/ScoreFile.hpp>
2#include <Game/BoardParser.hpp>
3#include <Misc/Utils.hpp>
4#include <Config/INI.hpp>
5
6#include <stdlib.h> // getenv()
7#include <fstream> // ofstream
8
9// HACK This will be initialized at `Globals::init()`
10std::string ScoreFile::directory = "";
11
12std::string ScoreFile::extension = "nsnakescores";
13
14
16 points(0),
17 speed(0),
18 level(""),
19 fruits(0),
20 random_walls(false),
21 teleport(false),
22 board_size(Globals::Game::LARGE),
23 board_scroll_delay(0),
24 board_scroll_left(false),
25 board_scroll_right(false),
26 board_scroll_up(false),
27 board_scroll_down(false)
28{ }
29
31{
32 if (this->level != other.level)
33 return false;
34
35 // First thing we gotta check is if this score
36 // was made on Arcade Mode (level is empty)
37 //
38 // If that's the case, then we care for the
39 // board size
40 if (this->level.empty())
41 {
42 return (this->fruits == other.fruits &&
43 this->random_walls == other.random_walls &&
44 this->teleport == other.teleport &&
45 this->speed == other.speed &&
46 this->board_scroll_delay == other.board_scroll_delay &&
47 this->board_scroll_left == other.board_scroll_left &&
48 this->board_scroll_right == other.board_scroll_right &&
49 this->board_scroll_up == other.board_scroll_up &&
50 this->board_scroll_down == other.board_scroll_down &&
51 this->board_size == other.board_size);
52 }
53
54 // If not, the board size is not important, since levels
55 // can have any size.
56 return (this->fruits == other.fruits &&
57 this->random_walls == other.random_walls &&
58 this->teleport == other.teleport &&
59 this->speed == other.speed &&
60 this->board_scroll_delay == other.board_scroll_delay &&
61 this->board_scroll_left == other.board_scroll_left &&
62 this->board_scroll_right == other.board_scroll_right &&
63 this->board_scroll_up == other.board_scroll_up &&
64 this->board_scroll_down == other.board_scroll_down);
65}
66
67
68
69
70
72{
73 // 1. Delete the arcade score fileerase this one
74 // 2. Lists all files under the score dir and erase
75 // the ones ending with a score extension
76
77 Utils::File::rm_f(Globals::Config::scoresFile);
78
79 std::vector<std::string> files = Utils::File::ls(ScoreFile::directory);
80
81 for (size_t i = 0; i < files.size(); i++)
82
84 Utils::File::rm_f(files[i]);
85}
86
87
88
89
90
91ScoreFile::ScoreFile(std::string levelName):
92 highScore(NULL),
93 level_name(levelName)
94{ }
95
97{
98 // Make it point nowhere, since we're refreshing
99 // the score entries.
100 this->highScore = NULL;
101
102 // Score files are dependent of the level name.
103 std::string score_file = (ScoreFile::directory +
104 this->level_name +
105 "." +
107
108 // Will fall back to default high score file
109 // (Arcade Mode) if no level was specified
110 if (this->level_name.empty())
111 score_file = Globals::Config::scoresFile;
112
113 if (! Utils::File::exists(score_file))
114 throw ScoreFileException("File '" + score_file + "' doesn't exist");
115
116 // Reading whole file's contents into a buffer
117 std::ifstream file;
118 file.open(score_file.c_str());
119
120 std::stringstream buffer;
121 buffer << file.rdbuf();
122 file.close();
123
124 std::stringstream contents;
125 contents << Utils::Base64::decode(buffer.str());
126
127 // Parsing file's contents as INI
128 INI::Parser ini(contents);
129
130 // If it's a score file from a different major version,
131 // how should we react?
132 // No need to worry about minor versions.
133 std::string version = ini["version"];
134
135 if (version[0] != Globals::version[MAJOR])
136 {
137 // Compare versions, lower, higher, whatever...
138 Globals::Error::old_version_score_file = true;
139
140 throw ScoreFileException("File '" + score_file + "' has an old version format");
141 }
142
143 // Going through each group on the INI file
144 // (each score the user had)
145 for (INI::Level::Sections::const_iterator it = ini.top().ordered_sections.begin();
146 it != ini.top().ordered_sections.end();
147 ++it)
148 {
149 // This is SOO ugly!
150 // We should NOT have to worry about INI parser's internals!
151 INI::Level ini_score = (*it)->second;
152
153 ScoreEntry entry;
154 entry.level = ini_score["level"];
155 entry.points = Utils::String::to<unsigned int>(ini_score["points"]);
156 entry.speed = Utils::String::to<unsigned int>(ini_score["speed"]);
157 entry.fruits = Utils::String::to<int>(ini_score["fruits"]);
158 entry.random_walls = Utils::String::to<bool>(ini_score["random_walls"]);
159 entry.teleport = Utils::String::to<bool>(ini_score["teleport"]);
160
161 entry.board_scroll_delay = Utils::String::to<int>(ini_score["board_scroll_delay"]);
162 entry.board_scroll_left = Utils::String::to<bool>(ini_score["board_scroll_left"]);
163 entry.board_scroll_right = Utils::String::to<bool>(ini_score["board_scroll_right"]);
164 entry.board_scroll_up = Utils::String::to<bool>(ini_score["board_scroll_up"]);
165 entry.board_scroll_down = Utils::String::to<bool>(ini_score["board_scroll_down"]);
166
167 int board_size = Utils::String::to<int>(ini_score["board_size"]);
168 entry.board_size = Globals::Game::intToBoardSize(board_size);
169
170 this->entries.push_back(entry);
171 }
172
173 // Finally, we have to pick the highest score
174 // according to these game settings.
175 ScoreEntry tmp_score;
176 tmp_score.level = this->level_name;
177 tmp_score.speed = Globals::Game::starting_speed;
178 tmp_score.fruits = Globals::Game::fruits_at_once;
179 tmp_score.random_walls = Globals::Game::random_walls;
180 tmp_score.teleport = Globals::Game::teleport;
181 tmp_score.board_size = Globals::Game::board_size;
182 tmp_score.board_scroll_delay = Globals::Game::board_scroll_delay;
183 tmp_score.board_scroll_left = Globals::Game::board_scroll_left;
184 tmp_score.board_scroll_right = Globals::Game::board_scroll_right;
185 tmp_score.board_scroll_up = Globals::Game::board_scroll_up;
186 tmp_score.board_scroll_down = Globals::Game::board_scroll_down;
187
188 for (size_t i = 0; i < (this->entries.size()); i++)
189 {
190 if (tmp_score.isLike(this->entries[i]))
191 {
192 this->highScore = &(this->entries[i]);
193 break;
194 }
195 }
196 if (this->highScore == NULL)
197 {
198 this->entries.push_back(tmp_score);
199 this->highScore = &(this->entries[this->entries.size() - 1]);
200 }
201}
203{
204 // Score files are dependent of the level name.
205 std::string score_file = (ScoreFile::directory +
206 this->level_name +
207 "." +
209
210 // Will fall back to default high score file
211 // if no level was specified
212 if (this->level_name.empty())
213 score_file = Globals::Config::scoresFile;
214
215 // Tries to create file if it doesn't exist.
216 // If we can't create it at all let's just give up.
217 if (! Utils::File::exists(score_file))
218 {
219 Utils::File::create(score_file);
220
221 if (! Utils::File::exists(score_file))
222 throw ScoreFileException("Could not create file '" + score_file + "'");
223 }
224
225 // We'll recreate the whole score file from scratch
226 INI::Parser ini;
227 ini.create();
228 ini.top().addKey("version", std::string(VERSION));
229
230 // Adding each score entry on the file
231 for (size_t i = 0; i < (this->entries.size()); i++)
232 {
233 std::string score_name = "score" + Utils::String::toString(i);
234
235 ini.top().addGroup(score_name);
236
237 ini(score_name).addKey("level", this->entries[i].level);
238 ini(score_name).addKey("points", Utils::String::toString(this->entries[i].points));
239 ini(score_name).addKey("speed", Utils::String::toString(this->entries[i].speed));
240 ini(score_name).addKey("fruits", Utils::String::toString(this->entries[i].fruits));
241
242 ini(score_name).addKey("random_walls", Utils::String::toString(this->entries[i].random_walls));
243 ini(score_name).addKey("teleport", Utils::String::toString(this->entries[i].teleport));
244
245 int board_size = Globals::Game::boardSizeToInt(this->entries[i].board_size);
246 ini(score_name).addKey("board_size", Utils::String::toString(board_size));
247
248 ini(score_name).addKey("board_scroll_delay", Utils::String::toString(this->entries[i].board_scroll_delay));
249 ini(score_name).addKey("board_scroll_left", Utils::String::toString(this->entries[i].board_scroll_left));
250 ini(score_name).addKey("board_scroll_right", Utils::String::toString(this->entries[i].board_scroll_right));
251 ini(score_name).addKey("board_scroll_up", Utils::String::toString(this->entries[i].board_scroll_up));
252 ini(score_name).addKey("board_scroll_down", Utils::String::toString(this->entries[i].board_scroll_down));
253 }
254
255 std::stringstream contents;
256 ini.dump(contents);
257
258 std::ofstream file;
259 file.open(score_file.c_str());
260 file << Utils::Base64::encode(contents.str());
261 file.close();
262}
264{
265 // No high score until now, we've made it!
266 if (! this->highScore)
267 {
268 this->entries.push_back(*score);
269 this->highScore = &(this->entries[this->entries.size() - 1]);
270 return true;
271 }
272
273 // Wrong game settings?
274 if (! score->isLike(*this->highScore))
275 return false;
276
277 if ((score->points) > (this->highScore->points))
278 {
279 this->highScore->points = score->points;
280
281 return true;
282 }
283 return false;
284}
285
Definition Game.hpp:17
Loads, reads and parses the contents of an INI file (or string).
Definition INI.hpp:157
void dump(std::ostream &stream)
Outputs the contents of the INI file to #stream.
Definition INI.cpp:84
Level & top()
Returns the top level of this INI file.
Definition INI.cpp:79
void create()
Creates a blank INI registry.
Definition INI.cpp:215
Custom exception class to specify an error that occurred during a level loading.
Definition ScoreFile.hpp:14
static std::string directory
Default directory where we store the game score files.
Definition ScoreFile.hpp:97
bool handle(ScoreEntry *score)
Checks if #score is the highest score and make it so.
ScoreFile(std::string levelName)
Creates a new score handler for the level #levelName.
Definition ScoreFile.cpp:91
static void eraseAll()
Erases all high score files.
Definition ScoreFile.cpp:71
void load()
Loads all high score entries based on a level name.
Definition ScoreFile.cpp:96
ScoreEntry * highScore
Maximum high score obtained for the current game.
static std::string extension
Default extension to save the score files.
void save()
Saves all the current scores on the file.
All global settings to the game.
Definition Globals.hpp:14
char version[3]
Game version (format MMP - Major Minor Patch).
Definition Globals.cpp:13
std::string encode(std::string str)
Transforms #str into a Base64 equivalent.
Definition Utils.cpp:439
std::string decode(std::string const &s)
Transforms a Base64-encoded #str into it's regular string equivalent.
Definition Utils.cpp:488
std::vector< std::string > ls(std::string path)
Lists all files withing #path.
Definition Utils.cpp:201
bool create(std::string path)
Creates empty file #path.
Definition Utils.cpp:164
void rm_f(std::string path)
Forcibly removes file within #path.
Definition Utils.cpp:152
std::string extension(std::string path)
Returns the extension of a file.
Definition Utils.cpp:294
bool exists(std::string path)
Tells if #path exists.
Definition Utils.cpp:84
Contains a "scope" of the INI file.
Definition INI.hpp:72
Sections ordered_sections
All Sections in the original order of the INI file.
Definition INI.hpp:126
void addKey(std::string name, std::string value)
Creates a new key #name with #value.
Definition INI.cpp:27
void addGroup(std::string name)
Creates a new child group with #name.
Definition INI.cpp:4
A single entry on the high-score file.
Definition ScoreFile.hpp:28
bool random_walls
If random walls were spawned on this level.
Definition ScoreFile.hpp:43
bool isLike(ScoreEntry &other)
Tells if both scores were made on exact same game settings.
Definition ScoreFile.cpp:30
std::string level
On which level the user made this score.
Definition ScoreFile.hpp:37
int fruits
How many fruits at once were allowed on this level.
Definition ScoreFile.hpp:40
bool teleport
If teleport was enabled on this level.
Definition ScoreFile.hpp:46
unsigned int speed
Under which game speed the score was made.
Definition ScoreFile.hpp:33
unsigned int points
How many points the user got.
Definition ScoreFile.hpp:30
ScoreEntry()
Creates an empty score entry.
Definition ScoreFile.cpp:15
Globals::Game::BoardSize board_size
How large was the game board on this score.
Definition ScoreFile.hpp:52