Help with Maze Game Assignment

Pages: 123
Hello, I'm trying to get started on a Maze game assignment and I want to make sure I'm setting up my classes correctly, before moving into functions and reading level files. The full assignment instructions are quite long so here is the class outline and an example level to read in.

Brief outline of goals and rules:
*Enable user to walk through maze
*Doors require keys
*Ghost wanders around
*User can pick up cherries to handle ghosts
*User must reach ladder to get to next maze
*Ladders go to next level (maze) until level with exit

Here is the class outline (given by instructor):

Game class - Array of floors, Array of characters, function to read in floors from files
Floor class - char[][] tiles, void print to screen, constructor
Character class: ABSTRACT - int x/y loc, char symbol, virtual move
Ghost class (inherits Character) - move (automated)
Player class (inherits Character) - move (prompt for WASD), keys(s), Invincibility counter, hold up to 3 keys

My header (so far):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#ifndef HEADER_H
#define HEADER_H

class Game {
public:
	int floor [3];
	int characters [];
	
	void readFloors();
}

class Floor {
	char tiles; 
	void printToScreen;
	Print();
	~Print();	
}

class Character {
	int x;
	int y;
	char symbol;
}

class Ghost: public Character {

}

class Player: public Character {
	
	void controls();
	int lives;
	int keys;
}

#endif // HEADER_H 


What should I do for the ABSTRACT class?

Also, what's the best way to read in files that look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
##############################
#      K                     #
#  ############## ### ###  # #
#      K    #   # #C# #K#  # #
# ######### # A # # # # #  # #
#      K    #   #          # #
# ############D#####D####### #
#                            #
#   C         G        C     #
#                            #
# ######D##############D#### #
#      #  C         #K#    # #
# #### ######## #   # #      #
# #K   #      # ### # # #### #
# # ## # #### #   # # #    # #
E # ## # #    ### # # #### # #
# #    # #K           D    # #
# #D#### ################### #
#                    K       #
############################## 


Map info:
# = Wall
D = Door
A = Ladder up and down, A and B
B = Ladder up and down, B and C
P = Player
G = Ghost
K = Key
C = Cherry
E = Entrance
X = Exit

Thanks for the help!
Last edited on
Line 6: Outline says game contains an array of floors. You have an array of 3 ints.

Line 7: You can't have any array of ints without a dimension.

Line 13: Outline says a 2D array of tiles. You have a single char.

Line 14: What is this? It is neither a variable not a function.

Line 16: Looks like you're trying to make a print destructor. Destructor names must match the name of the class with a ~ in front.

Line 19: Your Character class requires a virtual function declaration for move.

Line 25,29: Your Ghost and Player classes require a move function that overrides the virtual function declaration in Character.

what's the best way to read in files that look like this

Use getline.
Last edited on
Okay, hey @Abstraction! Thanks for your help again.

Here's my updated header file with comments on what I think the virtual functions should do:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#ifndef HEADER_H
#define HEADER_H

class Game {
public:
	int floor [3];
	int characters [];
	void readFloors();
}

class Floor {
	char tiles[20][30]; 
	void printToScreen();
	Floor();
	~Floor();	
}

class Character {
	int x;
	int y;
	char symbol;
	virtual void move() { return controls; } //use controls for move function
}

class Ghost: public Character {
	virtual void move() { return invalid; } //cannot move because of ghost
}

class Player: public Character {
	
	virtual void move(); //override the move function with the keyboard control entered
	int lives;
	int keys;
}

#endif // HEADER_H 


Questions:

Line 6: Outline says game contains an array of floors. You have an array of 3 ints.

What type of array would floors be? Since there are three floors with many characters.

Line 7: You can't have any array of ints without a dimension.

Then same question here, how can I determine the dimension, since each floor is different.

On Line 12 where my tiles array is declared, should this actually be [19][29] since 0 is included in the array? However the levels are each going to be 20 rows by 30 columns.

Think I fixed the other errors.

Thanks again.
What type of array would floors be?

Well, you have an class named Floor. One would think that would represent a floor.

4
5
6
7
 
