C++ Text-based Game - "Map" Implementation - c++

I'm trying to create a text-based adventure game. I'm thinking I want the map to be represented by different nodes where each node corresponds to a distinct location and has node pointer variables (left, forward, and right) that should point to another node in the respective direction. I tried to implement it as a linked list, but with this data structure, I could only have each node point to one other node. I want each node to point to three other nodes. Which data structure could I use to implement this or is this even possible?

A linked datastructure would do a good job of doing what you want:
example:
class location
{
std::string loc_name;
std::vector<std::pair<std::string,location*>> connections;
std::string description;
public:
bool add_link(location* loc, std::string dicription_to, std::string dicription_from);
//other parameters + functions to manage class
}
This would allow you to create locations such as:
location* loc = new location("graveyard");
loc->description = "A spooky graveyard on a hill, a cool mist floats amongst the gravestones and monuments";
loc->add_link(crypt /*previously defined*/,
"An imposing mausoleum with an open door, steps inside lead down into darkness",
"Moonlight filters down from the top of some steps, a way out?");
loc.add_link(spooky_house /*previously defined*/,
"The North gate of the graveyard",
"The entrance to the house's spooky graveyard");
I'd reccommend creating a map file you could read in though. Possibly using a template like this:
locations file:
/*locations, format = "name; description"*/
Spooky House; house_description
Crypt; crypt_description
Graveyard; A spooky graveyard on a hill, a cool mist floats amongst the gravestones and monuments
links file:
/*links, format = "index # (from); index # (to); description (from->to); description (to->from)"*/
3;2;An imposing mausoleum with an open door, steps inside lead down into darkness; Moonlight filters down from the top of some steps, a way out?
3;1;The North gate of the graveyard;The entrance to the house's spooky graveyard;
Loading the map would be as simple as reading in all the locations and pushing them into a vector for storage, then adding the links in to connect them.

You could implement a custom linked datastructure with linked positions on the map like this:
struct Map_Node{
Map_Node *left;
Map_Node *right;
Map_Node *forward;
/* other needed field*/
};
Then, you need to do the memory management on your own. For example by using smart pointers.
std::shared_ptr<Map_Node> entry{ new MapNode };
std::shared_ptr<Map_Node> hallway{ new MapNode };
entry->forward = &*hallway;
//and so on
An easier but less efficient for getting the next file would be a std::map. If each position
has its unique ID, e.g. a string, you could store the IDs of the neighboring fields and move freely on the map by using the ID.
struct Map_Node{
std::string name;
std::string left;
std::string right;
std::string forward;
/* other needed field*/
};
std::map<std::string, Map_Node> map;
Map_Node entry;
entry.name = "entry";
map[entry.name] = entry;
Map_Node hallway;
hallway.name = "hallway";
map[hallway.name] = hallway;
//links between:
map["entry"].forward = "hallway";

Related

C++ n-arry tree with different elements

I want to build a n-arry tree from a document. For that i have 3 different types of elements for the tree:
Struct Nodes
Have a name
can contain other Nodes
Depth
Element Node (Leaf of the tree)
Have a Key
Have a value
Depth
Element Template Node (Leaf of the tree)
Have a placeholder which should be resolved later in the program
Depth
At the moment i think about something like this:
class Node {
public:
Node(int depth);
int depth() const;
private:
int depth_;
};
class StructNode : public Node {
...
private:
std::vector<std::unique_ptr<Node>> children;
};
class ElementNode : public Node {
...
};
class ElementTemplateNode : public Node {
...
};
The Tree will be generated from an File on Startup and reused to create an output string like this:
Structname:
key = value
key = value
Structname:
key = value
Structname:
key = value
...
Where the Key and value where directly read from the ElementNode or read from another file with the value of the placeholder inside the ElementTemplateNode
Is there maybe a better Structure for the Tree? Because with the current one i have to check first if its a StructNode, ElementNode or ElementTemplateNode
This is a typical structure for implementing a tree with different kind of nodes. Another variant would be the composite pattern.
The problem that you describe, is usually caused by asking the nodes about what they know, instead of telling them what to do. If you'd do it the other way round (tell, don't ask), you could get rid of those checks and benefit from polymorphism.
The different kind of nodes inherit from Node. You could design your tree using a uniform interface, with virtual functions defined for Node which then can be overridden for the different types of nodes. Calling the method would then do the right things, without need for a manual type check. For generating the output string, you'd tell the root node to generate a string. If it's a structure, it would add the heading and tell its children to generate a string, but if it's a leaf it would just add the key/value pair to the string. No need from outside to know anything about each node.
If the operation of exploring the tree shall not be implemented by the tree itself, the usual approach is to use a visitor pattern. The big advantage is that you write the vistor once, and it's then easy to specialize a new kind of visitor for different algorithms. Again, no need to check the type of the nodes. The pattern makes sure that the right elementary function is called for the right type of node.

