Help with Maze Game Assignment

Pages: 123
Hey sorry, I edited my post quite a bit while you were responding. But yeah, the Entrance is only on the first floor, then their are ladders to the next two floors.
Last edited on
While I was busy typing, you were busy going the wrong way. :)

Please see my previous post about Read() and Print() being unrelated functions. PrintToScreen() has no business trying to read the floors, or for that matter trying to find the entrance. See my previous post for an example of displaying a floor.

Line 10: maze is a local variable. This goes out of scope then PrintToScreen exists. You should be referencing tiles[][].

You may want to add to your Game class an index (or pointer) to the current Floor.





Sorry I guess I'm a bit confused as far as getting entrance and placing player, as well as other important char's, but I separated the print and placePlayer functions. So here is what I have with a printToScreen function, but the only way I got it to work was with the vector like so, this also allows me to check for the entrance in my placePlayer() function.

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
void Floor::printToScreen() const{
	ifstream fin("FloorA.txt");
	if (!fin) cout << "failed";

	vector<string> maze;
	string line;
	while (getline(fin, line)) {
		maze.push_back(line);
	}
	for (int x = 0; x < maze.size(); x++)
	{
		for (int y = 0; y < maze[x].size(); y++)
		{
			cout << maze[x][y] << " ";
		}
		cout << endl;
	}
}

void Floor::placePlayer() {
	ifstream fin("FloorA.txt");
	if (!fin) cout << "failed";

	bool foundEntry = false;	
	vector<string> maze;
	string line;
	int entryX;
	int entryY;

	while (getline(fin, line)) {
		maze.push_back(line);
	}

	for (int x = 0; x < maze.size() && !foundEntry; x++)
	{
		for (int y = 0; y < maze[x].size() && !foundEntry; y++)
		{
			if (maze[x][y] == 'E'){
				entryX = x;
				entryY = y;
			}
		}
	}
    //place player
}


Would there be an easy way to utilize char tiles in the code above, or I could just use the vector maybe?

So I tried to separate the print and the placing player functions, but I'm not sure how to replace the 'E' entrance with the 'P' player character so my player is placed on the maze.

When implementing your Print function, I declared the int ROWS and int COLS as private members of Floor but when it prints, the code is just a bunch of random characters in the general shape of the maze, so I don't think it's reading in the file anywhere. Then if I wanted to use this to place player, could I just call tiles[r][c] in placePlayer() instead of maze?

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

class Floor in header:

1
2
3
4
5
6
7
8
9
10
11
12
class Floor {
	char tiles[20][30];
	const int ROWS = 20;
	const int COLS = 30;
	public:
	//Floor();
	void printToScreen() const;
	void Read (const char * filename);
	vector<string> maze;
	void placePlayer();
	void Print() const;
};


Hopefully I'm sort of going in the right direction now, so I can start working on movement once Player is placed. Thanks again, for the help. I don't mean to go in the wrong direction, I'm just trying to get everything to display and such so I probably strayed from where you were headed.


Last edited on
I'm not reporting this thread, but it is very similar to http://www.cplusplus.com/forum/beginner/158441/ and by the same person.

If you want to overwrite the data in the maze and make it so that you get the maze once, you'll need to store the maze in a local variable. You may also want to store player position in local variables. Then, as you go through the maze you just assign the player to the new location, update his location, and replace the old character.
I separated the print

You're still missing the point of printing a floor. Obviously, you can't print a floor until you're read it. Printing a floor should print the current state of the floor, not what is in the file.

Line 2-9: What are these lines doing here? A Floor is an instance of the class. It could be any of the three floors. A filename specific to a single floor does not belog in a Floor method. This is why it was passed in to Floor::Read as an argument.

Lines 5-9: You still don't get that Floor:Read() is responsible for loading tiles[][]. maze does not belong here. Use the member tiles.

Lines 21-22: Again, these lines don't belong here. An instance of a floor doesn't specifically know which floor it is.

Lines 25-26, 30-32: You don't to want read floorA.txt in order to find the entrance. PlacePlayer() should assume that the floop map has already been read.

Line 25: You want to use tiles[][], not maze.

Would there be an easy way to utilize char tiles in the code above

I've already shown you how to read and print a floor.

Lines 39-40: You're going to want to give Character a setter for current position.
1
2
3
4
void Character::set_position (int new_x, int new_y)
{  x = new_x;
    y = new_y;
}


Lines 39-40 become:
1
2
3
4
    if (tiles[x][y] == 'E')
    {  player.set_position (x,y); 
        tiles[x][y] = 'P';
    }


when it prints, the code is just a bunch of random characters in the general shape of the maze, so I don't think it's reading in the file anywhere

Did you read in the three floors in Game::Read() as I showed you several posts back.

could I just call tiles[r][c]

Don't know what you mean by "call tiles[r][c]]". tiles[r][c] is a char. You can't call a char.

Regarding your Floor header, put your consts before tiles.
1
2
3
    const int ROWS = 20;
    const int COLS = 30;
    char tiles[ROWS][COLS];


Line 9: What's the purpose of this vector? You already have tiles[][].

BTW, I don't appreciate your starting a new thread. Had gRex2595 not mentioned it, I would not have been aware of it. I'm going to ignore that thread, as I've addressed those issues here.
Last edited on
First off, I want to apologize to you, @Abstraction and also to @gRex2595 for opening the new thread. I wanted to ask a specific question regarding that function but it was wrong to open a similar thread. Again, I'm very sorry about that.

So I'm working on implementing everything you suggested in this entire thread so here are my functions so far, and I'm beginning to get into the move function, but I'm not sure how this will work with the virtual aspect of the program. Referencing your example, I started a move function but I don't think it properly utilizes the abstract (virtual function) class requirement.

functions:

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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");

}

void Floor::Print () const {
    for (int r=0; r<ROWS; r++)
    {   for (int c=0; c<COLS; c++)
            cout << tiles[r][c];
        cout << endl;
    }
}

void Character::set_position (int new_x, int new_y)
{  x = new_x;
    y = new_y;
}

void Floor::placePlayer()
{
    bool foundEntry = false;
    int entryX;
    int entryY;

    for (int x = 0; x < ROWS && !foundEntry; x++)
    {
        for (int y = 0; y < COLS && !foundEntry; y++)
        {
            if (tiles[x][y] == 'E')
            {
                player.set_position (x,y);
                tiles[x][y] = 'P';
            }
        }
    }
}

bool Floor::legal_move (int x, int y)
{
    return (x >= 0 && x < ROWS && y >= 0 && y < COLS);
}

void Player::move (int x_incr, int y_incr)
{
    if (! legal_move(player_x + x_incr, player_y + y_incr))
    {
        cout << "You are blocked in this direction" << endl;
        return;
    }
    tiles[player_x+x_incr][player_y+y_incr] = player;
    tiles[player_x][player_y] = ' ';
    player_x += x_incr;
    player_y += y_incr;
}


