RTS Resource Manager (C++)

Originally a graduate admissions assignment for SMU Guildhall’s Game Design Programming track, this is a data construct and string manipulation project that seeks to test C++ skill levels.

The program will load from “resource.txt” on start, reading each line as follows: the first word is the name of resource, and each subsequent word is a connected resource. Each resource is a node on a directed graph, and if any new connected resource is encountered, the program will automatically create a node for it on the graph.

The program displays a representation of the directed graph, and a table which shows which nodes are usable according to the assignment. The player can delete a node by name, or quit the program at any time.

The original assignment is below…


Assignment:

Create a Resource Manager for a Real Time Strategy (RTS) game. Your program manages a “directed graph” (a system of nodes and links between them going in one direction, much like web pages and their links). On startup, your program reads a file “resource.txt” (from the current directory) which describes resources and the resources they depend on. The file resource.txt could, for example, contain (exactly 4 lines):

handgun bullets
bullets ore
bombs ore
turret bullets

The first line says that there is a link from a node called “handgun” to a node called ‘bullet”. This means for a handgun to be a useable, it relies on the resource bullets.

Requirements:

  • Your program should work with any amount of nodes and any amount of links between them (your program may be tested with a larger resource.txt made by SMU Guildhall faculty). To this end, you should represent nodes with a Node class, and the set of links for a single node using an STL container of your choice.
  • Your program should loop and display two items on the screen: (1) a current view of the graph, and (2) a list of each node and whether it is usable or not. For any current node, if any of the nodes it relies on get deleted that node becomes unusable.
  • Your program should handle two types of input. A user should be allowed to delete a node and quit at any time. If using a console program, input should be “q” for quit, or the name of a node to delete it. When you delete a node, make sure any links to it are deleted correctly too. Be certain to have correct memory management for these operations ensuring no memory leaks when the program exits.

Skills Demonstrated:

  • STL Familiarity
  • STL Containers (Maps, Vectors, and Sets)
  • Iteration Over Containers Using “auto” Keyword
  • File Stream, String Stream, and Input Stream Understanding
  • Typename Ambiguity with Templates
  • Pointers
  • I/O Familiarity
  • Namespace Familiarity

Download:

You can download the source code here. The zip includes a “resource.txt” file which can be edited to test for new inputs.

Header File:

#pragma once

#include <map> 
#include <string>
#include <vector>
using std::string;
using std::map;
using std::vector;

class Node {
public:
	string resourceName;
	bool useable = true; // Unstable marker for faster check time
	map<string, Node>* linkedNodes = new map<string, Node>;
};

map<string, Node>* nodes = new map<string, Node>; // Dictionary for storing nodes <resource name, node>

void AddNodeToResources(string resourceName);
void AddNodeToResources(string resourceName, vector<string> linkedResources);
Node CreateNode(string resourceName);
void DeleteNode(string resourceName, map<string, Node>* map);
bool CheckNodeExists(const string& resource, map<string, Node>* map);
bool CheckUseable(Node resource);
void PrintResourceMap(map<string, Node>* map);
void PrintResourceMap_LinkedResources(map<string, Node> map);
void PrintNodeList(map<string, Node>* map);
void ExitProgram();

template <typename T>
vector<T> RemoveFirstElemOfVector(vector<T> vec);

CPP File:

// Author: Orrett Coke

#include <iostream>
#include <fstream>
#include <sstream>
#include <iostream>
#include <iterator>
#include "RTS Resource Manager.h"

using std::cout;
using std::cin;
using std::ifstream;
using std::getline;
using std::istringstream;
using std::istream_iterator;