Dijkstra's Algorithm in String-type Graph

I am making an inter-city route planning program where the graph that is formed has string-type nodes (e.g. LHR, ISB, DXB). It's undirected but weighted, and is initialized as:
map<pair<string, string>, int> city;
and then I can add edges by for example:
Graph g;
g.addEdge("DXB", "LHR", 305);
g.addEdge("HTR", "LHR", 267);
g.addEdge("HTR", "ISB", 543);
and the resultant output will be:
ISB LHR
DXB 0 305
HTR 543 267
Now, the question... I'm trying to implement Dijkstra's algorithm in this graph but so far have been unable to correctly run it on string-type nodes and opposed to learning and doing it on int-type nodes. Can someone guide me through the correct steps of implementing it in the most efficient way possible?
The data structure used by a graph application has a big impact on the efficiency and ease of coding.
Many designs start off with the nodes. I guess the nodes, in the problems that are being modelled, often have a physical reality while the links can be abstract relationships. So it is more natural to start writing a node class, and add on the links later.
However, when coding algorithms that solve problems in graph theory, it becomes clear that the links are the real focus. So, lets start with a link class.
class cLink
{
public:
cLink(int c = 1)
: myCost(c)
{
}
int myCost; // a constraint e.g. distance of a road, max xapacity of a pipe
int myValue; // a calculated value, e.g. actual flow through a pipe
};
If we store the out edges of node in a map keyed by the destination node index, then the map will be an attribute of the source node.
class cNode
{
public:
cNode(const std::string &name = "???")
: myName(name)
{
}
std::string myName;
std::map<int, cLink> myLink; // out edges of node, keyed by destination
};
We have links and nodes, so we are ready to construct a class to store the graph
class cGraph {
public:
std::map<int, cNode> myG; // the graph, keyed by internal node index
};
 Where did the node index come from? Humans are terrible at counting, so better the computer generates the index when the node is added.
cGraph::createNode( const std::string& name )
{
int n = myG.size();
myG.insert(std::make_pair(n, cNode(name)));
}
Don't implement this! It has a snag - it can create two nodes with the same name. We need to be able to check if node with a specified name exists.
int cGraph::find(const std::string &name)
{
for (auto n : myG)
{
if (n.second.myName == name)
{
return n.first;
}
}
return -1;
}
This is inefficient. However, it only needs to be done once when the node is added. Then the algorithms that search through the graph can use fast lookup of nodes by index number.
Now we can prevent two nodes being created with the same name
int cGraph::findoradd(const std::string &name)
{
// search among the existing nodes
int n = find(name);
if (n < 0)
{
// node does not exist, create a new one
// with a new index and add it to the graph
n = myG.size();
myG.insert(std::make_pair(n, cNode(name)));
}
return n;
}
Humans, in addition to being terrible counters, are also over confident in their counting prowess. When they specify a graph like this
1 -> 2
1 -> 3
Let’s not be taken in. Let’s regard these numbers as names and continue to use our own node index system.
/** Add costed link between two nodes
*
* If the nodes do not exist, they will be added.
*
*/
void addLink(
const std::string & srcname,
const std::string & dstname,
double cost = 1)
{
int u = findoradd(srcname);
int v = findoradd(dstname);
myG.find(u)->second.myLink.insert(
std::make_pair(v, cLink(cost)));
if (!myfDirected)
myG.find(v)->second.myLink.insert(
std::make_pair(u, cLink(cost)));
}
With the addition of a few getters and setters, we are ready to start implementing graph algorithms!
To see an complete implementation, including Dijsktra, using these ideas, check out PathFinder.
The core problem is that when we work on graphs with integer vertices, the index of the adjacency list represents the node (since the indexes are also numbers). Now instead of using adjacency list like vector<pair<int, int> > adj[N]we can use map<string,vector<string, int> > adj. Now adj["DXB"] will contain a vector of pairs of the form <string, int> which is the <name, weight> for the cities connected to "DXB".
If this approach seems very complex, then you use some extra memory to map a city to a number, and then you can code everything considering that the graph has integer vertices.

Can I group attributes of one class into one map?

