Initial import.

This commit is contained in:
2026-06-30 16:01:47 -07:00
commit dcb054ff9f
21 changed files with 899 additions and 0 deletions

8
starter/CMakeLists.txt Normal file
View File

@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.11.3)
set(CMAKE_CXX_STANDARD 17)
project(Membot)
set(PROJECT_SRCS src/main_cli.cpp src/chatlogic.cpp src/chatbot.cpp src/graphedge.cpp src/graphnode.cpp)
add_executable(membot ${PROJECT_SRCS})

View File

@@ -0,0 +1,87 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"files.associations": {
"__bit_reference": "cpp",
"__config": "cpp",
"__debug": "cpp",
"__functional_base": "cpp",
"__hash_table": "cpp",
"__locale": "cpp",
"__mutex_base": "cpp",
"__nullptr": "cpp",
"__split_buffer": "cpp",
"__string": "cpp",
"__threading_support": "cpp",
"__tuple": "cpp",
"algorithm": "cpp",
"array": "cpp",
"atomic": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"complex": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"exception": "cpp",
"optional": "cpp",
"string_view": "cpp",
"fstream": "cpp",
"functional": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"iterator": "cpp",
"limits": "cpp",
"list": "cpp",
"locale": "cpp",
"memory": "cpp",
"mutex": "cpp",
"new": "cpp",
"numeric": "cpp",
"ostream": "cpp",
"queue": "cpp",
"random": "cpp",
"ratio": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"string": "cpp",
"system_error": "cpp",
"thread": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"unordered_map": "cpp",
"utility": "cpp",
"vector": "cpp",
"hash_map": "cpp",
"*.tcc": "cpp",
"memory_resource": "cpp",
"cinttypes": "cpp",
"future": "cpp",
"__errc": "cpp",
"__node_handle": "cpp",
"stack": "cpp",
"bit": "cpp"
}
}
}

44
starter/README.md Normal file
View File