Error questions:

Line 44: player was not declared in this scope
This is where we are calling are playing constructor isn't it? So in my header I just have Player(); but I don't think it's simply a capitalization issue because player isn't really doing anything yet when we set the position.

Line 52: should legal_move be a member of Floor or Player? Then would I need to inherit Floor since we use ROWS and COLS? It doesn't return errors right now, but I'm not sure if this is correct.

Line 67-72: Scope errors with player_x, player_y, legal_move, tiles, and player.
In your example you have int player_x = y; and int player_y = 5; since we are getting the player position from where the entrance is, would be pass tiles[x][y] to the move function?

Again, I appreciate the help, you have basically been a tutor for the last few weeks so thank you very, very much!



That's starting to look a lot better.

Line 34: You're using foundEntry in your for statements, but you never set it true. You really don't need this bool varaible since you can return out of the function immediately when you've found the entrance.

Lines 35-36: These aren't being used.

Line 44: We don't actually have an instance of Player yet. Remember that Game has a vector of Characters. We haven't established how this vector gets populated yet. Since a Game would have a single Player, it would make sense to assume that characters[0] is the Player. You need to push a Player instance onto this vector at the start of the game. Since the other Characters in the game are ghosts, you're going to want to scan the floor at the time you load it, create each Ghost and push them onto the vector. When the player moves from floor to floor via a ladder, you're going to want to clear the ghosts from the floor being left from the characters vector, and scan the new floor for ghosts (much like you did for the entrance) and add them to the characters vector.

Line 45: Once you've done this you can return immediately out of the function.

Line 52: legal_move belongs as a member of Floor. You're checking a position on the Floor to see if it's legal. You also want to enhance this function to determine if tiles[x][y] is a wall or not.

Line 56: After I posted this function in the thread you got it from, it occurred to me that a better name for x_incr and y_incr would have been x_adj and y_adj. This naming makes a little more sense since x_adj and y_adj can each be -1, 0 or +1. The incr suffix sort of implies that the values are positive.

Lines 67-72: There are no player_x or player_y members. x and y are members of Character. You can replace lines 65-66 with set_position (x+x_incr, y+y_incr); x and y are currently private members of Character. It would make sense to make x and y protected members to that they can be referenced in derived classes.

Scope errors with legal_move, tiles, and player.

Did you add legal_move as a function declaration in the Floor header?

Line 56: This move() function does not overload Character::move() because the signatures are different. Character::move () takes no arguments. This move() function takes two arguments.






Okay, so I got rid of foundEntry all together, as you suggested, then had the function return after the 'P' value is set.

We don't actually have an instance of Player yet...

So this will need to be a separate function within Game, called something like populateMaze, then I'll need to do what you mentioned and set up my vector<char> characters correct?

Once you've done this...

Okay, I added the return.

legal_move...

It's a member of Floor. Where I have my comment in this function, would I just set the player.set_position back to where it was before it attempted to touch the wall? So increment it down by one.

After I posted this..

I adjusted these accordingly.

There are no player_x or player_y members...

Do you mean I should add these as something like tiles[x] = player_x and tiles[y] = player_y?
I have x and y as members of Character. Replaced 65-66 with set_position (x+x...
Made x and y protected.

Did you add legal_move

Yeah this is a member of Floor. I meant to say that these scope errors are in the move() function.

Here's all the code so you can get a better idea of what I have:

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
class Floor {

    const static int ROWS = 20;
    const static int COLS = 30;
    char tiles[ROWS][COLS];
    public:
	Floor();
	void Read (const char * filename);
	void placePlayer();
	void Print() const;
	bool legal_move(int x, int y);
};

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

class Character {
public:
	Character();
	virtual void move();
    private:
	void set_position (int new_x, int new_y);
	char symbol;
	protected:
    int x;
	int y;
};

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

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

#endif // HEADER_H 


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
53
54
55
56
57
58
59
60
61
62
63
64
65
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");

}

void Floor::Print () const {
    for (int r=0; r<ROWS; r++)
    {   for (int c=0; c<COLS; c++)
            cout << tiles[r][c];
        cout << endl;
    }
}

void Character::set_position (int new_x, int new_y)
{  x = new_x;
    y = new_y;
}

void Floor::placePlayer()
{
    for (int x = 0; x < ROWS; x++)
    {
        for (int y = 0; y < COLS; y++)
        {
            if (tiles[x][y] == 'E')
            {
                player.set_position (x,y);
                tiles[x][y] = 'P';
            }
            return;
        }
    }
}

bool Floor::legal_move (int x, int y)
{
    return (x >= 0 && x < ROWS && y >= 0 && y < COLS);
}

void Player::move (int x_adj, int y_adj)
{
    if (tiles[x][y] == '#') {
        cout << "Wall here";
        //set player position back to where they were
    }
    if (! legal_move(player_x + x_adj, player_y + y_adj))
    {
        cout << "You are blocked in this direction" << endl;
        return;
    }
    tiles[player_x+x_adj][player_y+y_adj] = player;
    tiles[player_x][player_y] = ' ';
    set_position (x+x_adj, y+y_adj);
}


Thanks!
So this will need to be a separate function within Game, called something like populateMaze, then I'll need to do what you mentioned and set up my vector<char> characters correct?

Yes. As I pointed out above, you will need to delete ghosts from the characters vector when you exit a floor and then scan new new floor to add the ghosts on the new floor to the characters vector. This assumes that ghosts don't migrate from floor to floor.

Line 46: legal_move() should be a const function. It makes no changes to the floor instance.

would I just set the player.set_position back to where it was before it attempted to touch the wall?

Lines 53-56: These lines are out of place. My suggestion was to enhance legal_move() to detect walls. Remove these lines.

Line 55: You haven't changed the x/y position yet so there is nothing to set back.

Here's legal_move() with detection of walls.
1
2
3
4
bool Floor::legal_move (int x, int y) const
{  return (x >= 0 && x < ROWS && y >= 0 && y < COLS)
          && tilles[x][y] != '#';
}


Do you mean I should add these as something like tiles[x] = player_x and tiles[y] = player_y?

No. There should be no player_x or player_y. A player's x/y position is stored in the base Character's class x and y variables. Also, those statements don't make sense. Your're storing an int (x/y) into a tile which is a char.

Getting back to the virtual move functionality, in Player::move() is where you want to prompt for WASD. What character the user enters determines the values passed as x_adj and y_adj. You might want to move lines 51/65 to a new function such as "move_to_x_y()".
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
void Player::move_to_x_y (int x_adj, int y_adj)
{  if (! legal_move (x + x_adj, y + y_adj))
    {  cout << "You are blocked in this direction" << endl;
        return;
    }
    //  Before overlaying the next tile with 'P', you want to take action 
    //  based on the current contents of that tile.  e.g.  if space, then no    
    //  problem.  If key, take the key, if ladder up, move to next floor, etc.
    tiles[x+x_adj][y+y_adj] = 'P';
    tiles[x][y] = ' ';
    set_position (x+x_adj, y+y_adj);
}

