Reputation: 35
I'm making an engine that is supposed to read formatted text files and output them as a text based adventure. The world is being written into a vector matrix. However, my program only seems to fill the matrix in one Dimension and only with the information from the very first cell of the matrix.
The WorldReader reads the World file and returns a specified line:
std::string WorldReader(std::string file,int line)
{
std::string out[n];
int i = 0;
World.open(file + "World.txt");
if(!World.good())
return "Bad File";
else while(i<n && getline(World, out[i]))
{
i++;
}
World.close();
return out[line];
}
Here is the write loop:
for(j=0; j<(width*height); j++)
{
int x;
int y;
stringstream Coordx(WorldReader(loc, 4+j*10));
Coordx >> x;
stringstream Coordy(WorldReader(loc, 5+j*10));
Coordy >> y;
std::string Desc = WorldReader(loc, 6+j*10);
W1.writeCell(x,y,0,Desc);
}
and here is the writeCell function:
std::vector<std::string> Value;
std::vector<std::vector<std::string> > wH;
std::vector< std::vector<std::vector<std::string> > > grid;
void World::writeCell(int writelocW, int writelocH, int ValLoc, std::string input)
{
if (wH.size() > writelocH)
{
Value.insert(Value.begin()+ValLoc,1,input);
wH.insert(wH.begin() + writelocH,1,Value);
grid.insert(grid.begin() + writelocW,1,wH);
}
else
{
wH.insert(wH.begin(),1,Value);
grid.insert(grid.begin(),1,wH);
}
}
also the matrix is getting immensely bloated even though i resized it to 3x3.
tips and help appreciated.
Upvotes: 2
Views: 667
Reputation: 66214
Ok. I think I know where your problem is. Please note this is extremely difficult to analyze without genuinely-runnable code. The high-point is this: You're inserting a new 2D matrix for every value you process into your grid
, and I hope it is clear why this is the case. It explains the mass-bloat (and inaccurate data) you're experiencing.
Your original code
void World::writeCell(int writelocW, int writelocH, int ValLoc, std::string input)
{
if (wH.size() > writelocH)
{
// inserts "input" into the Value member.
Value.insert(Value.begin()+ValLoc,1,input);
// inserts a **copy** of Value into wH
wH.insert(wH.begin() + writelocH,1,Value);
// inserts a **copy** of wH into the grid.
grid.insert(grid.begin() + writelocW,1,wH);
}
else
{ // inserts a **copy** of Value into wH
wH.insert(wH.begin(),1,Value);
// inserts a **copy** of wH into the grid.
grid.insert(grid.begin(),1,wH);
}
}
As you can plainly see. there is a whole lot of unintended copying going on here. You have three variables, each of which is independent.
std::vector<std::string> Value;
std::vector<std::vector<std::string> > wH;
std::vector< std::vector<std::vector<std::string> > > grid;
During the course of writeCell
you are trying to insert your string into a 3D location, but only "dereferencing" to at-most one of those dimensions. And copies o-festival ensues
From your variable names I'm assuming your grid dimensionality is based on:
writeocW * writelocH * ValLoc
You need to unwind the dimensions in most-to-least significant order, starting with grid
. ultimately that is how it is accessed anyway. I personally would use a sparse std::map<> series for this, as the space utilization would be much more efficient, but we're working with what you have. I'm writing this off-the-cuff with no nearby compiler to check for mistakes, so grant me a little latitude.
Proposed Solution
This is a stripped down version of the World class you no-doubt have. I've changed the names of the params to traditional 3D coords (x,y,z) in an effort to make it clear how to do what I think you want:
class World
{
public:
typedef std::vector<std::string> ValueRow;
typedef std::vector<ValueRow> ValueTable;
typedef std::vector<ValueTable> ValueGrid;
ValueGrid grid;
// code omitted to get to your writeCell()
void writeCell(size_t x, size_t y, size_t z, const std::string& val)
{
// resize grid to hold enough tables if we would
// otherwise be out of range.
if (grid.size() < (x+1))
grid.resize(x+1);
// get referenced table, then do the same as above,
// this time making appropriate space for rows.
ValueTable& table = grid[x];
if (table.size() < (y+1))
table.resize(y+1);
// get referenced row, then once again, just as above
// make space if needed to reach the requested value
ValueRow& row = table[y];
if (row.size() < (z+1))
row.resize(z+1);
// and finally. store the value.
row[z] = val;
}
};
I think that will get you where you want. Note that using large coords can quickly grow this cube.
Alternate Solution
Were it up to me I would use something like this:
typedef std::map<size_t, std::string> ValueMap;
typedef std::map<size_t, ValueMap> ValueRowMap;
typedef std::map<size_t, ValueRowMap> ValueGridMap;
ValueGridMap grid;
Since you'd be enumerating these when doing whatever it is you're doing with this grid, order of the keys (the 0-based indexes) is important, thus usage of std::map
rather than std::unordered_map
. An std::map
has a very nice feature with its operator[]()
accessor: It adds the referenced key slot if it doesn't already exist. Thus your writeCell function would collapse to this:
void writeCell(size_t x, size_t y, size_t z, const std::string& val)
{
grid[x][y][z] = val;
}
Obviously this would radically alter the way you use the container, as you would have to be conscious of the "skipped" indexes you're not using, and you would detect this while enumerating with the appropriate iterator for the dimension(s) being used. Regardless, your storage would be much more efficient.
Anyway, I hope this helps at least a little.
Upvotes: 3