@@ -0,0 +1,44 @@
# CPPND: Memory Management Chatbot
This is the project for the Memory Management course in the Udacity C++ Nanodegree Program.
# High-level Objectives
> [!IMPORTANT]
> _See the Project Rubric for a full list of requirements._
In this project, you'll apply your skills to:
1. **Analyze problems**, and
2. **Then modify the program** using the advanced concepts taught in this course
**To complete the project:**
1. **Find each `TODO`** located in the project's files. `TODO`s are located either:
- To the side, if the fix is for a single line of code
- Above the code, if the fix is for multiple lines of code. These also have a `END OF TODO` comment at the end of the relevant blocks of code.
3. **Analyze the relevant code to determine what needs to be fixed**
- Generally, the `TODO`s don't provide instructions or guidance. This is so you can practice identifying and implementing appropriate fixes.
4. **Implement the appropriate fix**
- Your fixes need to use the advanced concepts taught in this course
# Project Setup
## 1. Install Dependencies
> [!NOTE]
> If you're using Udacity Workspace, these dependencies are already installed
* cmake >= 3.11
* All OSes: [click here for installation instructions](https://cmake.org/install/)
* make >= 4.1 (Linux, Mac), 3.81 (Windows)
* Linux: make is installed by default on most Linux distros
* Mac: [install Xcode command line tools to get make](https://developer.apple.com/xcode/features/)
* Windows: [Click here for installation instructions](http://gnuwin32.sourceforge.net/packages/make.htm)
* gcc/g++ >= 5.4
* Linux: gcc / g++ is installed by default on most Linux distros
* Mac: same deal as make - [install Xcode command line tools](https://developer.apple.com/xcode/features/)
* Windows: recommend using [MinGW](http://www.mingw.org/)
## 2. Compile, build, and Run
> [!WARNING]
> **Until you finished the `TODO`s, the code will compile** (`cmake ..`) **but not build** (`make`)
In the top level directory of this project, run the following:
`mkdir build && cd build && cmake .. && make && ./membot`

View File

@@ -0,0 +1,29 @@
// define all graph nodes
<TYPE:NODE><ID:0><ANSWER:Welcome! My name is MemBot. You can ask me about things related to memory management in C++. Possible topics are 'pointers' and 'the C++ memory model'. What would you like to talk about?>
<TYPE:NODE><ID:1><ANSWER:Great! Let's talk about pointers. In computer science, a pointer is a special value whose meaning is a memory address. This address can contain either data, such as variables or objects, but also program codes (instructions). By dereferencing the pointer it is possible to access the data or the code. Among other things, pointers are used to manage dynamic memory. Other topics around pointers you can ask about are 'smart pointers' and 'nullptr'>
<TYPE:NODE><ID:2><ANSWER:When instantiating variables, programmers can choose wether to do this on the heap, on the stack or in static memory. Do you want to know more about those two concepts? Simply ask me about 'heap', 'stack' or 'static'.>
<TYPE:NODE><ID:3><ANSWER:A smart pointer is an object that behaves like a pointer, i.e. it must support pointer operations such as dereferencing or indirect access. In addition to these properties, the smart pointer handles resources better. In concrete terms, this means that it takes care not to cause a memory leak. A smart pointer exists in various flavors, which are unique_ptr, shared_ptr and weak_ptr. There are no more topics in this section, starting over!>
<TYPE:NODE><ID:4><ANSWER:The value nullptr can be assigned to pointers of any type. This turns the pointer into a null pointer that does not point to a memory location. The nullptr keyword can be used to test if a pointer or handle reference is null before the reference is used. There are no more topics in this section, starting over!>
<TYPE:NODE><ID:5><ANSWER:Local variables declared as part of a function are stored on the stack. Also, the stack is the place where function parameters are stored. Further, the stack is used for storing the return address of the calling functions, and it keeps the register contents and return address when an interrupt service routine is called. -- There are no more topics in this section, starting over!>
<TYPE:NODE><ID:6><ANSWER:Heap memory, also known as dynamic memory, is an alternative to local stack memory. Local memory is allocated automatically on function call and it is deallocated automatically when a function exits. Heap memory is different. The programmer explicitly requests the allocation of a memory block of a particular size, and the block continues to be allocated until the programmer explicitly requests that it be deallocated. -- There are no more topics in this section, starting over!>
<TYPE:NODE><ID:7><ANSWER:Static memory persists throughout the entire life of the program, and is usually used to store things like global variables, or variables created with the static clause. -- There are no more topics in this section, starting over!>
// connect nodes with edges
<TYPE:EDGE><ID:0><PARENT:0><CHILD:1><KEYWORD:pointer><KEYWORD:smart pointer>
<TYPE:EDGE><ID:1><PARENT:0><CHILD:2><KEYWORD:memory model><KEYWORD:heap><KEYWORD:stack>
<TYPE:EDGE><ID:2><PARENT:1><CHILD:3><KEYWORD:unique pointer><KEYWORD:shared pointer><KEYWORD:weak pointer>
<TYPE:EDGE><ID:3><PARENT:1><CHILD:4><KEYWORD:NULL><KEYWORD:null pointer><KEYWORD:nullptr>
<TYPE:EDGE><ID:4><PARENT:2><CHILD:5><KEYWORD:stack><KEYWORD:stack memory><KEYWORD:local variable storage>
<TYPE:EDGE><ID:5><PARENT:2><CHILD:6><KEYWORD:heap><KEYWORD:heap memory><KEYWORD:dynamic storage>
<TYPE:EDGE><ID:6><PARENT:2><CHILD:7><KEYWORD:static><KEYWORD:static memory><KEYWORD:global variable storage>

134
starter/src/chatbot.cpp Normal file
View File

@@ -0,0 +1,134 @@
#include <iostream>
#include <random>
#include <algorithm>
#include <ctime>
#include "chatlogic.h"
#include "graphnode.h"
#include "graphedge.h"
#include "chatbot.h"
// constructor WITHOUT memory allocation
ChatBot::ChatBot()
{
std::cerr << ">>> Rule of Five Component: ChatBot Default Constructor <<<" << std::endl;
_chatLogic = nullptr;
_rootNode = nullptr;
}
// TODO the following:
// TODO: add copy constructor
// TODO: add copy assignment operator
// TODO: add move constructor
// TODO: add move assignment operator
// END OF TODO
ChatBot::~ChatBot()
{
std::cerr << ">>> Rule of Five Component: ChatBot Destructor <<<" << std::endl;
}
void ChatBot::ReceiveMessageFromUser(std::string message)
{
typedef std::pair<GraphEdge *, int> EdgeDist;
std::vector<EdgeDist> levDists;
std::vector<GraphEdge *> keywordMatches;
std::string userInputLower = message;
std::transform(userInputLower.begin(), userInputLower.end(), userInputLower.begin(), ::tolower);
// First, check for direct keyword containment (case-insensitive)
for (size_t i = 0; i < _currentNode->GetNumberOfChildEdges(); ++i)
{
GraphEdge *edge = _currentNode->GetChildEdgeAtIndex(i);
for (auto keyword : edge->GetKeywords())
{
std::string keywordLower = keyword;
std::transform(keywordLower.begin(), keywordLower.end(), keywordLower.begin(), ::tolower);
if (userInputLower.find(keywordLower) != std::string::npos) {
keywordMatches.push_back(edge);
break; // Only need one keyword match per edge
}
}
}
GraphNode *newNode = nullptr;
if (!keywordMatches.empty()) {
// If multiple matches, pick the first (or could add more logic)
newNode = keywordMatches[0]->GetChildNode();
} else {
// Fall back to Levenshtein distance
for (size_t i = 0; i < _currentNode->GetNumberOfChildEdges(); ++i)
{
GraphEdge *edge = _currentNode->GetChildEdgeAtIndex(i);
for (auto keyword : edge->GetKeywords())
{
EdgeDist ed{edge, ComputeLevenshteinDistance(keyword, message)};
levDists.push_back(ed);
}
}
if (levDists.size() > 0)
{
std::sort(levDists.begin(), levDists.end(), [](const EdgeDist &a, const EdgeDist &b) { return a.second < b.second; });
newNode = levDists.at(0).first->GetChildNode();
}
else
{
newNode = _rootNode;
}
}
_currentNode->MoveChatbotToNewNode(newNode);
}
void ChatBot::SetCurrentNode(GraphNode *node)
{
_currentNode = node;
std::vector<std::string> answers = _currentNode->GetAnswers();
std::mt19937 generator(int(std::time(0)));
std::uniform_int_distribution<int> dis(0, answers.size() - 1);
std::string answer = answers.at(dis(generator));
_chatLogic->SendMessageToUser(answer);
// If the answer contains the reset message, reset to root and print the root answer
if (answer.find("There are no more topics in this section, starting over!") != std::string::npos) {
if (_currentNode != _rootNode) {
_currentNode->MoveChatbotToNewNode(_rootNode);
// The move will call SetCurrentNode on the root node, which will print the welcome message.
}
}
}
int ChatBot::ComputeLevenshteinDistance(std::string s1, std::string s2)
{
std::transform(s1.begin(), s1.end(), s1.begin(), ::toupper);
std::transform(s2.begin(), s2.end(), s2.begin(), ::toupper);
const size_t m(s1.size());
const size_t n(s2.size());
if (m == 0)
return n;
if (n == 0)
return m;
size_t *costs = new size_t[n + 1];
for (size_t k = 0; k <= n; k++)
costs[k] = k;
size_t i = 0;
for (std::string::const_iterator it1 = s1.begin(); it1 != s1.end(); ++it1, ++i)
{
costs[0] = i + 1;
size_t corner = i;
size_t j = 0;
for (std::string::const_iterator it2 = s2.begin(); it2 != s2.end(); ++it2, ++j)
{
size_t upper = costs[j + 1];
if (*it1 == *it2)
{
costs[j + 1] = corner;
}
else
{
size_t t(upper < corner ? upper : corner);
costs[j + 1] = (costs[j] < t ? costs[j] : t) + 1;
}
corner = upper;
}
}
int result = costs[n];
delete[] costs;
return result;
}

42
starter/src/chatbot.h Normal file
View File

@@ -0,0 +1,42 @@
#ifndef CHATBOT_H_
#define CHATBOT_H_
#include <string>
class GraphNode; // forward declaration
class ChatLogic; // forward declaration
class ChatBot
{
private:
// data handles (not owned)
GraphNode *_currentNode;
GraphNode *_rootNode;
ChatLogic *_chatLogic;
// proprietary functions
int ComputeLevenshteinDistance(std::string s1, std::string s2);
public:
// constructors / destructors
ChatBot(); // constructor
ChatBot(std::string filename); // constructor (filename is ignored in CLI)
~ChatBot();
// TODO the following:
// TODO: add copy constructor
// TODO: add copy assignment operator
// TODO: add move constructor
// TODO: add move assignment operator
// END OF TODO
// getters / setters
void SetCurrentNode(GraphNode *node);
void SetRootNode(GraphNode *rootNode) { _rootNode = rootNode; }
void SetChatLogicHandle(ChatLogic *chatLogic) { _chatLogic = chatLogic; }
// communication
void ReceiveMessageFromUser(std::string message);
};
#endif /* CHATBOT_H_ */

225
starter/src/chatlogic.cpp Normal file
View File

@@ -0,0 +1,225 @@
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
#include <iterator>
#include <tuple>
#include <algorithm>
#include "graphedge.h"
#include "graphnode.h"
#include "chatbot.h"
#include "chatlogic.h"
ChatLogic::ChatLogic()
{
_chatBot = new ChatBot();
_chatBot->SetChatLogicHandle(this);
}
ChatLogic::~ChatLogic()
{
delete _chatBot;
// delete all nodes
for (auto it = std::begin(_nodes); it != std::end(_nodes); ++it)
{
delete *it;
}
}
template <typename T>
void ChatLogic::AddAllTokensToElement(std::string tokenID, tokenlist &tokens, T &element)
{
// find all occurences for current node
auto token = tokens.begin();
while (true)
{
token = std::find_if(token, tokens.end(), [&tokenID](const std::pair<std::string, std::string> &pair) { return pair.first == tokenID;; });
if (token != tokens.end())
{
element.AddToken(token->second); // add new keyword to edge
token++; // increment iterator to next element
}
else
{
break; // quit infinite while-loop
}
}
}
void ChatLogic::LoadAnswerGraphFromFile(std::string filename)
{
// load file with answer graph elements
std::ifstream file(filename);
// check for file availability and process it line by line
if (file)
{
// loop over all lines in the file
std::string lineStr;
while (getline(file, lineStr))
{
// extract all tokens from current line
tokenlist tokens;
while (lineStr.size() > 0)
{
// extract next token
int posTokenFront = lineStr.find("<");
int posTokenBack = lineStr.find(">");
if (posTokenFront < 0 || posTokenBack < 0)
break; // quit loop if no complete token has been found
std::string tokenStr = lineStr.substr(posTokenFront + 1, posTokenBack - 1);
// extract token type and info
int posTokenInfo = tokenStr.find(":");
if (posTokenInfo != std::string::npos)
{
std::string tokenType = tokenStr.substr(0, posTokenInfo);
std::string tokenInfo = tokenStr.substr(posTokenInfo + 1, tokenStr.size() - 1);
// add token to vector
tokens.push_back(std::make_pair(tokenType, tokenInfo));
}
// remove token from current line
lineStr = lineStr.substr(posTokenBack + 1, lineStr.size());
}
// process tokens for current line
auto type = std::find_if(tokens.begin(), tokens.end(), [](const std::pair<std::string, std::string> &pair) { return pair.first == "TYPE"; });
if (type != tokens.end())
{
// check for id
auto idToken = std::find_if(tokens.begin(), tokens.end(), [](const std::pair<std::string, std::string> &pair) { return pair.first == "ID"; });
if (idToken != tokens.end())
{
// extract id from token
int id = std::stoi(idToken->second);
// node-based processing
if (type->second == "NODE")
{
// check if node with this ID exists already
auto newNode = std::find_if(_nodes.begin(), _nodes.end(), [&id](GraphNode *node) { return node->GetID() == id; }); // TODO
// create new element if ID does not yet exist
if (newNode == _nodes.end())
{
_nodes.emplace_back(new GraphNode(id)); // TODO
newNode = _nodes.end() - 1; // get iterator to last element
// add all answers to current node
AddAllTokensToElement("ANSWER", tokens, **newNode);
}
}
// edge-based processing
if (type->second == "EDGE")
{
//// STUDENT CODE
////
// find tokens for incoming (parent) and outgoing (child) node
auto parentToken = std::find_if(tokens.begin(), tokens.end(), [](const std::pair<std::string, std::string> &pair) { return pair.first == "PARENT"; });
auto childToken = std::find_if(tokens.begin(), tokens.end(), [](const std::pair<std::string, std::string> &pair) { return pair.first == "CHILD"; });
if (parentToken != tokens.end() && childToken != tokens.end())
{
// get iterator on incoming and outgoing node via ID search
auto parentNode = std::find_if(_nodes.begin(), _nodes.end(), [&parentToken](GraphNode *node) { return node->GetID() == std::stoi(parentToken->second); }); // TODO
auto childNode = std::find_if(_nodes.begin(), _nodes.end(), [&childToken](GraphNode *node) { return node->GetID() == std::stoi(childToken->second); }); // TODO
// TODO: create new edge
GraphEdge *edge = new GraphEdge(id);
edge->SetChildNode(*childNode);
edge->SetParentNode(*parentNode);
//_edges.push_back(edge);
// END OF TODO
// find all keywords for current node
AddAllTokensToElement("KEYWORD", tokens, *edge);
// TODO: store reference in child node and parent node
(*childNode)->AddEdgeToParentNode(edge); // TODO: add non-owning reference
(*parentNode)->AddEdgeToChildNode(edge); // TODO: transfer ownership to parent node
// END OF TODO
}
}
}
else
{
std::cout << "Error: ID missing. Line is ignored!" << std::endl;
}
}
} // eof loop over all lines in the file
file.close();
} // eof check for file availability
else
{
std::cout << "File could not be opened!" << std::endl;
return;
}
//// STUDENT CODE
////
// identify root node
GraphNode *rootNode = nullptr;
for (auto it = std::begin(_nodes); it != std::end(_nodes); ++it)
{
// search for nodes which have no incoming edges
if ((*it)->GetNumberOfParents() == 0)
{
if (rootNode == nullptr)
{
// assign current node to root
rootNode = *it; // TODO: assign current node to root
}
else
{
std::cout << "ERROR : Multiple root nodes detected" << std::endl;
}
}
}
// TODO: add chatbot to graph root node
_chatBot->SetRootNode(rootNode);
rootNode->moveChatbotHere(_chatBot);
// END OF TODO
}
#ifdef GUI_APP
void ChatLogic::SetPanelDialogHandle(ChatBotPanelDialog *panelDialog)
{
_panelDialog = panelDialog;
}
void ChatLogic::SendMessageToUser(std::string message)
{
_panelDialog->PrintChatbotResponse(message);
}
#else
void ChatLogic::SetPanelDialogHandle(ChatBotPanelDialog *panelDialog) {}
void ChatLogic::SendMessageToUser(std::string message) {}
void ChatLogic::SetChatbotHandle(ChatBot *chatbot) {
_chatBot = chatbot;
}
void ChatLogic::SendMessageToChatbot(std::string message) {
if (_chatBot) {
_chatBot->ReceiveMessageFromUser(message);
}
}
#endif
bool ChatLogic::IsGraphLoaded() const {
return !_nodes.empty();
}

49
starter/src/chatlogic.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef CHATLOGIC_H_
#define CHATLOGIC_H_
#include <vector>
#include <string>
#include <memory>
// #include "chatgui.h" // Removed for CLI decoupling
// forward declarations
class ChatBot;
class GraphEdge;
class GraphNode;
class ChatBotPanelDialog; // Forward declaration for GUI compatibility (deprecated)
class ChatLogic
{
private:
// data handles (owned)
std::vector<GraphNode *> _nodes; // TODO
// data handles (not owned)
GraphNode *_currentNode;
ChatBot *_chatBot;
ChatBotPanelDialog *_panelDialog;
// proprietary type definitions
typedef std::vector<std::pair<std::string, std::string>> tokenlist;
// proprietary functions
template <typename T>
void AddAllTokensToElement(std::string tokenID, tokenlist &tokens, T &element);
public:
// constructor / destructor
ChatLogic();
~ChatLogic();
// getter / setter
void SetPanelDialogHandle(ChatBotPanelDialog *panelDialog);
void SetChatbotHandle(ChatBot *chatbot);
// proprietary functions
void LoadAnswerGraphFromFile(std::string filename);
void SendMessageToChatbot(std::string message);
virtual void SendMessageToUser(std::string message);
bool IsGraphLoaded() const;
};
#endif /* CHATLOGIC_H_ */

22
starter/src/graphedge.cpp Normal file
View File

@@ -0,0 +1,22 @@
#include "graphnode.h"
#include "graphedge.h"
GraphEdge::GraphEdge(int id)
{
_id = id;
}
void GraphEdge::SetChildNode(GraphNode *childNode)
{
_childNode = childNode;
}
void GraphEdge::SetParentNode(GraphNode *parentNode)
{
_parentNode = parentNode;
}
void GraphEdge::AddToken(std::string token)
{
_keywords.push_back(token);
}

36
starter/src/graphedge.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef GRAPHEDGE_H_
#define GRAPHEDGE_H_
#include <vector>
#include <string>
class GraphNode; // forward declaration
class GraphEdge
{
private:
// data handles (not owned)
GraphNode *_childNode;
GraphNode *_parentNode;
// proprietary members
int _id;
std::vector<std::string> _keywords; // list of topics associated with this edge
public:
// constructor / desctructor
GraphEdge(int id);
// getter / setter
int GetID() { return _id; }
void SetChildNode(GraphNode *childNode);
void SetParentNode(GraphNode *parentNode);
GraphNode *GetChildNode() { return _childNode; }
std::vector<std::string> GetKeywords() { return _keywords; }
// proprietary functions
void AddToken(std::string token);
};
#endif /* GRAPHEDGE_H_ */

46
starter/src/graphnode.cpp Normal file
View File

@@ -0,0 +1,46 @@
#include "chatbot.h"
#include "graphedge.h"
#include "graphnode.h"
GraphNode::GraphNode(int id)
{
_id = id;
}
GraphNode::~GraphNode()
{
// leave as-is
}
void GraphNode::AddToken(std::string token)
{
_answers.push_back(token);
}
void GraphNode::AddEdgeToParentNode(GraphEdge *edge)
{
_parentEdges.push_back(edge);
}
void GraphNode::AddEdgeToChildNode(GraphEdge* edge)
{
_childEdges.push_back(edge); // TODO
}
void GraphNode::moveChatbotHere(ChatBot* chatbot)
{
_chatBot = chatbot;
_chatBot->SetCurrentNode(this);
}
void GraphNode::MoveChatbotToNewNode(GraphNode *newNode)
{
newNode->moveChatbotHere(std::move(_chatBot)); // TODO
}
GraphEdge *GraphNode::GetChildEdgeAtIndex(int index)
{
return _childEdges[index]; // TODO
}

54
starter/src/graphnode.h Normal file
View File

@@ -0,0 +1,54 @@
#ifndef GRAPHNODE_H_
#define GRAPHNODE_H_
#include <vector>
#include <string>
#include <memory>
#include "chatbot.h"
// forward declarations
class GraphEdge;
class GraphNode
{
private:
//// TODO
// data handles (owned)
std::vector<GraphEdge *> _childEdges; // edges to subsequent nodes
// data handles (not owned)
std::vector<GraphEdge *> _parentEdges; // edges to preceding nodes
ChatBot *_chatBot;
//// End of TODO
// proprietary members
int _id;
std::vector<std::string> _answers;
public:
// constructor / destructor
GraphNode(int id);
~GraphNode();
// getter / setter
int GetID() { return _id; }
int GetNumberOfChildEdges() { return _childEdges.size(); }
GraphEdge *GetChildEdgeAtIndex(int index);
std::vector<std::string> GetAnswers() { return _answers; }
int GetNumberOfParents() { return _parentEdges.size(); }
// proprietary functions
void AddToken(std::string token); // add answers to list
void AddEdgeToParentNode(GraphEdge *edge);
void AddEdgeToChildNode(GraphEdge *edge); // TODO
void moveChatbotHere(ChatBot *newNode); // TODO
void MoveChatbotToNewNode(GraphNode *newNode);
};
#endif /* GRAPHNODE_H_ */

35
starter/src/main_cli.cpp Normal file
View File

@@ -0,0 +1,35 @@
// CLI-only main for membot. Does not require wxWidgets or GUI code.
#define GUI_APP 0
#include <iostream>
#include <string>
#include "chatlogic.h"
// Simple CLI version of SendMessageToUser
class ChatLogicCLI : public ChatLogic {
public:
void SendMessageToUser(std::string message) override {
std::cout << "Bot: " << message << std::endl;
}
};
int main() {
ChatLogicCLI chatLogic;
chatLogic.LoadAnswerGraphFromFile("../src/answergraph.txt");
// Check if the root node was loaded (basic check for file load success)
// If the file could not be opened, exit gracefully
// (Assume that if no nodes are loaded, the file was not found or empty)
if (!chatLogic.IsGraphLoaded()) {
std::cerr << "Error: Could not load answer graph. Exiting." << std::endl;
return 1;
}
std::string userInput;
std::cout << "Type 'quit' to exit at anytime." << std::endl;
while (true) {
std::cout << "You: ";
std::getline(std::cin, userInput);
if (userInput == "quit") break;
chatLogic.SendMessageToChatbot(userInput);
}
std::cout << "Goodbye!" << std::endl;
return 0;
}