//  Overloads Character::move()
void Player::move () 
{  //  prompt for WASD
    switch (c)
    {
    case 'W':  //  Up
        move_to_x_y (1,0);  
        break;
    //  Other cases for ASD
    //  See other thread for examples
    }
}





Last edited on
Okay so I fixed pretty much everything you mentioned after:
Yes. As I pointed out above, you will need to delete ghosts from the characters vector when you exit a floor and then scan new new floor to add the ghosts on the new floor to the characters vector. This assumes that ghosts don't migrate from floor to floor.


So I'll post those changes at the bottom, but obviously populating and searching the vector<char> characters is a major step in getting pretty much everything else to work so I started working on a populateMaze() function but even after two hours of researching how to save elements into a vector and search for specific characters in a vector, my code is probably pretty useless. Hopefully the logic here is somewhat correct... comments show what I was going for.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void Game::populateMaze()
{
    vector<char>::iterator p, g;
    while (!m_floor[0].eof()) //read in FloorA
    {
        for (int i = 0; i < m_floor.size(); i++){ //scan each element in m_floor
            //for(int j = 0; j < m_floor.size(); j++){
                characters.push_back(Floor); //push to Floor constructor
        }
    }
    p = find (characters.begin(), characters.end(), 'P'); //search for 'P'
        while (p != vector.end())
            p = characters.push_back(Player[p]); //save location in characters as Player
    g = find (characters.begin(), characters.end(), 'G'); //search for 'G'
        while (g != vector.end())
            g = characters.push_back(Ghost[g]); //save location in characters as Ghost
}


Updated functions (not including Print() and set_position which were unchanged):

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

void Floor::placePlayer()
{
    for (int x = 0; x < ROWS; x++)
    {
        for (int y = 0; y < COLS; y++)
        {
            if (tiles[x][y] == 'E')
            {
                player.set_position (x,y);
                tiles[x][y] = 'P';
            }
            return;
        }
    }
}

bool Floor::legal_move (int x, int y) const
{
    return (x >= 0 && x < ROWS && y >= 0 && y < COLS)
            && tiles[x][y] != '#';
}

void Player::move_to_x_y (int x_adj, int y_adj)
{
    if (! legal_move (x + x_adj, y + y_adj))
    {
        cout << "Blocked in this direction" << endl;
        return;
    }
    tiles [x+x_adj][y+y_adj] = 'P';
    tiles [x][y] = ' ';
    set_position (x+x_adj, y+y_adj);

    if (tiles[x][y] = 'K') {
        keys = keys + 1;
    }
}

void Player::move()
{
    char c;
    char load;
    do{
        cout << "Enter move: ";
        switch (c)
        {
        case 'W':
            move_to_x_y(1, 0);
            break;
        case 'A':
            move_to_x_y(0, -1);
            break;
        case 'S':
            move_to_x_y(-1, 0);
            break;
        case 'D':
            move_to_x_y(0, 1);
            break;
        }
        cout << "Load map (press 'L')";
        cin >> load;
    } while (load == 'L');


}


I added the other cases to the move() function and added a while loop to continue prompting for moves and asking to load map.

Hopefully I'm making some progress, with your help. Still a ways to go.

Thanks, as always!


populateMaze
------------------
line 4: m_floor[] is not an istream object. It's a Floor object, which does not have an eof() method.

line 6: m_floor is an array. You need a subscript.

Line 8: This is wrong on two counts. 1) Floor is a class name. You can't push a class name. 2) You're trying to push a Floor onto a vector of char.

Lines 11-13: These lines are contradictory. You're searching for a 'P' which would be a Player object. Then you try to push a Player. Again, you're trying to a class name, not a instance of a class. In other words, you're searching the vector for something you haven't pushed on to it.

You need to search a floor for a 'P', then create a player instance and push that player instance onto the characters array. Likewise, you need to search the current floor for 'G's. When you find a 'G', create a Ghost instance and push that instance onto the characters vector.

The way I was envisioning this was a leave_floor() function. This would remove ghosts from the characters vector. Then a enter_floor () function
that scans the new floor for ghosts and add thoses ghosts to the characters vector. You can implement these functions in one of two ways.
1) Add these functions to the Game class. If you do this, each function would need the appropriate floor passed as an argument.
2) Add these functions to the Floor class. If you do this, then you need to pass the Game object as an argument.

functions
------------
Line 13: return is in the wrong place. It should be after line 10.

line 15: You might was to throw an exception if you get here. It means you haven't found the entrance.

lines 43-44,61-63: What's the purpose of the load variable?

Okay, sorry I think I'm still really confused with the syntax for vectors. I've done lots of research but this code is still incorrect, perhaps you can provide a little better example, but hopefully this is a little closer.

populateMaze():
So this is where most of the trouble is right now as I just can't grasp vectors. I added a player object and ghost object, then pushed the player object into characters (at least that's what I'm trying to do). Then I did the same for ghosts. I added istream eof(); to the Floor class but perhaps I'm approaching this loop all wrong.

leave_floor():
I'm simply replacing the 'P' character with a space.

enter_floor():
Should I scan for a ladder, since this is where the 'P' will start at. Once I figure out the vectors in populateMaze(), this should be a bit easier to code.

Then I think I fixed the errors you mentioned within the other functions.

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
void Game::populateMaze()
{
    Player player;
    Ghost ghost;
    vector<char>::iterator p, g;
    while (!m_floor[0].eof()) //read in FloorA
    {
        for (int rows = 0; rows < sizeof (*m_floor); rows++)
        { for (int columns = 0; columns < sizeof (*m_floor); columns++)
            {
            characters.push_back(m_floor); //push to Floor constructor
            }
        }
    }
    player = find (characters.begin(), characters.end(), 'P'); //search for 'P'
        while (player != vector.end())
            characters.push_back(player); //save location in characters as Player
    ghost = find (characters.begin(), characters.end(), 'G'); //search for 'G'
        while (ghost != vector.end())
            characters.push_back(ghost); //save location in characters as Ghost
}
}
void Game::leave_floor(Floor & m_floor) {
    replace (characters.begin(), characters.end(), 'P', ' ');
}
void Game::enter_floor(Floor & m_floor) {
    if (tiles[x][y] == 'G'){
        characters.push_back(ghost);
    }
}

void Floor::Print () const {
    for (int r=0; r<ROWS; r++)
    {   for (int c=0; c<COLS; c++)
            cout << tiles[r][c];
        cout << endl;
    }
}

