dynamically allocate input from text file into struct array

I'm trying to dynamically allocate information from a text file using a structure.

The console keeps printing out large numbers instead of what I need it to from the text file, so it leaves me thinking that I didn't allocate correctly.

any pointers (hah see what i did there) as to what I'm doing wrong would be greatly appreciated.

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
#include <iostream>
#include <cstdio>
#include <string>
#include <fstream>
#include <sstream>
 
using namespace std;
 
struct Room
{
    string name;
    string description;
    int exits[4];
};
 
int main(int argc, char** argv)
{
    //input file stream
	Room r;
    ifstream fin;
    string temp;
    string rooms;
    int count, numRooms;
   
    //checking command line arguments count
    if (argc != 2)
    {
        cout << "Error: Usage, must input only 1 file to read." << endl;
        exit(1);
    }
    
    fin.open(argv[1]);
    
    //checking if file is open.
    if (!fin.is_open())
    {
        cout << "Error: File is not open" << endl;
        return 1;
    }
   
    count = 0;
    //count the ~'s in the file
    while (getline(fin, temp))
    {
        if(temp == "~")
        {
            count++;
        }
    }
    
    numRooms = (count / 3);
    //allocate the memory for the rooms
    Room* room;
    
    room = new Room[numRooms];
 
    //read the room information from file
	
	while(getline(fin, rooms))
	{
		getline(fin, room[numRooms].name);
		getline(fin, room[numRooms].description);
		fin >> room[numRooms].exits[4];
	}
	
	for (int i = 0; i < 3; i++){
		cout << room[i].name << endl;
		cout << room[i].description << endl;
		cout << room[i].exits[4] << endl;
	}

	delete[] room;

	return 0;

}
    



File contents:

Room #0
~
You are at the start. Your journey begins..
~
s 5
~
Room #1
~
You see feces hanging on the wall. What could it mean?
~
s 6
~
Room #2
~
There seem to be some animal droppings in the corner. Gross.
~
e 3
~
Room #3
~
A dirty hall with cockroaches crawling around. Is this hell?
~
w 2
e 4
~

Last edited on
Put your code in "code tags" like this:

[code]
Your code goes here.
Preserves indentation, uses monospace font, adds syntax highlighting.
[/code]

You can edit your post to add them in.


Also, post an example of the file contents.
Last edited on
Thanks for the code header; was wondering how to do that.
You could try something like this.

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
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>

using namespace std;

struct Room
{
    string name;
    string description;
    int exits[4];
};

enum { North, South, East, West };

const string Marker = "~";
const int NoExit = -1;

int main(int argc, char** argv)
{
    if (argc != 2)
    {
        cout << "Usage: adventure FILENAME\n";
        exit(1);
    }

    ifstream fin(argv[1]);
    if (!fin)
    {
        cerr << "Error: Cannot open " << argv[1] << '\n';
        return 1;
    }

    int markerCount = 0;
    for (string line; getline(fin, line); )
        if (line == Marker)
            ++markerCount;

    int numRooms = markerCount / 3;

    Room* rooms = new Room[numRooms];

    // Reset file back to the start.
    fin.clear();
    fin.seekg(0);

    for (int r = 0; r < numRooms; ++r)
    {
        string line;
        getline(fin, rooms[r].name);
        getline(fin, line); // eat the marker

        getline(fin, rooms[r].description);
        while (getline(fin, line) && line != Marker)
            rooms[r].description += '\n' + line;

        for (int i = 0; i < 4; ++i)
            rooms[r].exits[i] = NoExit;
        while (getline(fin, line) && line != Marker)
        {
            istringstream ss(line);
            char dir;
            int room;
            ss >> dir >> room;
            switch (dir)
            {
            case 'n': rooms[r].exits[North] = room; break;
            case 's': rooms[r].exits[South] = room; break;
            case 'e': rooms[r].exits[East] = room; break;
            case 'w': rooms[r].exits[West] = room; break;
            default:
                cerr << "Bad direction: " << dir << '\n';
                exit(1);
            }
        }
    }

    for (int r = 0; r < numRooms; ++r)
    {
        cout << rooms[r].name << '\n';
        cout << rooms[r].description << '\n';
        if (rooms[r].exits[North] != NoExit)
            cout << "n: " << rooms[r].exits[North] << "  ";
        if (rooms[r].exits[South] != NoExit)
            cout << "s: " << rooms[r].exits[South] << "  ";
        if (rooms[r].exits[East] != NoExit)
            cout << "e: " << rooms[r].exits[East] << "  ";
        if (rooms[r].exits[West] != NoExit)
            cout << "w: " << rooms[r].exits[West];
        cout << '\n';
    }

    delete[] rooms;
}