int main()
{
	string command;
	ifstream rscFile("resource.txt");
	string fileLine;

	nodes = new map<string, Node>; // Holds resource nodes

	if (rscFile.is_open()) {
		// Pull resources from text file line-by-line and add to resources
		while (getline(rscFile, fileLine))
		{
			// Use string stream to seperate each file line from a string into a vector of words
			istringstream stringStream(fileLine);
			vector<string> results((istream_iterator<string>(stringStream)),
				istream_iterator<string>());

			if (results.size() == 1)
				AddNodeToResources(results[0]);
			else
				AddNodeToResources(results[0], RemoveFirstElemOfVector(results)); // Remove the first word to get the linked resources in this file line
		}
		rscFile.close();
	}
	else {
		cout << "Cannot Find/Open resource.txt\n";
		return 0;
	}

	while (true)
	{
		// Show Graph
		cout << "\n========================\n";
		cout << "Resource Directed Graph:\n";
		cout << "========================\n";
		if (nodes->empty())
			cout << "No resources.\n";
		else
			PrintResourceMap(nodes);


		// Show Useability
		cout << "\n========================\n";
		cout << "Resource Usability:\n";
		cout << "========================\n";
		if (nodes->empty())
			cout << "No resources.\n";
		else
			PrintNodeList(nodes);

		while (true) // Input Loop
		{
			// Wait for command
			cout << "\nPlease type a resource name for deletion, or q to exit:\n";
			cin >> command;

			// Quit command
			if (command == "q" || command == "Q")
				ExitProgram();

			// Delete the specified node if it exists
			if (CheckNodeExists(command, nodes))
			{
				DeleteNode(command, nodes);
				break;
			}
			else
			{
				cout << "\nERROR: A node of this name does not exist...\n\n";
			}
		}
	}

	ExitProgram();
}

void ExitProgram()
{
	cout << "\n**************\n";
	cout << "Program Exit\n";
	cout << "**************\n";

	exit(0);
}

void AddNodeToResources(string resourceName) {
	// If node does not exist, create and assign node
	if (!CheckNodeExists(resourceName, nodes))
	{
		nodes->emplace(resourceName, CreateNode(resourceName));
	}
}

void AddNodeToResources(string resourceName, vector<string> linkedResources) {
	// Check if node already exists (in resources dictionary)
	if (CheckNodeExists(resourceName, nodes))
	{
		// Add linked resources to resources
		for (auto const& elem : linkedResources)
		{
			// If linked resource has not been encountered (is not in resources dictionary), add it to resources as well
			AddNodeToResources(elem);
			// Add linked resources to the node's linked resources list
			nodes->find(resourceName)->second.linkedNodes->emplace(elem, nodes->find(elem)->second);
		}
	}
	else
	{
		// Add new node to resources, as well as linked nodes
		nodes->emplace(resourceName, CreateNode(resourceName));

		for (auto const& elem : linkedResources)
		{
			AddNodeToResources(elem);
			nodes->find(resourceName)->second.linkedNodes->emplace(elem, nodes->find(elem)->second);
		}
	}
}

Node CreateNode(string resourceName) {
	// Create and assign node
	Node tempNode;
	tempNode.resourceName = resourceName;

	return tempNode;
}

void DeleteNode(string resourceName, map<string, Node>* map) {
	// Find and delete node
	map->erase(resourceName);

	// Update useability
	for (auto elem : *map)
	{
		// Change useability if a node depends on the deleted resource (deleted resource is in another node's linked resources)
		if (CheckNodeExists(resourceName, elem.second.linkedNodes))
		{
			map->find(elem.first)->second.useable = false; // Change useability
			map->find(elem.first)->second.linkedNodes->erase(resourceName); // Delete node in linked resources, too
		}
	}
}

bool CheckNodeExists(const string& resource, map<string, Node>* map) {
	return map->find(resource) != map->end();
}

// Check if node's dependencies (linked resources) have been deleted
bool CheckUseable(Node resource)
{
	return resource.useable;
}

void PrintResourceMap(map<string, Node>* map) {
	// Print map elements
	for (auto elem : *map)
	{
		cout << elem.first;
		if (!elem.second.linkedNodes->empty()) // Print linked resources if they exist (print connected nodes, a.k.a. dependencies)
		{
			cout << " ->";
			PrintResourceMap_LinkedResources(*(elem.second.linkedNodes));
		}

		cout << "\n";
	}
}

// Print map elements
void PrintResourceMap_LinkedResources(map<string, Node> map) {
	cout << " " << map.begin()->first; // Print first linked resource (connected node, a.k.a. resource dependency)

	// Iterate through and print remaining linked resources
	map.erase(map.begin());
	for (auto elem : map)
	{
		cout << ", ";
		cout << elem.first;
	}
}

// Print list of nodes with usability
void PrintNodeList(map<string, Node>* map) {
	// Print map elements
	for (auto elem : *map)
	{
		cout << elem.first;
		if (CheckUseable(elem.second))
			cout << " (Y)\n";
		else
			cout << " (N)\n";
	}
}

template <typename T>
vector<T> RemoveFirstElemOfVector(vector<T> vec) {
	vec.erase(vec.begin());
	return vec;
}
Design a site like this with WordPress.com
Get started
search previous next tag category expand menu location phone mail time cart zoom edit close