void Character::set_position (int new_x, int new_y)
{  x = new_x;
    y = new_y;
}

void Floor::placePlayer()
{
    for (int x = 0; x < ROWS; x++)
    {
        for (int y = 0; y < COLS; y++)
        {
            if (tiles[x][y] == 'E')
            {
                player.set_position (x,y);
                return;
                tiles[x][y] = 'P';
            }
        }
    }
    if (tiles[x][y] != 'E' || 'P'){
            cout << "Entrance not found. Check txt files.";
            break;
    }
}

bool Floor::legal_move (int x, int y) const
{
    return (x >= 0 && x < ROWS && y >= 0 && y < COLS)
            && tiles[x][y] != '#';
}

void Player::move_to_x_y (int x_adj, int y_adj)
{
    if (! legal_move (x + x_adj, y + y_adj))
    {
        cout << "Blocked in this direction" << endl;
        return;
    }
    tiles [x+x_adj][y+y_adj] = 'P';
    tiles [x][y] = ' ';
    set_position (x+x_adj, y+y_adj);

    if (tiles[x][y] = 'K') {
        keys = keys + 1;
    }
}

void Player::move()
{
    char c;
    do{
        cout << "Enter move: ";
        switch (c)
        {
        case 'W':
            move_to_x_y(1, 0);
            break;
        case 'A':
            move_to_x_y(0, -1);
            break;
        case 'S':
            move_to_x_y(-1, 0);
            break;
        case 'D':
            move_to_x_y(0, 1);
            break;
        }


}


I feel like I'm just going in circles with my populateMaze function.

Thanks for the help.
I think I'm still really confused with the syntax for vectors

You were doing fine with vectors in your poker assignment.

I added istream eof(); to the Floor class

As I said before, a Floor is not an istream. There is no such thing as an eof() condition on a floor object.

Line 6: I really don't understand what you're trying to do with this loop. Per your comment, there is no reason to read in Floor. Presumably, you've already read in all three floors via the Game::Read function I already provided. Once you've read in the three floors, you have them in memory. There is no reason to read them in again. You're not reading them here, but you're calling eof() as if you were.

Lines 8-13: A couple of problems here:
1) You're looping through every row and column and trying to push a Floor onto the characters array.
2) sizeof(*m_floor) makes no sense. This tells you how many bytes are in a Floor object. The number of bytes in a Floor object should not be your loop limit.
3) You're trying to push a floor. A floor is not a class derived from Character.
4) m_floor is an array, not a single instance.

lines 15-20: Again, you're trying to search a vector you haven;t populated yet.

Line 24: You're trying to replace an object derived from Character with a space. Not a valid operation.

Lines 27-28: Right idea. A few problems though.
1) You need to iterate through alll the tiles of a floor. There may be multiple ghosts.
2) tiles is a private member of floor, so you can't access tiles directly. You need a getter. See code below.
3) You're trying to push ghost, but ghost is not defined.

Lne 21: Looks like an extra }

Line 53: player is not defined. You need to pass it in as an argument (by reference).

Line 54: return is in the wrong place. Should be after line 55.

Line 59: x and y have gone out of scope (no longer defined).
C++ does not support an implicit left hand side expression (|| 'P').
There is no need for a condition here. If you get here, you're in trouble.
 
    raise std::exception ("Entrance not found");


Line 73: legal_move is a member of Flloor, not Player.

Lines 78-79,82: tiles[][] is not a member of Player.

Line 82: You can't test tiles[x][y] equal to 'K' here because you just overlaid tiles[x][y] with a space at line 79. You're using the assignment operator (=) here, not the equality operator (==).

Line 91: You need to do a cin here to get the character.

Line 106: You need a default case in the event the user typed something other than WASD.

Line 107: You're missing a } and the while condition of the do statement.

I feel like I'm just going in circles with my populateMaze function.

Here's my take on enter_floor, leave_floor, and populate_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
28
29
void Game::enter_floor (Floor & floor)  // We've entered a new floor
{  m_curr_floor = & floor;                     // Save pointer to current floor
    for (int r=0; r<FLOOR::ROWS; r++)  // Assumes ROWS is public 
      for (int c=0; c<FLOOR::COLS; c++) // Ditto for COLS
      {  if (floor.get_tile(r,c) == 'G')
          {  Ghost ghost (r,c);                   // Ghost has a constructor that takes x and y
               characters.push_back (ghost);
           }
      }
}

void Game::leave_floor ()
{  // Iterate through characters vector and remove any ghosts
}

void Game::populateMaze ()
{  Player  player;
    
    Read ();    // Read in the floors (in case you haven't already done this)
    m_floor[0].place_player ();  // Place the player at the entrance of first floor
    characters.push_back (player);  // Add player to characters vector
    enter_floor (m_floor[0]);            // Scan for ghosts
}

void Floor::ladder_up (Game & game)
{  game.leave_floor (*this);  // Remove ghosts from characters vector
    game.enter_floor (*this[1]);  // Scan next floor for ghosts and add to characters
    // Player's x and y stay the same, just on the next floor
}


Lines 23,26: Don't use the m_ prefix here. m_ indicates variable is a member of the class. These are arguments.

Would you mind posting the patterns for floorB and floorC please?





Last edited on
Alright, so I've updated the enter_floor, leave_floor and populateMaze() based on your suggestions, but I'm having some trouble with the introduction of things like

Line 3: m_curr_floor Should this be a seperate function or simply something like m_curr_floor = m_floor since current floor should be the m_floor we are on?

Line 7: get_tile on here are we just calling tiles[][]? Also same question when it's used in leave_floor().
Did I do leave_floor() correctly?

Line 9: Error no matching function call to Ghost::Ghost (int&, int&). Should I just declare ghost(); in my header?

Line 23: Read() was not declared in this scope. Aren't we just calling Read(), seems like this would work.

Line 24: no matching function for call to Floor::place_player(). This may be coming up because I changed place_player in the header to pass player as reference, as you suggested.
See header below.

Line 31: No matching function call to Game::leave_floor(Game&). Not sure what is wrong with this.

Line 48: Prototype does not match any in class. Copy and pasted the exact syntax just in case and this still comes up.

Line 73 - 80: x, y were not declared in this scope. Wouldn't calling legal_move on line 73 fix this? This is the same code you had in an earlier post.
Then set_position was not declared in this scope. Again, I though it is just calling the set_position function and filling in the values.

Line 92, 95, 98, 101: move_to_x_y was not declared in scope. Tried typing in Floor::move_to_x_y but that doesn't fix it.