class Game 
{  Floor   m_floor [3];  // Note: declaration of Floor must precede Game
    ...


Line 7: Since you can have an arbitrary number of characters in the game, I'd make this a vector<Character>.

how can I determine the dimension, since each floor is different.

It would be very unusual if every floor had different dimensions. Line 12, you have this as a 20x30 array, which seems reasonable.

On Line 12 where my tiles array is declared, should this actually be [19][29]

No. If you want a 20x30 array, you declare it as [20][30]. When subscripting it, you address it as 0-19 and 0-29. Good idea to define global constants for the max number of rows and columns.

Line 22: Where/what is controls. not defined.

Line 26: Where/what is invalid? Not defined. Your Ghost::move() function is responsible for moving a ghost object on the map. Your comment implies the player can't move there. That has nothing to do with moving the ghost on the map.

Lines 22,26,31: These functions should be public, otherwise you're not going to be able to access them.


Okay, made the suggested changes.

Yeah, for my virtual functions, should I just leave them as
virtual void move();

in each class or return controls like in the proposed code below?

Looking at virtual function examples, it looked like you had to return something, so I was thinking controls, because these are what will be needed in order to 'move' the character and ghost. Just not sure what would be returned if not controls.

Then as far as defining controls, like you mentioned. Something like a char array work?
char controls[4] = ['w', 'a', 's', 'd'];

Then I could update the controls for the character and ghost virtual functions.

Updated code with my proposed changes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#ifndef HEADER_H
#define HEADER_H

class Floor {
	char tiles[20][30]; 
	void printToScreen();
	Floor();
	~Floor();	
}

class Game {
public:
	Floor m_floor [3];
	vector<Character> characters; 
	void readFloors();
}

class Character {
	public:
	int x;
	int y;
	char controls[4] = ['w','a','s','d']; //would this work?
	char symbol;
	virtual void move();
}

class Ghost: public Character {
	public:
	virtual void move() {return controls}; //would this work?
}

class Player: public Character {
	public:
	virtual void move() {return controls}; //would this work?
	int lives;
	int keys;
}

#endif // HEADER_H 


Thanks!!
Line 8: not clear why you need a destructor for Floor.

Line 22: Not sure why this is even here. You probably want these as case values in a switch statement.

Lines 21-22,23: I would make these variables private. Since you have variables that need to be initialized, you probably want a default constructor for Character.

Line 29,34: You can't return something from a void function. I'd take out the return as opposed to changing the type of the function.

Lines 9,16,25,30,37: All your class declarations need a ; terminating the declarations.

Lines 35-36: Ditto regarding making these private and needing a default constructor.

Okay, how's this looking:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#ifndef HEADER_H
#define HEADER_H

class Floor {
	char tiles[20][30]; 
	void printToScreen();
	Floor();
};

class Game {
	public:
	Floor m_floor [3];
	vector<Character> characters; 
	void readFloors();
};

class Character {
    public:
	virtual void move();
    private:
	int x;
	int y;
	char symbol;
	Character();
};

class Ghost: public Character {
	public:
	virtual void move(); 
};

class Player: public Character {
	public:
	virtual void move(); 
	private:
	Player();
	int lives;
	int keys;
};

#endif // HEADER_H 


Definitely have some questions about the virtual void move() stuff, but let me know if everything is fixed.

Thanks!
Last edited on
Line 7: Floor's constructor should be public.

Lines 12-13: These should be private.

Line 24: Character's constructor should be public.

Line 36: Player's constructor's should be public.

Otherwise, looks okay so far.
Okay, great made the changes. I'll start working on the functions.

I'll probably ask you a few questions about reading the files in but I'll see if I can get an outline up first.

Thanks!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#ifndef HEADER_H
#define HEADER_H

class Floor {
	char tiles[20][30]; 
	void printToScreen();
	public:
	Floor();
};

class Game {
	private:
	Floor m_floor [3];
	vector<Character> characters; 
	public:
	void readFloors();
};

class Character {
public:
	Character();
	virtual void move();
    private:
	int x;
	int y;
	char symbol;
};

class Ghost: public Character {
	public:
	virtual void move(); 
};

class Player: public Character {
public:
	Player();
	virtual void move(); 
	private:
	int lives;
	int keys;
};

#endif // HEADER_H 
Last edited on
BTW, check your poker game thread. I replied regarding the context errors you were getting.
http://www.cplusplus.com/forum/beginner/157042/2/#msg809098
Oh yea, I saw that. Thank you! I'll try to revisit that stuff in a few weeks, once I'm done with this assignment and the next one.

Thanks!
Alright, so I'm trying to get the file input to work so I can read in each maze floor.

Here is the function:

1
2
3
4
5
6
7
8
9
10
11
12
void readFloors() {
	
	size_t row = 0;
	size_t col = 0;
	ifstream input("FloorA.txt");
	while( input && chars_read < 30) {
		input >> two_dim_array[20][30];
		if( ++col == 30) {
			++row;
			col = 0;
	}
}


So I know that I need to use my char tiles[20][30] array, but this is declared in my Floor class. Would the best way to use this in my readFloors() functions be to do something like the poker game where we just do.

 
Floor char tiles;


obviously, the above wouldn't work properly, but I'm trying to get the array into the readFloors function.

Then would the loop I have, allow me to analyze the input of each character?

Thanks.
Not clear from what you've posted so far, if each floor is in a different file, or if they are concatenated in the same file. That really doesn't make a lot of difference, just affects where you want to open the file.

Don't you think that reading a floor should be a function of the Floor class?

Line 6: Where is chars_read?

Line 7: This is bogus.

Consider the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const int ROWS = 20;

void Floor::Read (const char * filename)
{   size_t row = 0;
    ifstream input(filename);
	
    while (row < ROWS && input >> tiles[row++]) 
        ;    
}

void Game::readFloors()
{   m_floor[0].Read ("floora.txt");
    m_floor[1].Read ("floorb.txt");
    m_floor[2].Read ("floorc.txt");
}


This above code assumes one file per level. If all levels are in the same file, then open the single file in the calling function and pass the istream as the argument.
 
void Floor::Read (istream & input)



Yeah, the levels are each in there own file. So three separate txt files, each with a different maze of chars.

Okay, moving readFloors() into the Floor class makes more sense, not sure what I was going for originally.

I'll adjust the code as you suggested, yours seems simpler and makes a bit more sense for this type of problem.

Would you recommend simple if or case statements for interpreting the array of characters, read in from each floor? So should I say if the char is a '#' then player hits wall, if 'K', player gets key, etc.?

Thanks for the help, I'll continue working on some of the other functions.
Last edited on
Okay, moving readFloors() into the Floor class makes more sense

Note that I used two "read" functions. readFloors() as a member of the Game class which is responsible for loading all floors. And Floor::Read() which is responsible for reading one Floor object from a file.

Would you recommend simple if or case statements for interpreting the array of characters

If have more than 3 tests of the same value, I prefer a switch statement.
I also prefer a switch statement when a default case is important.
Okay so I have a question regarding checking the array for the characters on the Floor. Would you recommend just doing a for loop to check the columns and another for loop to check the rows, so this would examine every element on the floor. Or just check the entire array all at once with a loop since our tiles array should have everything in it, but then how would I look for each character, since there are many different possibilities on the map.

Would each switch statement have a for loop to check if '#' is present, then a wall is here? Something like that?

EDIT (as of 6:00 pm)-------------------------------------

I asked a question like this on Stack Overflow

http://stackoverflow.com/questions/28785691/how-to-check-each-element-in-array/28786221#28786221

but I'm still a bit confused on how to set this up in some switch statements for checking if each character is a wall, players, key, etc.

So I got my printToScreen() function to print the maze:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void Floor::printToScreen()
{
	ifstream f("FloorA.txt");
	string str;
	int cols = 0;
	int rows = 0;

	char maze[20][30];

	int line = 0;
	while(getline(f, str)){
		const char * chars = str.c_str();
		for(int i = 0; i<str.length(); i++){
			maze[line][i] = chars[i];
		}
		cols = str.length();
		line++;
		rows++;
	}
    for (int i=0; i<line; i++){
		for(int j=0; j<rows; j++){
			cout << maze[i][j] << " ";
		}
		cout << endl;
	}
	cout << "Rows: " << rows << " Columns: " << cols;
}


I know the above code isn't really using what you had above but I just wanted to get a function where I could print it, then analyze each character. Your functions are much simpler though so I would like to work with those. But I'm trying to check the spot [i] for instance, to see if it is a wall (#) character. Then I would have the player not be able to move. Keys, they would pick it up, etc.

Sorry if this post is a bit confusing...
Last edited on
You're looking at this the wrong way. There is no need for for loops to scan the board on each move. You will need to scan the board to find the entrance (or test each character for the entrance when you're reading the board). That becomes the Player's initial position.

The only thing that moves in the game is the player (in response to WASD) and ghosts. You have a vector of Characters in your Game object. On each "turn", you will want to iterate through the vector of characters, calling each character's move() function. Since each chararacter (player or ghost) has x and y attributes, you know where the character is on the board. If the player wants to move left (A), you examine the position to the left of the player and if it is a legal position, you move the player there and take action based on the type of that tile.
For ghosts, you will want to develop an algorithm for their movement on each "turn". The usual algorithm is to move towards the player, but they have to know how to navigate walls. i.e. If they're blocked by a wall, they have to change direction.

You might want to give your Character class a "bool legal_move()" function. legal_move() checks that the proposed new x and y values are inbounds and not a wall. Moving through a door is only legal if you have a key. Depending on the rules for Player and Ghosts, legal_move() might vary for each (i.e. a virtual function). Take a look at this post for an example of legal_move() and move_player(): http://www.cplusplus.com/forum/beginner/157261/#msg806609

but I'm still a bit confused on how to set this up in some switch statements for checking if each character is a wall, players, key, etc.

There are two parts to this. There is "pre-move" checking to determine if a proposed position is legal (not a wall). Then there are "post-move" actions to take, eat a cherry, move up a ladder, open a door, quit when you find the exit, etc.

Last edited on
thanks for posting this thread. i cant really find console games exercises in google
Okay, @Abstraction. I understand what you're saying, that makes more sense since scanning the entire board each time wouldn't help much. Here is what I have so far for getting the location of the entrance, then I'll work on implementing the moves and legal_move functions like you mentioned. So here is the code including scanning for the entrance.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
void Floor::printToScreen()
{
	ifstream f("FloorA.txt");
	string str;
	int cols = 0;
	int rows = 0;
	int MAX_ROWS = 20;
	int MAX_COLUMNS = 30;

	char maze[20][30];

	int line = 0;
	while(getline(f, str)){
		const char * chars = str.c_str();
		for(int i = 0; i<str.length(); i++){
			maze[line][i] = chars[i];
		}
		cols = str.length();
		line++;
		rows++;
	}
    for (int i=0; i<line; i++){
		for(int j=0; j<rows; j++){
			cout << maze[i][j] << "";
		}
		cout << endl;
	}
	cout << "Rows: " << rows << " Columns: " << cols << endl << endl;

	bool entrance_found = false;
       unsigned int entrance_row = 0;
       unsigned int entrance_column = 0;
    for (rows = 0; rows < MAX_ROWS; rows++)
        {
    for (cols = 0; cols < MAX_COLUMNS; ++cols)
        {
    if (maze[rows][cols] == 'E')
    {
      entrance_found = true;
      entrance_row = rows;
      entrance_column = cols;
      break;
    }
        }
    if (entrance_found)
    {
    cout << "Entrance found" << endl;
    break;
    }
        }
}


So right now this code outputs the maze, although it seems to cut off the maze at 20 columns for some reason?

So then I'm not sure how to assign this entrance_found value to where my Player will start from? So I guess this is where the int x, int y values come in?

For the vector of characters, are you talking about the char characters on the map? So would things like walls and keys be stored in my characters vector?

Last edited on
Your first snippet is doing two things. 1) reading in a floor and 2) printing that floor.

Reading and printing are two separate and unrelated operations. You don't want to combine them into the same function. You're going to want to call printToScreen() after each "turn". You don't want to read the file for the floor on every turn.

To display a floor is simply:
1
2
3
4
5
6
7
void Floor::Print () const
{   for (int r=0; r<ROWS; r++)
    {   for (inr c=0; c<COLS; c++)
            cout << tiles[r][c];
        cout << endl;  
    }
}


Would this work for identifying the 'E' within the maze? Then assign the row/column to the P (player) to start from?

Yes. Make it a function though. It should be a member of Floor since Floor hold the tiles for a single floor. I would expect the entrance to always be on the first floor, since one would normally start on the first floor and level up. But it could vary depending on how devious your professor is.






Pages: 123