If I have class "node", and wish to include all possible (say int) attributes. Is grouping them into one map good solution.
So instead:
class node{
int color;
int isVisited;
int weight;
public:
};
To have
class node{
map<string, int> property;
public:
setProperty(string property_label, int property_value)
{property[propery_label] = property_value;};
};
int main(){
node n;
n.setProperty("color",int(color::red));
n.setProperty("isVisited", 1);
n.setProperty("weight", 12);
}
EDIT:
The reason to do this is that, when transforming a graph, some local properties (like is visited during some traversal, or is it marked) are needed somewhere in the middle of an algorithm, but these local properties do not represent intrinsic property of a node, and are not needed in the output. Also, sometimes I need more than one "isVisited" variables.
One more reason is to keep the class "node" universal and open for new attributes that eventually might be needed.
The example you gave gives the impression that any node would have all the properties you provided (colour, isVisited, weight). If so it is (usually) better to keep the original class you started with.
There might be a few scenarios in which a map (or possibly rather std::unordered_map) might be superior, though; just a few examples:
You have a large number of possible (but pre-defined) attributes and each node only needs a rather small subset of. Possibly an enum is better suited as key then.
You want/need to store arbitrary attributes unknown at compile time.
Each node has the same attributes, but you mainly access them via user input; then especially an unordered_map could be faster than a (possibly long) if-else-chain.
In the end, all depends on the use case...
For strings as keys possibly a trie might be an interesting alternative, too.
A class (identical to a struct except it defaults to private access rather than public) is primarily to group elements of data and/or functionality together.
You node seems to simply group together three elements. So you probably want to start off with something as simple as:
struct node // access is public by default
{
int color;
int isVisited; // maybe a bool rather than int?
int weight;
}
...
node myNode;
myNode.color = ...
...
std::cout << myNode.weight;

I want to make a better tree than st_tree

At https://github.com/erikerlandson/st_tree you will find a good template for creating trees for instance you can write:
tree<string, ordered<> > t;
t.insert("A");
// Insert strings at ply 1
node_iterator c = t.root().insert("C");
t.root().insert("B");
t.root().insert("D");
// Insert strings at ply 2
c->insert("F");
c->insert("E");
You can choose between ordered (using multiset), raw (using vector) and keyed node (using map), each node holds some data which also is the key for ordered and keyed node. It is not possible to make a tree with mixed type of nodes. I want to make an alternative template where the nodes not always have data but merely is a holder for some childs and also allows some mix. A simplified example could be:
tree t;
t.iterator it=t.insert(new node<raw_no_data<>>);
t.iterator it2=it->insert(new node<ordered_no_data<string>>); //indicating that its childs
//data should be string;
t.iterator it3=it->insert(new node<keyed_no_data<string>>);
it2->insert(new node<leave,string>(“B”));
it3->insert(new node<leave_key,string,int>(“D”,5));
node_base &n1=t[0].find(“B”); //looking in multiset
node_base &n2=t[1].find(“D”); //looking in map
In other words in a more general case, I want the option to access a node like this:
mynode=t[2][0]["cat"][1];
Where tree is a mix of ordered and raw nodes, and where we have no _data for childs of the raw node.
Does something like this already exist as open source? If not I will try to make such a project and upload it on GitHub.

Populating a vector of objects within a class

and I've hit my first wall on my coding project/assignment.
I'm to implement functionality into code that's been done to some stage, and I cannot alter the given code so I have to work around the given structure.
The code, in a nutshell, reads family relations from a text file and populates database with the family relation data and later on allows user to print out information he wants to access.
What I'm having trouble with is understanding how I can and how I have to utilize a struct given to me in the assignment. The struct is
struct Person
{
std::string id_ = NO_ID;
int height_ = NO_HEIGHT;
std::vector<Person*> parents_{nullptr, nullptr};
std::vector<Person*> children_;
};
and I'm using it at least in the initialization phase of the data structure.
I start by calling the process in main.cpp with
database->addRelation(it->child_, it->parents_, std::cout);
In the naming/height adding phase I'd simply do it with
MyPerson.id_ = id;
MyPerson.height_ = height;
where MyPerson is defined by Person MyPerson;
but as far as I can tell, I have to somehow access the object pointers to be able to populate the vectors for when I want to add children/parents to the person.
The class functions that are called when initializing person's name, height and family relations are these two:
void Familytree::addNewPerson(const string &id, const int &height, ostream &output)
{
MyPerson.id_ = id;
MyPerson.height_ = height;
}
void Familytree::addRelation(const string &child,
const std::vector<string>
&parents, ostream &output)
{
}
The addRelation fuction is what I'm having a hard time getting to work. Simply appending the strings to it won't work since it expects Person* -objects, which are, as far as I can tell, just pointers to the other Persons, but I'm not sure how I can access them.
Also, let me know if anything here is excessive or if I'm missing anything crucial, I'll edit it to the best of my ability
Editing with additional information:
The only things I've added myself that can be seen here is
Person MyPerson;
and the contents of the class function addNewPerson. The other snippets I can not change in any shape or form.
Edit#2
Current progress, debatable whether I'm closer or further from the goal
My persons map is using Personmap = std::map<std::string, Person >;
and I'm using it in addNewPerson with
persons_[id] = id;
persons_[id] = height;
, but I'm still randomly trying different things to try and make it work for the next phase where I need to somehow add the objects to the vectors.
The biggest problem I have is the fact that I do now know how to play around the difference of *Person and Person