Then experimenting with my while loop, my thinking is that as long as lives are above 0, the controls can still be entered, but this becomes a scope issue again. (As with most of my errors).

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
void Game::enter_floor(Floor & floor)
{
    m_curr_floor = & floor;
    for (int r=0; r<Floor::ROWS; r++)
        for (int c=0; c<Floor::ROWS; r++)
        {
            if (floor.get_tile(r,c) == 'G')
            {
                Ghost ghost (r,c);
                characters.push_back (ghost);
            }
        }
}
void Game::leave_floor() {
    if (floor.get_tile(r,c) == 'G'){
        replace (characters.begin(), characters.end(), 'G', ' ');
    }
}
void Game::populateMaze()
{
    Player player;

    Read();
    m_floor[0].place_player ();
    characters.push_back (player);
    enter_floor (m_floor[0]);
}

void Game::ladder_up (Game & game)
{
    game.leave_floor (*this);
      game.enter_floor (*this[1]);
}

void Floor::Print () const {
    for (int r=0; r<ROWS; r++)
    {   for (int c=0; c<COLS; c++)
            cout << tiles[r][c];
        cout << endl;
    }
}

void Character::set_position (int new_x, int new_y)
{  x = new_x;
    y = new_y;
}

void Floor::place_player(Player & player)
{
    for (int x = 0; x < ROWS; x++)
    {
        for (int y = 0; y < COLS; y++)
        {
            if (tiles[x][y] == 'E')
            {
                player.set_position (x,y);
                tiles[x][y] = 'P';
                return;
            }
        }
    }
    throw "Entrance not found";
}

bool Floor::legal_move (int x, int y) const
{
    return (x >= 0 && x < ROWS && y >= 0 && y < COLS)
            && tiles[x][y] != '#';
}

void Floor::move_to_x_y (int x_adj, int y_adj)
{
    if (! legal_move (x + x_adj, y + y_adj))
    {
        cout << "Blocked in this direction" << endl;
        return;
    }
    tiles [x+x_adj][y+y_adj] = 'P';
    tiles [x][y] = ' ';
    set_position (x+x_adj, y+y_adj);
}

void Player::move()
{
    char c;
    do{
        cout << "Enter move: ";
        cin >> c;
        switch (c)
        {
        case 'W':
            move_to_x_y(1, 0);
            break;
        case 'A':
            move_to_x_y(0, -1);
            break;
        case 'S':
            move_to_x_y(-1, 0);
            break;
        case 'D':
            move_to_x_y(0, 1);
            break;
        default:
            cout << "Invalid control (WASD only): ";
            cin >> c;
        }
        }while (lives > 0);
}


header:

Line 14: Player has not been declared. Tried to do what we did for enter_floor but doesn't work here?

I think my header needs some re-organizing, that may fix the scope issues, but in order to fix some errors, I think I moved some members and now it is probably just really confusing and leading to more problems.

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
53
54
55
56
57
58
59
#ifndef HEADER_H
#define HEADER_H

using namespace std;

class Floor {

    public:
    const static int ROWS = 20;
    const static int COLS = 30;
    char tiles[ROWS][COLS];
	Floor();
	void Read (const char * filename);
	void place_player(Player & player);
	void Print() const;
	bool legal_move(int x, int y) const;
	void move_to_x_y (int x_adj, int y_adj);
};

class Game {
	private:
	Floor m_floor [3];
	vector<char> characters;
	public:
	void readFloors();
	void populateMaze();
	void leave_floor();
	void enter_floor(Floor & floor);
	void ladder_up(Game & game);
};

class Character {
public:
	Character();
	virtual void move();
    private:
	void set_position (int new_x, int new_y);
	char symbol;
	protected:
    int x;
	int y;
};

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

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

#endif // HEADER_H


As you requested here are the patters for FloorB and FloorC:

FloorB:

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

FloorC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
###############X##############
# CD           G          DC #
#D##################D#######D#
#                            #
# ## ############ ### ###  # #
# #           K   # # #K#  # #
# ################# ### #  # #
#                          # #
# ########################## #
#                            #
# ########################## #
#            #B#  K #K#    # #
# ####D##### # #    # #      #
# #K   #     # #### # # #### #
# # ## # ##### #  # # #    # #
# # ## # #   # ## # # #### # #
# #    # #K  # #           # #
# #### # ### # # ########### #
#                            #
############################## 


So After we all the errors above fixed, I think all that's left is dealing with C-cherries which just make you invincible for 20 moves. Then keys which open doors. Then when the player is on an A or B letter, they can go up (if 'U' is pressed) and down (if 'Z' is pressed). Hopefully these aren't to hard to implement. Also an AI for ghost, which I think I'll just make pretty simple once I get there.

Sorry about all the error questions above, I always try debugging myself before asking on here. If you want to just write your comments as code comments, that may save time since I realize the posts take quite a while to write.

Thanks for the help, sorry for the long post.
Last edited on
Line 3: m_curr_floor Should this be a seperate function

As my comment indicated, this is a pointer. Pointers are not functions. In this case, it belongs in the Game class.
After line 23:
 
  Floor * m_curr_floor;  // Pointer to current floor 


Line 7: get_tile on here are we just calling tiles[][]?

As I've explained before, you don't "call" a character. Since tiles is a private member of Floor, you can't access tiles from outside the Floor class. So in order to access a specific tile, we need a "getter" function.

1
2
3
char Floor::get_tile (int x, int y) const
{  return tiles[x][y];
}


Did I do leave_floor() correctly?

No. A couple of problems:
1) You have to iterate through the all instances in the characters vectors. If an instance is a ghost, the erase that instance in the vector.
2) You're trying to use replace to change a Character instance to a space. A space is not a Character. Somewhat unfortunately naming of the base class, but we probably shouldn't change it. i.e. Character is an instance of your base class, it is not interchangable with a char.

Line 9: Error no matching function call to Ghost::Ghost (int&, int&).

here I introduced a Ghost constructor that takes two values, initial x and y. You need both a declaracation and an implementation for that constructor.
1
2
3
4
Ghost::Ghost (int init_x, int init_y) : Character ('G')
{  x = init_x;
    y = init_y;
}

Note: I've also introduced a Character constructor which takes a char that is used to initialize sym.

Line 23: Read() was not declared in this scope.

I believe you called your read function in Game readFloors(), rather than just Read().

Line 24: no matching function for call to Floor::place_player(). This may be coming up because I changed place_player in the header to pass player as reference

Correct.

Line 31: No matching function call to Game::leave_floor(Game&).

You have ladder_up() as a member of Game. I had ladder_up() as a member of Floor.

Line 48: Prototype does not match any in class.

This occurred because the compiler did not like your declaration of place_player (Player & player) in the header. We'll get to that in a minute (header line 14).

Line 73 - 80: x, y were not declared in this scope.

The problem here is trying to reference variables ina different scope. x and y are protected members of Player, not Floor. In order to access x and y, you're going to need to plass Player as an argument, the use getters to access x and y. Note that get_x() and get_y should be members of the Character class. set_position is also a member of Charater, so it will have to be called through a Player instance.