We would normally use a vector, which handles the memory allocation automatically.
Splitting it into functions is good too.
yeah I want to use vectors with this... it would make it much easier but i've been restricted from using them. I'm gonna try this and see what happens. Thanks for the help.
your solution works in printing all of them out but i have to navigate through the file using the cardinal directions so I think I'll have to change up the switch statements or at least index it differently so i can point to the indexes
Last edited on
I'm not sure what you mean. You do realize that this enum

 
enum { North, South, East, West };

is just a way of naming the indices from 0 to 3, with North being 0 up to West which is 3.

You just need to print the room name and description, then go through the directions North, South, East, West, and for any that aren't NoExit (-1, which is obviously an invalid room index) you print that direction, something like "There are exits: North, East".
Then you have a switch to convert input like 'n', or 'e' back to the enum values (a value between 0 and 3), then you switch the current room to the value of that exit. The current room would of course start at 0. Just do that in a loop to navigate the rooms.
Last edited on
How would you do that conversion from one of the cardinal directions to an enum value? I've never used an enum before; sadly haven't learned them yet.
Simple enums are just named integer constants.

 
enum { Alpha = 23, Beta = 42, Gamma = 99 };

If you don't specify a value, they go up by 1.

 
enum { Alpha = 23, Beta, Gamma }; // Alpha = 23, Beta = 24, Gamma = 25 

If you don't specify the first value, it starts at 0. So

 
enum { North, South, East, West };

simply assigns 0 to North, 1 to South, 2 to East, and 3 to West.

One way to convert from a letter to an integer index:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    int ndir = -1;

    cout << "Enter direction (n, s, e, w): ";
    char cdir;
    cin >> cdir
    switch (tolower(cdir))
    {
    case 'n': ndir = North; break;
    case 's': ndir = South; break;
    case 'e': ndir = East;  break;
    case 'w': ndir = West;  break;
    }

    if (ndir == -1)
        cout << "Bad direction\n";
    else
    {
        // ...
    }

enums are just fancy named constants.

the easy way to convert to text is
enum dirs {north,south,east,west, maxdirs};
vector<string> dirstxt{"north","south","east","west"}; //can use array of string too if banned.
//string alternate[maxdirs] ....
cout << dirstxt[north]; //"north"
to convert from text is more work:
if(input == "north") something = 0;
else if(...

maxdirs lets you iterate normally (for dirs i = north; i < maxdirs; i++)
and if you grow it later to have northeast, southwest, etc 8 directions instead of 4, the above loop still works if north remains the first one and maxdirs the last one (that is, keep a fixed begin and end name and if you modify the middle it will work itself out).

The text names in the code are not able to be directly related to user I/O in enums. They are just integer constants grouped up. There are some tricks you can play with them, but that is all they are and all they can do. (you can set the values, eg north=23, but most enums let the compiler pick the value and it goes 0,1,2,3,... in order).
Last edited on
thank you for the quick lesson haha

only thing I'm left confused on is how that maps to one of the rooms whenever you enter a direction.
thank you for the quick lesson haha

I don't know who you're responding to, here. Did you see my post?

how that maps to one of the rooms whenever you enter a direction

That's ridiculously simple.
The value of the exits for the chosen direction is the room that it leads to.
If the value is -1 (NoExit) then there is no exit in that direction.

1
2
3
4
5
    int nextRoom = rooms[currentRoom].exits[ndir];
    if (nextRoom == NoExit)
        cout << "There's no exit in that direction.\n";
    else
        currentRoom = nextRoom;

Last edited on
Yeah I realized that after I sent the last message.

I was talking to you @dutch.

I've mapped the variables but it seems as if the index is off by one whenever I put a direction into the compiler. Southern direction off of room 0 is supposed to take me to room 5 but it takes me to room 6
That's strange. You can PM me your code and input file if you want. Or you can post it here or upload it somewhere and post a link.
Topic archived. No new replies allowed.