nsnake
Classic snake game for the terminal
BoardParser.cpp
1 #include <Game/BoardParser.hpp>
2 #include <Config/Globals.hpp>
3 #include <Config/INI.hpp>
4 #include <Misc/Utils.hpp>
5 
6 #include <fstream>
7 #include <vector>
8 #include <string>
9 
10 // HACK This will be initialized at `Globals::init()`
11 std::string BoardParser::directory = "";
12 
13 std::string BoardParser::extension = "nsnake";
14 
15 
16 
17 
18 Board* BoardParser::load(std::string name)
19 {
20  std::string filename = (BoardParser::directory +
21  name +
22  "." +
24 
25  return BoardParser::loadFile(filename);
26 }
27 
28 Board* BoardParser::loadFile(std::string filename)
29 {
30  std::ifstream file(filename.c_str());
31 
32  if (!(file.is_open()))
33  throw BoardParserException("Can't open file '" + filename + "'");
34 
35  // Tells what's the current line on the file
36  // (independent of comments and empty lines)
37  int line_count = 0;
38 
39  // The whole file has two parts: metadata and level definition.
40  //
41  // Let's read the whole file, line by line, adding the respective
42  // parts to each of these buffers.
43  //
44  // We'll handle them separately later.
45  std::string metadata_buffer;
46  std::string level_buffer;
47 
48  // This will get used to the end of the
49  // function.
50  std::string current_line = "";
51 
52  while (std::getline(file, current_line))
53  {
54  ++line_count;
55 
56  current_line = Utils::String::trim(current_line);
57 
58  // We only care for the line that tells a level
59  // definition will start.
60  if (current_line != "start")
61  metadata_buffer += (current_line + '\n');
62 
63  else
64  {
65  // Yay, start of the level definition!
66  bool parsed_level = false;
67 
68  while (std::getline(file, current_line))
69  {
70  ++line_count;
71 
72  current_line = Utils::String::trim(current_line);
73 
74  if (current_line == "end")
75  {
76  parsed_level = true;
77  break;
78  }
79 
80  level_buffer += (current_line + '\n');
81  }
82 
83  if (! parsed_level)
84  {
85  // End-of-file...
86  // Something wrong happened
88  "Abrupt ending of file while parsing level at line " +
89  Utils::String::toString(line_count)
90  );
91  }
92  // Finished parsing the level!
93  // Back to the metadata.
94  }
95  }
96 
97  // Now we'll analyze the level definition we just got from the file
98 
99  int player_start_x = 1; // It's (1, 1) because if it somehow starts
100  int player_start_y = 1; // at (0, 0) it will always end up in a wall
101  // and die right at the beginning
102 
103  // Finally, when we read the level we have
104  // two states for each tile - "wall" or "not wall"
105  std::vector<std::vector<bool> > rawBoard;
106 
107 
108  std::vector<std::string> level_lines = Utils::String::split(level_buffer, '\n');
109 
110  for (size_t j = 0; j < (level_lines.size()); j++)
111  {
112  current_line = level_lines[j];
113 
114  if (current_line.empty())
115  continue;
116 
117  std::vector<bool> rawBoardLine;
118 
119  // And now we go through each char on the line
120  // checking if it's a wall, blank space or the
121  // player's starting point.
122  //
123  for (size_t i = 0; i < current_line.size(); i++)
124  {
125  if (current_line[i] == SNAKE_CHAR)
126  {
127  player_start_x = i;
128  player_start_y = rawBoard.size();
129 
130  // It IS an empty space, after all...
131  rawBoardLine.push_back(false);
132  }
133  else
134  rawBoardLine.push_back(current_line[i] == WALL_CHAR);
135  }
136 
137  // Commit this line to the level
138  rawBoard.push_back(rawBoardLine);
139  }
140 
141  // I know it's counter-intuitive, but the width
142  // and height is just like this
143  int board_width = rawBoard[0].size();
144  int board_height = rawBoard.size();
145 
146  Board* board = new Board(board_width,
147  board_height,
148  ((Globals::Game::teleport) ?
149  Board::TELEPORT :
150  Board::SOLID));
151 
152  board->setBoard(rawBoard);
153 
154  board->setStartX(player_start_x);
155  board->setStartY(player_start_y);
156 
157  // Remember that metadata up there?
158  // Let's get every present metadata through an INI parser
159  std::stringstream stream;
160  stream << metadata_buffer;
161  INI::Parser parser(stream);
162 
163  board->setMetadata("name", parser["name"]);
164  board->setMetadata("author", parser["author"]);
165  board->setMetadata("date", parser["date"]);
166  board->setMetadata("comment", parser["comment"]);
167 
168  return board;
169 }
170 std::vector<std::string> BoardParser::listLevels()
171 {
172  std::vector<std::string> levels = Utils::File::ls(BoardParser::directory);
173 
174  // Remove files that doesn't end with the default file extension
175  //
176  // Also, don't store the full path, only it's basename
177  // (like "file" and not "/path/to/file")
178  //
179  for (std::vector<std::string>::iterator it = levels.begin();
180  it != levels.end();
181  ++it)
182  {
185 
186  else
187  {
188  // When we remove an element of a vector
189  // it points to the next element.
190  it = levels.erase(it);
191 
192  // We need to decrement it because the `for`
193  // will increment at the end
194  --it;
195  }
196  }
197 
198  return levels;
199 }
200 
Loads, reads and parses the contents of an INI file (or string).
Definition: INI.hpp:156
std::string extension(std::string path)
Returns the extension of a file.
Definition: Utils.cpp:294
static std::vector< std::string > listLevels()
Lists all levels found by the game.
static Board * loadFile(std::string filename)
Loads and parses the level at filename.
Definition: BoardParser.cpp:28
static std::string directory
Default directory where the level files are.
Definition: BoardParser.hpp:38
A level where the snake runs and eats fruits.
Definition: Board.hpp:32
void setMetadata(std::string name, std::string value)
Sets a meta information from this level.
Definition: Board.cpp:165
static Board * load(std::string filename)
Loads and parses level with name.
Definition: BoardParser.cpp:18
void setBoard(std::vector< std::vector< bool > > &newBoard)
Sets the whole level content.
Definition: Board.cpp:139
static std::string extension
Default extension for nSnake level files.
Definition: BoardParser.hpp:47
Custom exception class to specify an error that occurred during a level loading.
Definition: BoardParser.hpp:12
std::vector< std::string > ls(std::string path)
Lists all files withing #path.
Definition: Utils.cpp:201
std::string basename(std::string path)
Returns the component of a pathname (file name and extension).
Definition: Utils.cpp:263
std::string dropExtension(std::string path)
Returns the filename without it&#39;s extension.
Definition: Utils.cpp:305