Line 92, 95, 98, 101: move_to_x_y was not declared in scope

move_to_x_y() is a member of the Floor class. You're in the player class here. My suggestion here is to pass the Game object as an argument to move(). Once you have the game object, you can call game.get_current_floor() (you need to write the getter) and can then access a floor instance.
1
2
3
4
5
6
7
8
void Player::move (Game & game)
{  Floor *  curr_floor;

    curr_floor = game.get_curr_floor();
..
    curr_floor->move_to_x_y (1,0);
...
}


my thinking is that as long as lives are above 0, the controls can still be entered, but this becomes a scope issue again.

Not sure what the scope issue is here. lives is a member of player, so that reference should be okay.

header Line 14: Player has not been declared.

True, because the declaration for Player is after Floor.
The easiest fix for this is to use a forward declaration.
After header line 5:
 
class Player;


Also an AI for ghost, which I think I'll just make pretty simple once I get there.

The usual AI for ghosts is to calculate the angle from the ghost to the player. If the angle is 0-45, the player is more up than right and the ghost attempts to move up (if possible), if the angle is 45-90, the player is more right and the ghost moves right, etc for the remaining angles to 360.

Edit #1: one additional thought. If you're going to give the user the option of not going up a ladder, you going to need to save the fact that there is (was) a ladder there. This is because your move logic is somewhat destructive. i.e. When you move onto a ladder, you place a 'P' there. When you move off that location, you store a ' ' there. The ladder is no longer there.

Edit #2: One of the causes of confusion is that your header line 23 is a vector<char>, while I was assuming it was vector<Character>. I just caught this. When you get to your do_turn() logic, you are going to want to iterate through the vector<Character>, calling each character's virtual move function.
Last edited on
Okay, so I'm still having lots of trouble with leave_floor(). I adjusted my function but I don't think it would work properly, I just can't find a good example online that I can relate to this program.

As far as the errors you mentioned. I added these at the top of the header to fix some of the errors related to instances of Player, Character and Games, as you suggested.

1
2
3
class Player;
class Game;
class Character;


Line 3: added the pointer to the class.
Line 7: changed it to tiles[][]
Line 9: Think I fixed this by updating the class as mentioned above.
Line 23: We have a Read() and readFloors(), based on some of your really early posts. Calling readFloors() here seems to work.
Line 24: (same fix as line 9)
Line 31 and Line 48: updated class
Line 73-80: Another major problem here, I tried to add a simple get_x() function to class but not really sure if these should be pointers like m_curr_floor or their own function. But it seems like we are getting x and y more than we need to so can't we just pass in x and y.

Fixed move_to_x_y based on your code.

All the code 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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
#include <iostream>
#include <vector>
#include <algorithm>
#include <array>
#include <fstream>
#include <string>
#include "header.h"

using namespace std;

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");

}

char Floor::get_tile (int x, int y) const
{
    return tiles[x][y];
}

Ghost::Ghost (int init_x, int init_y) : Character ('G')
{
    x = init_x;
    y = init_y;
}

void Game::enter_floor(Floor & floor)
{
    m_curr_floor = & floor;
    for (int r=0; r<Floor::ROWS; r++)
        for (int c=0; c<Floor::ROWS; r++)
        {
            if (floor.get_tile(r,c) == 'G')
            {
                Ghost ghost (r,c);
                characters.push_back (ghost);
            }
        }
}
/*void Game::leave_floor(Game & game) {
    for (vector<Character>::iterator it = characters.begin(); it != characters.end(); ) {
    it->x += 1;
    if (it->x == 'G') {
        it = characters.erase(it);
    } else {
        ++it;
    }
}
}
*/
void Game::populateMaze()
{
    Player player;

    readFloors();
    m_floor[0].place_player ();
    characters.push_back (player);
    enter_floor (m_floor[0]);
}

void Floor::ladder_up (Game & game)
{
    game.leave_floor (*this);
      game.enter_floor (*this[1]);
}

void Floor::ladder_down (Game & game)
{
    game.leave_floor (*this);
        game.enter_floor (*this[0]);
}

void Floor::Print () const {
    for (int r=0; r<ROWS; r++)
    {   for (int c=0; c<COLS; c++)
            cout << tiles[r][c];
        cout << endl;
    }
}

void Character::set_position (int new_x, int new_y)
{  x = new_x;
    y = new_y;
}

void Floor::place_player(Player & player)
{
    player.get_x();
    player.get_y();
    for (int x = 0; x < ROWS; x++)
    {
        for (int y = 0; y < COLS; y++)
        {
            if (tiles[x][y] == 'E')
            {
                player.set_position (x,y);
                tiles[x][y] = 'P';
                return;
            }
        }
    }
    throw "Entrance not found";
}

bool Floor::legal_move (int x, int y) const
{
    return (x >= 0 && x < ROWS && y >= 0 && y < COLS)
            && tiles[x][y] != '#';
}

void Floor::move_to_x_y (int x_adj, int y_adj)
{
    if (! legal_move (x + x_adj, y + y_adj))
    {
        cout << "Blocked in this direction" << endl;
        return;
    }
    tiles [x+x_adj][y+y_adj] = 'P';
    if (tiles[x][y] == 'B' || 'A') { //check for ladders
        tiles[x][y] == tiles [x][y]; }
    else {
        tiles [x][y] = ' '; }
    set_position (x+x_adj, y+y_adj);
}

void Player::move(Game & game)
{
    Floor * curr_floor;
    curr_floor = game.get_curr_floor();
    char c;
    do{
        cout << "Enter move: ";
        cin >> c;
        switch (c)
        {
        case 'W':
            curr_floor->move_to_x_y(1, 0);
            break;
        case 'A':
            curr_floor->move_to_x_y(0, -1);
            break;
        case 'S':
            curr_floor->move_to_x_y(-1, 0);
            break;
        case 'D':
            curr_floor->move_to_x_y(0, 1);
            break;
        default:
            cout << "Invalid control (WASD only): ";
            cin >> c;
        }
        }while (lives > 0);
}

void Ghost::move(Game & game) {

   char c;
    do{
        switch (c)
        {
        case 'W':
            curr_floor->move_to_x_y(-1, 0);
            break;
        case 'A':
            curr_floor->move_to_x_y(0, 1);
            break;
        case 'S':
            curr_floor->move_to_x_y(1, 0);
            break;
        case 'D':
            curr_floor->move_to_x_y(0, -1);
            break;
        case 'U':
            ladder_up();
            break;
        case 'Z':
            ladder_down();
            break;
        default:
            curr_floor->move_to_x_y(0,0);
        }
        }while (lives > 0);

}

