Search algorithms for the 8-puzzle solution
GameState.hpp
Go to the documentation of this file.
1 /**
2  * @author Douglas De Rizzo Meneghetti (douglasrizzom@gmail.com)
3  * @date 2017-6-22
4  * @brief Describes a single state in the 8-puzzle.
5  */
6 
7 
8 #ifndef SEARCH_GAMESTATE_HPP
9 #define SEARCH_GAMESTATE_HPP
10 
11 #include <string>
12 #include <iostream>
13 #include "GameAction.hpp"
14 
15 using namespace std;
16 
17 //!Describes a single state in the 8-puzzle.
18 class GameState {
19  private:
20 
24 
25  //! Turns a string representation of an 8-puzzle into an array
26  //! \param s string representation of an 8-puzzle
27  //! \return Pointer to an int array containing the state of the puzzle
28  int *toIntArray(string s) {
29 
30  //count spaces in string
31  int spaces = 1;
32  for (int i = 0; i < s.length(); i ++) {
33  if (s[i] == ' ') spaces ++;
34  }
35 
36  //create array
37  int *ret = new int[spaces];
38 
39  int currentInt = 0;
40  string intString = "";
41  for (int i = 0; i < s.length(); i ++) {
42 
43  //glues it number together until a space is found
44  if (s[i] != ' ') {
45  intString += s[i];
46  }
47 
48  //space means end of number, turn it into an int and put it in the return array
49  if ((s[i] == ' ') || (i == s.length() - 1)) {
50  ret[currentInt ++] = stoi(intString);
51  intString = "";
52  }
53  }
54  return ret;
55  }
56 
57  public:
58 
59  //!Default constructor, everything starts as 0 or NULL
61  depth = dimension = 0;
62  representation = NULL;
63  parent = NULL;
64  }
65 
66  //! Creates a GameState from a string, where tile numbers are separated by spaces in a single line
67  //! \param s string representation on an 8-puzzle state
68  explicit GameState(string s) {
69  depth = 0;
70  parent = NULL;
71 
72  // converts string to int[]
73  int *conversion = toIntArray(s);
74 
75  // calculates puzzle dimension from array size
76  dimension = sizeof(conversion) / sizeof(conversion[0]) + 1;
77 
78  // searches for duplicate numbers inside the array
79  for (int i = 0; i < dimension * dimension - 1; i ++) {
80  for (int j = i + 1; j < dimension * dimension; j ++) {
81  if (conversion[i] == conversion[j]) {
82  cout << conversion[i] << conversion[j] << i << j;
83  throw invalid_argument(s);
84  }
85  }
86  }
87 
88  // initializes array of arrays to keep the 8-puzzle state
89  representation = new int *[dimension];
90 
91  //iterates through rows and columns, adding the numbers converted from the input string
92  int val = 0;
93  for (int x = 0; x < dimension; x ++) {
94  representation[x] = new int[dimension]; // initialize inner arrays
95 
96  for (int y = 0; y < dimension; y ++) {
97  representation[x][y] = conversion[val ++];
98  }
99  }
100 
101  delete[] conversion;
102  }
103 
104  //! Copy constructor of the class
105  //! \param obj object that will be copied
106  GameState(const GameState &obj) {
107  dimension = obj.dimension;
108  depth = obj.depth;
109 
110  representation = new int *[dimension];
111 
112  for (int x = 0; x < dimension; x ++) {
113  representation[x] = new int[dimension];
114 
115  for (int y = 0; y < dimension; y ++) {
116  representation[x][y] = obj.representation[x][y];
117  }
118  }
119 
120  parent = obj.parent;
121  }
122 
123  //! Creates a new state based on a given previous state and applying an action to it
124  //! \param previous the previous state
125  //! \param action the action to be applied to the previous state
126  GameState(GameState *previous, GameAction action) {
127  //dimension is the same, depth is one lower, parent is the state passed in the argument
128  dimension = previous->dimension;
129  depth = previous->depth + 1;
130  parent = previous;
131  representation = new int *[dimension];
132 
133  // first, copy the state
134  for (int x = 0; x < dimension; x ++) {
135  representation[x] = new int[dimension];
136 
137  for (int y = 0; y < dimension; y ++) {
138  representation[x][y] = previous->representation[x][y];
139  }
140  }
141 
142  //find the 0
143  int *pos_0 = find(0);
144  int x = pos_0[0], y = pos_0[1];
145  delete[] pos_0;
146  int tmp = 0;
147 
148  // validate action
149  if (action != RIGHT and action != LEFT and action != UP and action != DOWN)
150  throw invalid_argument("No previous action to build the new GameState.");
151 
152  if (! isValid(action)) {
153  throw invalid_argument("Invalid action for current game state.");
154  }
155 
156  // move the 0 according to the given action
157  // keep the value of the tile where the 0 is being move to in tmp
158  switch (action) {
159  case RIGHT:tmp = previous->representation[x + 1][y];
160  representation[x + 1][y] = 0;
161  break;
162 
163  case LEFT:tmp = previous->representation[x - 1][y];
164  representation[x - 1][y] = 0;
165  break;
166 
167  case UP:tmp = previous->representation[x][y - 1];
168  representation[x][y - 1] = 0;
169  break;
170 
171  case DOWN:tmp = previous->representation[x][y + 1];
172  representation[x][y + 1] = 0;
173  break;
174  }
175 
176  //put the value in tmp where the 0 was
177  representation[x][y] = tmp;
178  }
179 
180  //!Destructor of GameState
182  // erase inner arrays
183  // since the value of `dimension` is lost, calculate it here
184  int dim = sizeof(representation[0]) / sizeof(representation[0][0]) + 1;
185  for (int i = 0; i < dim; i ++)
186  delete[] representation[i];
187 
188  //erase outer array
189  delete[] representation;
190 
191  representation=NULL;
192  parent = NULL;
193  depth = dimension = 0;
194  }
195 
196  //! Checks whether an action is valid for this state.
197  //! Actions are invalid if they'd move the 0 tile outside of the board.
198  //! \param action the action to be validated
199  //! \return true if the action can be applied in this state, otherwise false
200  bool isValid(GameAction action) {
201  if (action != RIGHT and action != LEFT and action != UP and action != DOWN)
202  return false;
203 
204  int *pos_0 = find(0);
205  int x = pos_0[0], y = pos_0[1];
206 
207  delete[] pos_0;
208 
209  return ! (action == RIGHT and x == dimension - 1 ||
210  action == LEFT and x == 0 ||
211  action == UP and y == 0 ||
212  action == DOWN and y == dimension - 1);
213  }
214 
215  //! Checks whether two GameState objects are equal.
216  //! They are equal if they have the same board dimensions and the same values in the same positions of their board.
217  //! \param other The GameState to be compared to
218  //! \return true if equal, otherwise false
219  bool operator==(const GameState &other) const {
220  if (dimension != other.dimension) return false;
221 
222  for (int x = 0; x < dimension; x ++) {
223  for (int y = 0; y < dimension; y ++) {
224  if (representation[x][y] != other.representation[x][y]) return false;
225  }
226  }
227  return true;
228  }
229 
230  //! Counts the number of inversions in the puzzle.
231  //! An inversion occurs whenever a number comes before a smaller number in the puzzle.
232  //! \return number of inversions in this states of the puzzle
234  int inv_count = 0;
235 
236  int rasterDimension = dimension * dimension;
237  int *raster = new int[rasterDimension];
238 
239  int current = 0;
240  for (int x = 0; x < dimension; x ++) {
241  for (int y = 0; y < dimension; y ++) {
242  raster[current ++] = representation[x][y];
243  }
244  }
245 
246  for (int i = 0; i < rasterDimension - 1; i ++)
247  for (int j = i + 1; j < rasterDimension; j ++)
248  if (raster[i] != 0 and raster[j] != 0 and raster[i] > raster[j])
249  inv_count ++;
250 
251  delete[] raster;
252 
253  return inv_count;
254  }
255 
256  //! Check if the state is solvable.
257  //! \return a state is solvable if the number of inversions in it is even.
258  bool isSolvable() {
259  return countInversions() % 2 == 0;
260  }
261 
262  //! Compares two GameState objects. Tile numbers are compared in order. If this[i,j] < other[i,j], this < other.
263  //! This operator is useful in case objects need to be sorted inside a data structure, for example.
264  //! \param other The GameState to be compared to
265  //! \return true if this < other, otherwise false
266  bool operator<(const GameState &other) const {
267  if (dimension != other.dimension) throw invalid_argument("Puzzles of different dimensions aren't comparable");
268 
269  for (int x = 0; x < dimension; x ++) {
270  for (int y = 0; y < dimension; y ++) {
271  if (representation[x][y] < other.representation[x][y]) return true;
272  }
273  }
274  return false;
275  }
276 
277  //! Inequality operator for GameState. This is the negation of the equality operator.
278  //! \param other The GameState to be compared to
279  //! \return false if equal, otherwise true
280  bool operator!=(const GameState &other) const {
281  return ! (*this == other);
282  }
283 
284  int getDimension() const {
285  return dimension;
286  }
287 
288  //! Returns a multi-line string representation of the 8-puzzle board state
289  //! \return multi-line string representation of the state
290  string to_string() const {
291  string tmp = "";
292 
293  for (int x = 0; x < dimension; x ++) {
294  for (int y = 0; y < dimension; y ++) {
295  tmp.append(std::to_string(representation[x][y])).append(" ");
296  }
297  tmp.append("\n");
298  }
299  return tmp;
300  }
301 
302  //! Returns a single line string representation of the 8-puzzle board state
303  //! \return single line string representation of the state
304  string to_line_string() const {
305  string tmp = "";
306 
307  for (int x = 0; x < dimension; x ++) {
308  for (int y = 0; y < dimension; y ++) {
309  tmp.append(std::to_string(representation[x][y])).append(" ");
310  }
311  }
312  return tmp.erase(tmp.find_last_not_of(" \n\r\t") + 1);
313  }
314 
315  //! Finds the position of a tile in the board
316  //! \param value the value of the tile to look for
317  //! \return int array where [0] contains x position and [1] y position of the number
318  int *find(int value) {
319  for (int x = 0; x < dimension; x ++) {
320  for (int y = 0; y < dimension; y ++) {
321  if (representation[x][y] == value) return new int[2]{x, y};
322  }
323  }
324 
325  throw invalid_argument("Inexistent number.");
326  }
327 
328  void setRepresentation(int **representation) {
329  this->representation = representation;
330 
331  //dimension must be recalculated
332  dimension = sizeof(representation[0]) / sizeof(representation[0][0]) + 1;
333  }
334 
335  GameState *getParent() const {
336  return parent;
337  }
338 
339  int getDepth() const {
340  return depth;
341  }
342 };
343 
344 #endif // SEARCH_GAMESTATE_HPP
GameAction
Actions allowed in the 8-puzzle and its variants.
Definition: GameAction.hpp:11
bool operator<(const GameState &other) const
Compares two GameState objects.
Definition: GameState.hpp:266
int dimension
Definition: GameState.hpp:22
int * toIntArray(string s)
Turns a string representation of an 8-puzzle into an array.
Definition: GameState.hpp:28
int countInversions()
Counts the number of inversions in the puzzle.
Definition: GameState.hpp:233
bool operator==(const GameState &other) const
Checks whether two GameState objects are equal.
Definition: GameState.hpp:219
GameState(string s)
Creates a GameState from a string, where tile numbers are separated by spaces in a single line...
Definition: GameState.hpp:68
GameState(GameState *previous, GameAction action)
Creates a new state based on a given previous state and applying an action to it. ...
Definition: GameState.hpp:126
string to_line_string() const
Returns a single line string representation of the 8-puzzle board state.
Definition: GameState.hpp:304
int getDimension() const
Definition: GameState.hpp:284
int * find(int value)
Finds the position of a tile in the board.
Definition: GameState.hpp:318
bool isSolvable()
Check if the state is solvable.
Definition: GameState.hpp:258
bool isValid(GameAction action)
Checks whether an action is valid for this state.
Definition: GameState.hpp:200
GameState()
Default constructor, everything starts as 0 or NULL.
Definition: GameState.hpp:60
void setRepresentation(int **representation)
Definition: GameState.hpp:328
GameState * getParent() const
Definition: GameState.hpp:335
GameState * parent
Definition: GameState.hpp:23
int ** representation
Definition: GameState.hpp:21
Describes a single state in the 8-puzzle.
Definition: GameState.hpp:18
~GameState()
Destructor of GameState.
Definition: GameState.hpp:181
GameState(const GameState &obj)
Copy constructor of the class.
Definition: GameState.hpp:106
string to_string() const
Returns a multi-line string representation of the 8-puzzle board state.
Definition: GameState.hpp:290
int getDepth() const
Definition: GameState.hpp:339
bool operator!=(const GameState &other) const
Inequality operator for GameState.
Definition: GameState.hpp:280