void Player::items() {
    if (tiles[x][y] == 'K') {
        keys = int + 1;
    }
    if (tiles[x][y] == 'D') {
        while (keys > 0) {
            player.set_position(x,y);
        }
        else {
            cout << "Look for key.";
        }
    }

}


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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#ifndef HEADER_H
#define HEADER_H

using namespace std;
class Player;
class Game;
class Character;
class Floor {

    public:
    const static int ROWS = 20;
    const static int COLS = 30;
    char tiles[ROWS][COLS];
	Floor();
	void Read (const char * filename);
	char get_tile(int x, int y) const;
	void place_player(Player & player);
	void Print() const;
	bool legal_move(int x, int y) const;
	void move_to_x_y (int x_adj, int y_adj);
	void ladder_up(Game & game);
	void ladder_down(Game & game);
};

class Game {
	private:
	Floor m_floor [3];
	vector<Character> characters;
	public:
	void readFloors();
	void populateMaze();
	void leave_floor(Game & game);
	void enter_floor(Floor & floor);
	Floor * m_curr_floor;
};

class Character {
public:
	Character(char);
	virtual void move();
    private:
	void set_position (int new_x, int new_y);
	void get_x();
	void get_y();
	char symbol;
	protected:
    int x;
	int y;
};

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

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

#endif // HEADER_H 


Things we still need to add:

I started a really basic AI for ghosts, I know this doesn't do much but I'd like to get everything working before I worry to much about the AI. Right now it at least moves around a little bit.

Your "Edit#1": added a few lines at Line 132

Your "Edit#2": I updated the vector to vector<Character> characters because simply doing vector<Character> doesn't seem to work and also, aren't all our characters.something still going to be used or would this all need to be changed.

So unfortunately for me this assignment is due tomorrow and after nearly two weeks of working on it, it is not done. I've struggled through it working with you but even after that much time, I don't have it.

I'm trying to scramble and get things like Keys and Doors and Cherries to work so I started those functions at the bottom. Cherries will need to work with ghosts somehow, because when a Cherry is picked up, the Player has 20 moves of invincibility. I don't think items should be as hard to implement as all the stuff we have done so far.

It would be hugely helpful if you could go through the code and debug because it seems like most errors happen between the syntax placed in functions and the header. Then could you post the updated code here?

Thanks again and hopefully we can get a workable product by tonight.

I don't have time to debug this right now. I might be able to spend some time on it this evening. I will point out some obvious errors though.

1
2
3
4
5
6
7
void Game::leave_floor(Game & game) 
{  for (vector<Character>::iterator it = characters.begin(); it != characters.end(); ) 
    if (it->get_sym() == 'G')           //  Need getter for symbol
        it = characters.erase(it);
    else 
        ++it;
}


Line 83:
 
    game.enter_floor (*this[-1]);  // previous floor 


Lines 101-102: These lines do nothing.

Lines 149-158: I'd suggest using lower case.

Line 131: Both Player::move and Ghost::move call move_to_x_y(). This line always puts a 'P' in the new locations. That won't work real well for ghosts.

Line 132: You're trying to use an implicit left hand side again.. That won't work.

Line 170: c is an unitialized variable. The simplest thing to do here is generate a random number from 0-3 and then use 0-3 in your case labels. You might want to be a little more sophisticated than that since a random direction might not be legal (a wall). If not legal, you want to generate another random number.

Lines 186-191: I didn't think ghosts could move from floor to floor?

Line 199: I don't see any calls to items().

Line 201: WTF is this? This is not how you increment a variable.

Line 205: Should opening a door decrement the number of keys held.

Your PMs are off. Please turn them on.









Alright I turned PM's on. Going through it, I fixed what I could but the Ghost and Cherry still needs work so this doesn't make much sense in the code. I'm not sure how to say if a Cherry is picked up, make invincibility for 20 moves. Then if P is on G, check if it has been more than 20 moves.

Line 131: Both Player::move...

Not sure what else I could do here since P needs to be placed on the tile. I placed an if statement to check if the tile is a Ghost but then I just call to items(), because if it were a Ghost, then the Player would need to have picked up a cherry, else loose a life.

Line 170: c is...

I added a rand() statement in Ghost, but I'm not sure if this would be between 0 and 3 or 1 and 3, which means my cases may be off.

I don't think ghosts can move from...

You're right, changed this.

Line 199: I don't...

Made a call to items() in move_to_x_y, but not sure if this is utilized correctly with Ghosts and Keys.

Line 201: WTF is this?

Fixed that. Stupid mistake.

Line 205: Should opening a door.

Fixed this by decrementing keys in the if statement for 'D'.

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#include <iostream>
#include <vector>
#include <algorithm>
#include <array>
#include <fstream>
#include <string>
#include "header.h"

using namespace std;

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");

}

char Floor::get_tile (int x, int y) const
{
    return tiles[x][y];
}

Ghost::Ghost (int init_x, int init_y) : Character ('G')
{
    x = init_x;
    y = init_y;
}

void Game::enter_floor(Floor & floor)
{
    m_curr_floor = & floor;
    for (int r=0; r<Floor::ROWS; r++)
        for (int c=0; c<Floor::ROWS; r++)
        {
            if (floor.get_tile(r,c) == 'G')
            {
                Ghost ghost (r,c);
                characters.push_back (ghost);
            }
        }
}
void Game::leave_floor(Game & game)
{  for (vector<Character>::iterator it = characters.begin(); it != characters.end(); )
    if (it->get_sym() == 'G')           //  Need getter for symbol
        it = characters.erase(it);
    else
        ++it;
}

void Game::populateMaze()
{
    Player player;

    readFloors();
    m_floor[0].place_player (player);
    characters.push_back (player);
    enter_floor (m_floor[0]);
}

void Floor::ladder_up (Game & game)
{
    game.leave_floor (*this);
      game.enter_floor (this[0]);
}

void Floor::ladder_down (Game & game)
{
    game.leave_floor (*this);
        game.enter_floor (*this[-1]);
}

void Floor::Print () const {
    for (int r=0; r<ROWS; r++)
    {   for (int c=0; c<COLS; c++)
            cout << tiles[r][c];
        cout << endl;
    }
}

void Character::set_position (int new_x, int new_y)
{  x = new_x;
    y = new_y;
}

void Floor::place_player(Player & player)
{
    for (int x = 0; x < ROWS; x++)
    {
        for (int y = 0; y < COLS; y++)
        {
            if (tiles[x][y] == 'E')
            {
                player.set_position (x,y);
                tiles[x][y] = 'P';
                return;
            }
        }
    }
    throw "Entrance not found";
}

bool Floor::legal_move (int x, int y) const
{
    return (x >= 0 && x < ROWS && y >= 0 && y < COLS)
            && tiles[x][y] != '#';
}

void Floor::move_to_x_y (int x_adj, int y_adj)
{
    if (! legal_move (x + x_adj, y + y_adj))
    {
        cout << "Blocked in this direction" << endl;
        return;
    }
    tiles [x+x_adj][y+y_adj] = 'P';
    if (tiles[x][y] == 'G'){
        items();
    }
    if (tiles[x][y] == 'B' || 'A') { //check for ladders
        tiles[x][y] == tiles [x][y]; }
    if (tiles[x]y] == 'K') {
        items();
    }
    else {
        tiles [x][y] = ' '; }
    set_position (x+x_adj, y+y_adj);
}

void Player::move(Game & game)
{
    Floor * curr_floor;
    curr_floor = game.get_curr_floor();
    char c;
    do{
        cout << "Enter move: ";
        cin >> c;
        switch (c)
        {
        case 'w':
            curr_floor->move_to_x_y(1, 0);
            break;
        case 'a':
            curr_floor->move_to_x_y(0, -1);
            break;
        case 's':
            curr_floor->move_to_x_y(-1, 0);
            break;
        case 'd':
            curr_floor->move_to_x_y(0, 1);
            break;
        case 'u':
            ladder_up();
            break;
        case 'z':
            ladder_down();
            break;
        default:
            cout << "Invalid control (WASD only): ";
            cin >> c;
        }
        }while (lives > 0);
}

void Ghost::move(Game & game) {

    srand(time(NULL));
    randNum = rand() % 3;
    do{
        switch (randNum)
        {
        case (randNum = 0):
            curr_floor->move_to_x_y(-1, 0);
            break;
        case (randNum = 1):
            curr_floor->move_to_x_y(0, 1);
            break;
        case (randNum = 2):
            curr_floor->move_to_x_y(1, 0);
            break;
        case (randNum = 3):
            curr_floor->move_to_x_y(0, -1);
            break;
        default:
            curr_floor->move_to_x_y(0,0);
        }
        }while (lives > 0);

}

void Player::items() {
    if (tiles[x][y] == 'K') {
        keys = keys + 1;
    }
    if (tiles[x][y] == 'D') {
        while (keys > 0) {
            player.set_position(x,y);
            keys = keys - 1;
        }
        else {
            cout << "Look for key.";
        }
    }
    if (tiles[x][y] == 'G'){
    }

}



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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#ifndef HEADER_H
#define HEADER_H

using namespace std;
class Player;
class Game;
class Character;
class Floor {

    public:
    const static int ROWS = 20;
    const static int COLS = 30;
    char tiles[ROWS][COLS];
	Floor();
	void Read (const char * filename);
	char get_tile(int x, int y) const;
	void place_player(Player & player);
	void Print() const;
	bool legal_move(int x, int y) const;
	void move_to_x_y (int x_adj, int y_adj);
	void ladder_up(Game & game);
	void ladder_down(Game & game);
};

class Game {
	private:
	Floor m_floor [3];
	vector<Character> characters;
	public:
	void readFloors();
	void populateMaze();
	void leave_floor(Game & game);
	void enter_floor(Floor & floor);
	Floor * m_curr_floor;
};

class Character {
public:
	Character(char);
	virtual void move();
    private:
	void set_position (int new_x, int new_y);
	void get_x();
	void get_y();
	char symbol;
	protected:
    int x;
	int y;
};

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

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

#endif // HEADER_H 


The only thing this doesn't have is an option for the user to enter something like 'w5' and move 5 spaces up, max increment is 5. This is a feature we are supposed to have but it's only worth a few points so I don't mind if we don't get it implemented.

Like I said, it is due today, but if you could get it debugged and sent to me tonight or even tomorrow. I would greatly appreciate it. Seems like most of the mistakes are fairly minor and I could really use your help on just going through it and sending the updated code.

Thanks for all your help so far!!



Last edited on
Line 131: Both Player::move...

I changed move_to_x_y to accept a Character as the first argument. This allows move_to_x_y to operate on both a Player and a Ghost. Note that line 10 requires a getter to be added to Character to get the symbol of the Character.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool Floor::move_to_x_y (Character & character, int x_adj, int y_adj)
{   char save_next;
    int x, y;
    
    x = character.get_x();
    y = character.get_y();
    if (! legal_move(x + x_adj, y + y_adj))
        return false;
    save_next = tiles[x+x_adj][y+y_adj];
    tiles[x+x_adj][y+y_adj] = character.get_sym();
    tiles[x][y] = ' ';
    character.set_position (x+x_adj, y+y_adj);
    cout << "Moved " << character.get_sym() << " to " << x+x_adj << "," << y+y_adj << endl;
    //  Act on save_next
    return true;
}

Line 123: You don't want this cout here. It will get annoying when move_x_to_y is also used with ghosts.

Line 130: You're still trying to do an implied left hand side That doesn't work in C++.
1
2
3
4
5
6
if (tiles[x][y] == 'B' || 'A') 
// evaluates to:
if (true || 'A') 
// which is always true
// This should be: 
if (tiles[x][y] == 'B' || tiles[x][y] == 'A') 


Line 126: What happens when a player lands on a ghost? You might want to be smart enough to move away from a ghost.

Line 131: You're assigning a tile to itself. That does nothing.

Lines 151,157: The arguments are incorrect. w should decrement x (-1,0) and s should increment x (1,0).
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
void Player::move (Game & game)
{   char c;
    Floor * floor;
    bool    moved = false;
    
    floor = game.get_curr_floor();    
    do
    {   cout << "Enter move: ";
        cin >> c;
        switch (c)
        {
        case 'w':
            moved = floor->move_to_x_y (*this, -1, 0);
            break;
        case 'a':
            moved = floor->move_to_x_y (*this, 0, -1);
            break;
        case 's':
            moved = floor->move_to_x_y(*this, 1, 0);
            break;
        case 'd':
            moved = floor->move_to_x_y(*this, 0, 1);
            break;
        default: 
            cout << "Not a valid move" << endl;                        
        }   
    } 
    while (! moved);
}


line 177: srand() should only be called one at the beginning of the program. Move this call to main().

Lines 179-199: Your case labels are not valid. They are assignments. Also your modulo should be 4 if you want the ghost to move in all four directions.

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
void Ghost::move (Game & game)
{   Floor *     floor;
    int         r;
    bool        moved = false;
    
    floor = game.get_curr_floor();
    do 
    {   r = rand() % 4;
        switch (r)
        {
        case 0:
            moved = floor->move_to_x_y (*this, 1, 0);
            break;
        case 1:
            moved = floor->move_to_x_y (*this, 0, -1);
            break;
        case 2:
            moved = floor->move_to_x_y(*this, -1, 0);
            break;
        case 3:
            moved = floor->move_to_x_y(*this, 0, 1);
            break;
        }
    }
    while (! moved);
}

Note that move_to_x_y() now returns a bool to indicate if the attempted move was valid or not.

How to do you have your main set up?
Pages: 123