Solving 8-Puzzle in C++ with A* results in endless loop - c++

I'm currently trying to solve the 8-Puzzle with the A* search algorithm, but my program gets stuck in an endless loop.
My main searching loop is:
std::vector<Field> Search::AStar(Field &start, Field &goal){
std::cout << "Calculating..." << std::endl;
std::unordered_map<Field, Field> explored;
std::vector<Field> searched;
if (Puzzle::finished(start))
return MakePath(start, start);
std::priority_queue<Field, std::vector<Field>, std::greater<Field>> frontier;
frontier.push(start);
Field current;
Field child;
size_t i = 0;
while (!frontier.empty())
{
current = frontier.top();
frontier.pop();
if (++i > 500)
{
std::cout << "Iteration Error" << std::endl;
return searched;
}
searched.push_back(current);
for (Direction d : Puzzle::Actions(current))
{
child = Puzzle::Action(d, current);
if (Puzzle::finished(child))
{
std::cout << "Found goal!" << std::endl;
return MakePath(explored[child], start);
}
child.CostG = current.CostG + 1; // Make a step
if (!isIn(child, explored) || child.CostG < explored[child].CostG)
{
child.CostH = Puzzle::Heuristic(child, goal); // Calculate Heuristic
child.CostF = child.CostG + child.CostH; // Calculate final costs
frontier.push(child);
explored[child] = child;
explored[child].setParent(&explored[current]);
}
}
}
std::cout << "Error: frontier Empty" << std::endl;
return searched;
}
The vector "searched" is just so that I can see what A* does, and I will delete it as soon as the algorithm works.
The CostG stands for the number of steps done until this point, the CostH are the estimated minimum (heuristic) costs to the "goal" and the CostF are those two combined.
The index of the Field::Boxes vector is the number of the field, and every element contains the position.
My Heuristic function looks like this:
inline int Heuristic(Field &goal)
{
size_t d = 0;
for (size_t i = 0; i < Boxes.size(); i++)
{
d += (std::abs(static_cast<int>(Boxes[i].x) - static_cast<int>(goal.Boxes[i].x))
+ std::abs(static_cast<int>(Boxes[i].y) - static_cast<int>(goal.Boxes[i].y)));
}
return d;
}
For better readability and stuff, the code also is on Github. However, to execute it, you need SFML in your Visual Studio include direction.
Every help is appreciated!
Edit 1:
You now no longer need SFML to executed & debug the program! I commited the changes to github, the link is the same.

The problem is that although you remove the current node from your frontier, you never added it to the explored set, i.e. you never close it. The following code should work. My revisions closely follow Wikipedia's A* Pseudocode.
I also recommend you test your algorithm with the trivial heuristic (the one that returns zero for all values) on a simple puzzle to verify that your algorithm is implemented correctly. (See this answer for a brief explanation of this technique.)
while (!frontier.empty())
{
current = frontier.top();
frontier.pop();
if (++i > 500)
{
std::cout << "Iteration Error" << std::endl;
return searched;
}
// Check for goal here
if (Puzzle::finished(current)
{
std::cout << "Found goal!" << std::endl;
return MakePath(explored[current], start);
}
explored[current] = current; //close the current node
searched.push_back(current);
for (Direction d : Puzzle::Actions(current))
{
child = Puzzle::Action(d, current);
if (isIn(child,explored))
{
continue; //ignore the neighbor which is already evaluated
}
child.CostG = current.CostG + 1; // Make a step
if (!isIn(child, frontier)) //discovered a new node
{
frontier.push(child);
}
else if (child.CostG >= explored[child].CostG)
{
continue; //this is not a better path
{
//the path is best until now. Record it!
child.CostH = Puzzle::Heuristic(child, goal); // Calculate Heuristic
child.CostF = child.CostG + child.CostH; // Calculate final costs
//frontier.push(child); moved up to earlier point in code
explored[child] = child;
explored[child].setParent(&explored[current]);
}
}

Related

vector, removing object trouble

I am new to c++ and am currently learning sfml.
I have set up a system that adds one 'Snowpile' object to the vector. But when I keep hitting errors like "can't increment vector past iterator past end" or that it's outside the scope.
std::vector<Snowpile*> snowpiles;
I want it to check every snowpile for the removed2 bool, and delete the ones that do have it.
for (auto s_it = snowpiles.begin(); s_it != snowpiles.end(); s_it++) {
int sindex = std::distance(snowpiles.begin(), s_it);
if (snowpiles[sindex]->getSprite_S().getGlobalBounds().intersects(player.getSpriteP().getGlobalBounds()) && snowpiles[sindex]->melting == false) {
snowpiles[sindex]->melting = true;
}
else if (snowpiles[sindex]->getSprite_S().getGlobalBounds().intersects(player.getSpriteP().getGlobalBounds()) && snowpiles[sindex]->melting == true) {
snowpiles[sindex]->melting = true;
}
else if (!snowpiles[sindex]->getSprite_S().getGlobalBounds().intersects(player.getSpriteP().getGlobalBounds()) && snowpiles[sindex]->melting == true) {
snowpiles[sindex]->melting = false;
}
snowpiles[sindex]->meltedrem(sindex, snowpiles, m_win);
if (snowpiles[sindex]->removed2 == true)
{
cout << "Detected removed 2 at " << sindex << endl;
//delete snowpiles[sindex];
snowpiles.erase(snowpiles.begin() + sindex - 1);
}
}
The melting parts determine whether the player is on top of a snowpile. The meltdrem functions checks for bool 'melting' == true and then proceeds to start the timer. After a few seconds (+ animations) it sets the bool removed2 to true.
I know that at the program at least sees the bools changing, so thats not it.
Am I simply using vector wrong, or do I need to change something in my loop?
The loop is located in the while(window.isOpen()) loop in int main.
For starters it is unclear why there is used the expression
snowpiles.erase(snowpiles.begin() + sindex - 1);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
instead of
snowpiles.erase(snowpiles.begin() + sindex);
^^^^^^^^^^^^^^^^^^^^^^^^^^
if in a comment you wrote
//delete snowpiles[sindex];
You need to increase the iterator in the for loop only when a current object was not removed.
Change the loop the following way
for (auto s_it = snowpiles.begin(); s_it != snowpiles.end(); ) {
//...
if (snowpiles[sindex]->removed2 == true)
{
cout << "Detected removed 2 at " << sindex << endl;
//delete snowpiles[sindex];
s_it = snowpiles.erase( s_it );
}
else
{
++s_it;
}
}

C++ binary search tree creates segmentation fault

I'm trying to make a program that identifies AVR assembly instructions by opcode, since those are just a list of 1's and 0's I thought it would be a good project to make a binary search tree for.
Sadly I keep getting segmentation faults when trying to search through the tree. As I understand it a seg fault is usually the result of trying to do stuff with a pointer that doesn't point to anything, but since I have a Boolean that I check first that should never happen.
I'm pretty sure it has something to do with the way I use pointers, as I'm not very experienced with those. But I can't seem to figure out what's going wrong.
Below is the code involved (SearchTree is only a global variable in this minimal example, not in the real program.):
The code:
#include <iostream>
void ADD(short &code) {std::cout << code << "\n";}
void LDI(short &code) {std::cout << code << "\n";}
void SBRC(short &code){std::cout << code << "\n";}
struct node
{
void(* instruct)(short &code);
bool hasInst = false;
struct node *zero;
bool hasZero = false;
struct node *one;
bool hasOne = false;
};
node SearchTree;
auto parseOpcode(short code, node *currentRoot)
{
std::cout << "Looking for a: " << ((code >> 15) & 0b01 == 1) << std::endl;
std::cout << "Current node 1: " << (*currentRoot).hasOne << std::endl;
std::cout << "Current node 0: " << (*currentRoot).hasZero << std::endl;
// Return instruction if we've found it.
if ((*currentRoot).hasInst) return (*currentRoot).instruct;
// Case current bit == 1.
else if ((code >> 15) & 0b01 == 1)
{
if ((*currentRoot).hasOne) return parseOpcode((code << 1), (*currentRoot).one);
else throw "this instruction does not exist";
}
// Case current bit == 0.
else {
if ((*currentRoot).hasZero) return parseOpcode((code << 1), (*currentRoot).zero);
else throw "this instruction does not exist";
}
}
void addopcode(void(& instruct)(short &code), int opcode, int codeLength)
{
node *latest;
latest = &SearchTree;
for (int i = 0; i <= codeLength; i++)
{
// Add function pointer to struct if we hit the bottom.
if (i == codeLength)
{
if ((*latest).hasInst == false)
{
(*latest).instruct = &instruct;
(*latest).hasInst = true;
}
}
// Case 1
else if (opcode >> (codeLength - 1 - i) & 0b01)
{
if ((*latest).hasOne)
{
latest = (*latest).one;
}
else{
node newNode;
(*latest).one = &newNode;
(*latest).hasOne = true;
latest = &newNode;
}
}
// Case 0
else {
if ((*latest).hasZero)
{
latest = (*latest).zero;
}
else{
node newNode;
(*latest).zero = &newNode;
(*latest).hasZero = true;
latest = &newNode;
}
}
}
}
int main()
{
addopcode(ADD, 0b000011, 6);
addopcode(LDI, 0b1110, 4);
addopcode(SBRC, 0b1111110, 7);
short firstOpcode = 0b1110000000010011;
void(* instruction)(short &code) = parseOpcode(firstOpcode, &SearchTree);
instruction(firstOpcode);
return 0;
}
EDIT: I still had some #includes at the top of my file that linked to code I didn't put on StackOverflow.
The error happened because I forgot to use the new keyword and was therefor populating my search tree with local variables (which were obviously now longer around by the time I started searching through the tree).
Fixed by using:
node *newNode = new node();
(*latest).one = newNode;
(*latest).hasOne = true;
latest = newNode;
Instead of:
node newNode;
(*latest).one = &newNode;
(*latest).hasOne = true;
latest = &newNode;

Synchronizing MapB to MapA in C++

I have a std::map that I generate from a json and a map that I generate from sqlite.
I want to compare the two maps and make changes to the sqlite so that it matches the json. I originally used the map.find(key) method through both maps to figure out what to add and what to delete but my friend told me that map was sorted from least to greatest key and so I could just run through it once.
I came up with two methods. Do you have any advice on which algorithm would be preferred and why? I am thinking the one I have uncommented is faster as (and please correct me if I'm wrong) I believe the uncommented one is O(n) worst case while the latter is O(n^2) worst case.
Also, my friend had mentioned that I didn't need the second 'clean up' while loop to reconcile the remaining sqlMap items, but I really think I need it. Is he right?
Here's my code:
void SqlSync::syncEvents() {
int added = 0;
int replaced = 0;
int deleted = 0;
int skipped = 0;
// get categories from Apsiva
std::map<int, Event> jsonMap = _apsivaRest->getEvents();
// get categories from sqlite
std::map<int, Event> sqlMap = _sqliteConnection->getEventMap(true);
// COMPARE
map<int, Event>::iterator jsonIter = jsonMap.begin();
map<int, Event>::iterator sqlIter = sqlMap.begin();
while (jsonIter != jsonMap.end() && sqlIter != sqlMap.end()) {
int jsonId = jsonIter->first;
Event jsonObj = jsonIter->second;
int sqlId = sqlIter->first;
if (jsonId < sqlId) {
// add
_sqliteConnection->addEvent(jsonObj);
++added;
++jsonIter;
} else if (jsonId > sqlId) {
// remove
_sqliteConnection->deleteEvent(sqlId);
++deleted;
++sqlIter;
} else {
if (jsonObj.isNewerThan(sqlIter->second)) {
_sqliteConnection->updateEvent(jsonObj);
++replaced;
} else {
// ignore
cout << "Skipped event b/c not newer" << endl; // delete when verified
++skipped;
}
++jsonIter;
++sqlIter;
}
}
// int jRemaining = std::distance(jsonIter, jsonMap.end());
// int sRemaining = std::distance(sqlIter, sqlMap.end());
// add remaining jsonMap Objects
while (jsonIter != jsonMap.end()) {
Event jsonObj = jsonIter->second;
_sqliteConnection->addEvent(jsonIter->second);
++added;
++jsonIter;
}
// delete remaining sqlMap Objects
while (sqlIter != sqlMap.end()) {
_sqliteConnection->deleteEvent(sqlIter->first);
++deleted;
++sqlIter;
}
// OLD WAY TO COMPARE.
// // add/replace keys found in json
// for (map<int, Event>::const_iterator jsonIter = jsonMap.begin(); jsonIter != jsonMap.end(); ++jsonIter) {
// map<int,Event>::const_iterator it = sqlMap.find(jsonIter->first);
// Event jsonObj = jsonIter->second;
// if (it != sqlMap.end()) {
// Event sqlObj = it->second;
// if (jsonObj.isNewerThan(sqlObj)) {
//// _sqliteConnection->updateEvent(jsonObj);
// ++replaced;
// } else {
// // ignore
// cout << "Skipped category b/c not newer" << endl; // delete when verified
// ++skipped;
// }
// } else {
//// _sqliteConnection->addEvent(jsonObj);
// ++added;
// }
// }
//
// // delete sqlmap CategoryRows not in jsonMap
// for (map<int, Event>::const_iterator sqlObj = sqlMap.begin(); sqlObj != sqlMap.end(); ++sqlObj) {
// if (jsonMap.find(sqlObj->first) == jsonMap.end()) {
//// _sqliteConnection->deleteEvent(sqlObj->first);
// ++deleted;
// }
// }
#ifdef DEBUG
cout << "CATEGORIES SYNC:" << endl;
cout << "---------------" << endl;
cout << "Added: " << added << " | Replaced: " << replaced
<< " | Deleted: " << deleted << " | Skipped: " << skipped << endl;
#endif //DEBUG
}
The uncommented way is more efficient. The complexity will be O(n+m) when n and m are sizes of json and SQLite maps.
You will need the last loop, since when you exit the first loop you don't know which map end you reached first. Consider next case - json map has ids 1,2,4,5 and SQLite has ids 1,2,6,7.
You will need the last loop in order to delete items 6 and 7.

c++ trying to find the distance between 2 nodes

I am currently working on a function that has 1 helper function, the main function takes in 2 strings and searches for the first one (which becomes a reference as if it was m_root) and a second one to be searched in the tree. once they are searched, my helper function is supposed to search for the 2nd city and count the distance it had to travel as if a truck was going towards that city.
int Stree::distance(string origin_city, string destination_city)
{
int total_distance = 0;
Node *new_root = m_root;
new_root = find_node(m_root, origin_city);
total_distance = get_distance(new_root, total_distance, destination_city);
return total_distance;
}
int Stree::get_distance(Node* cur, int distance, string destination)
{
Node *tmp = cur;
if(cur == NULL)
return 0;
if(cur->m_city == destination || tmp->m_city == destination)
{
//cout << distance + cur->m_parent_distance << endl;
return distance += cur->m_parent_distance;
}
if(tmp->m_left != NULL)
{
//cout << "checking left" << endl;
tmp = cur->m_left;
return get_distance(cur->m_left, distance, destination) ;
}
else
{
//cout << "checking right" << endl;
return get_distance(cur->m_right, distance, destination);
}
}
In a cursory glance, I don't see anywhere that you modify or increment distance, whether it be the distance variable or something like:
return 1 + get_distance(cur->m_right, distance, destination);
So I would make sure that in an algorithmic sense, each step walked is counted, otherwise it will certainly return 0 every time.

C++ Decision Tree Implementation Question: Think In Code

I've been coding for a few years but I still haven't gotten the hang of pseudo-coding or actually thinking things out in code yet. Due to this problem, I'm having trouble figuring out exactly what to do in creating a learning Decision Tree.
Here are a few sites that I have looked at and trust me there were plenty more
Decision Tree Tutorials
DMS Tutorials
Along with several books such as Ian Millington's AI for Games which includes a decent run-down of the different learning algorithms used in decision trees and Behavioral Mathematics for Game Programming which is basically all about Decision Trees and theory. I understand the concepts for a decision tree along with Entropy, ID3 and a little on how to intertwine a genetic algorithm and have a decision tree decide the nodes for the GA.
They give good insight, but not what I am looking for really.
I do have some basic code that creates the nodes for the decision tree, and I believe I know how to implement actual logic but it's no use if I don't have a purpose to the program or have entropy or a learning algorithm involved.
What I am asking is, can someone help me figure out what I need to do to create this learning decision tree. I have my nodes in a class of their own flowing through functions to create the tree, but how would I put entropy into this and should it have a class, a struct I'm not sure how to put it together. Pseudo-code and an Idea of where I am going with all this theory and numbers. I can put the code together if only I knew what I needed to code. Any guidance would be appreciated.
How Would I Go About This, Basically.
Adding a learning algorithm such as ID3 and Entropy. How should it be set up?
Once I do have this figured out on how to go about all this,I plan to implement this into a state machine that goes through different states in a game/simulation format. All of this is already set up, I just figure this could be stand-alone and once I figure it out I can just move it to the other project.
Here is the source code I have for now.
Thanks in advance!
Main.cpp:
int main()
{
//create the new decision tree object
DecisionTree* NewTree = new DecisionTree();
//add root node the very first 'Question' or decision to be made
//is monster health greater than player health?
NewTree->CreateRootNode(1);
//add nodes depending on decisions
//2nd decision to be made
//is monster strength greater than player strength?
NewTree->AddNode1(1, 2);
//3rd decision
//is the monster closer than home base?
NewTree->AddNode2(1, 3);
//depending on the weights of all three decisions, will return certain node result
//results!
//Run, Attack,
NewTree->AddNode1(2, 4);
NewTree->AddNode2(2, 5);
NewTree->AddNode1(3, 6);
NewTree->AddNode2(3, 7);
//Others: Run to Base ++ Strength, Surrender Monster/Player,
//needs to be made recursive, that way when strength++ it affects decisions second time around DT
//display information after creating all the nodes
//display the entire tree, i want to make it look like the actual diagram!
NewTree->Output();
//ask/answer question decision making process
NewTree->Query();
cout << "Decision Made. Press Any Key To Quit." << endl;
//pause quit, oh wait how did you do that again...look it up and put here
//release memory!
delete NewTree;
//return random value
//return 1;
}
Decision Tree.h:
//the decision tree class
class DecisionTree
{
public:
//functions
void RemoveNode(TreeNodes* node);
void DisplayTree(TreeNodes* CurrentNode);
void Output();
void Query();
void QueryTree(TreeNodes* rootNode);
void AddNode1(int ExistingNodeID, int NewNodeID);
void AddNode2(int ExistingNodeID, int NewNodeID);
void CreateRootNode(int NodeID);
void MakeDecision(TreeNodes* node);
bool SearchAddNode1(TreeNodes* CurrentNode, int ExistingNodeID, int NewNodeID);
bool SearchAddNode2(TreeNodes* CurrentNode, int ExistingNodeID, int NewNodeID);
TreeNodes* m_RootNode;
DecisionTree();
virtual ~DecisionTree();
};
Decisions.cpp:
int random(int upperLimit);
//for random variables that will effect decisions/node values/weights
int random(int upperLimit)
{
int randNum = rand() % upperLimit;
return randNum;
}
//constructor
//Step 1!
DecisionTree::DecisionTree()
{
//set root node to null on tree creation
//beginning of tree creation
m_RootNode = NULL;
}
//destructor
//Final Step in a sense
DecisionTree::~DecisionTree()
{
RemoveNode(m_RootNode);
}
//Step 2!
void DecisionTree::CreateRootNode(int NodeID)
{
//create root node with specific ID
// In MO, you may want to use thestatic creation of IDs like with entities. depends on how many nodes you plan to have
//or have instantaneously created nodes/changing nodes
m_RootNode = new TreeNodes(NodeID);
}
//Step 5.1!~
void DecisionTree::AddNode1(int ExistingNodeID, int NewNodeID)
{
//check to make sure you have a root node. can't add another node without a root node
if(m_RootNode == NULL)
{
cout << "ERROR - No Root Node";
return;
}
if(SearchAddNode1(m_RootNode, ExistingNodeID, NewNodeID))
{
cout << "Added Node Type1 With ID " << NewNodeID << " onto Branch Level " << ExistingNodeID << endl;
}
else
{
//check
cout << "Node: " << ExistingNodeID << " Not Found.";
}
}
//Step 6.1!~ search and add new node to current node
bool DecisionTree::SearchAddNode1(TreeNodes *CurrentNode, int ExistingNodeID, int NewNodeID)
{
//if there is a node
if(CurrentNode->m_NodeID == ExistingNodeID)
{
//create the node
if(CurrentNode->NewBranch1 == NULL)
{
CurrentNode->NewBranch1 = new TreeNodes(NewNodeID);
}
else
{
CurrentNode->NewBranch1 = new TreeNodes(NewNodeID);
}
return true;
}
else
{
//try branch if it exists
//for a third, add another one of these too!
if(CurrentNode->NewBranch1 != NULL)
{
if(SearchAddNode1(CurrentNode->NewBranch1, ExistingNodeID, NewNodeID))
{
return true;
}
else
{
//try second branch if it exists
if(CurrentNode->NewBranch2 != NULL)
{
return(SearchAddNode2(CurrentNode->NewBranch2, ExistingNodeID, NewNodeID));
}
else
{
return false;
}
}
}
return false;
}
}
//Step 5.2!~ does same thing as node 1. if you wanted to have more decisions,
//create a node 3 which would be the same as this maybe with small differences
void DecisionTree::AddNode2(int ExistingNodeID, int NewNodeID)
{
if(m_RootNode == NULL)
{
cout << "ERROR - No Root Node";
}
if(SearchAddNode2(m_RootNode, ExistingNodeID, NewNodeID))
{
cout << "Added Node Type2 With ID " << NewNodeID << " onto Branch Level " << ExistingNodeID << endl;
}
else
{
cout << "Node: " << ExistingNodeID << " Not Found.";
}
}
//Step 6.2!~ search and add new node to current node
//as stated earlier, make one for 3rd node if there was meant to be one
bool DecisionTree::SearchAddNode2(TreeNodes *CurrentNode, int ExistingNodeID, int NewNodeID)
{
if(CurrentNode->m_NodeID == ExistingNodeID)
{
//create the node
if(CurrentNode->NewBranch2 == NULL)
{
CurrentNode->NewBranch2 = new TreeNodes(NewNodeID);
}
else
{
CurrentNode->NewBranch2 = new TreeNodes(NewNodeID);
}
return true;
}
else
{
//try branch if it exists
if(CurrentNode->NewBranch1 != NULL)
{
if(SearchAddNode2(CurrentNode->NewBranch1, ExistingNodeID, NewNodeID))
{
return true;
}
else
{
//try second branch if it exists
if(CurrentNode->NewBranch2 != NULL)
{
return(SearchAddNode2(CurrentNode->NewBranch2, ExistingNodeID, NewNodeID));
}
else
{
return false;
}
}
}
return false;
}
}
//Step 11
void DecisionTree::QueryTree(TreeNodes* CurrentNode)
{
if(CurrentNode->NewBranch1 == NULL)
{
//if both branches are null, tree is at a decision outcome state
if(CurrentNode->NewBranch2 == NULL)
{
//output decision 'question'
///////////////////////////////////////////////////////////////////////////////////////
}
else
{
cout << "Missing Branch 1";
}
return;
}
if(CurrentNode->NewBranch2 == NULL)
{
cout << "Missing Branch 2";
return;
}
//otherwise test decisions at current node
MakeDecision(CurrentNode);
}
//Step 10
void DecisionTree::Query()
{
QueryTree(m_RootNode);
}
////////////////////////////////////////////////////////////
//debate decisions create new function for decision logic
// cout << node->stringforquestion;
//Step 12
void DecisionTree::MakeDecision(TreeNodes *node)
{
//should I declare variables here or inside of decisions.h
int PHealth;
int MHealth;
int PStrength;
int MStrength;
int DistanceFBase;
int DistanceFMonster;
////sets random!
srand(time(NULL));
//randomly create the numbers for health, strength and distance for each variable
PHealth = random(60);
MHealth = random(60);
PStrength = random(50);
MStrength = random(50);
DistanceFBase = random(75);
DistanceFMonster = random(75);
//the decision to be made string example: Player health: Monster Health: player health is lower/higher
cout << "Player Health: " << PHealth << endl;
cout << "Monster Health: " << MHealth << endl;
cout << "Player Strength: " << PStrength << endl;
cout << "Monster Strength: " << MStrength << endl;
cout << "Distance Player is From Base: " << DistanceFBase << endl;
cout << "Disntace Player is From Monster: " << DistanceFMonster << endl;
//MH > PH
//MH < PH
//PS > MS
//PS > MS
//DB > DM
//DB < DM
//good place to break off into different decision nodes, not just 'binary'
//if statement lower/higher query respective branch
if(PHealth > MHealth)
{
}
else
{
}
//re-do question for next branch. Player strength: Monster strength: Player strength is lower/higher
//if statement lower/higher query respective branch
if(PStrength > MStrength)
{
}
else
{
}
//recursive question for next branch. Player distance from base/monster.
if(DistanceFBase > DistanceFMonster)
{
}
else
{
}
//DECISION WOULD BE MADE
//if statement?
// inside query output decision?
//cout << <<
//QueryTree(node->NewBranch2);
//MakeDecision(node);
}
//Step.....8ish?
void DecisionTree::Output()
{
//take repsective node
DisplayTree(m_RootNode);
}
//Step 9
void DecisionTree::DisplayTree(TreeNodes* CurrentNode)
{
//if it doesn't exist, don't display of course
if(CurrentNode == NULL)
{
return;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
//need to make a string to display for each branch
cout << "Node ID " << CurrentNode->m_NodeID << "Decision Display: " << endl;
//go down branch 1
DisplayTree(CurrentNode->NewBranch1);
//go down branch 2
DisplayTree(CurrentNode->NewBranch2);
}
//Final step at least in this case. A way to Delete node from tree. Can't think of a way to use it yet but i know it's needed
void DecisionTree::RemoveNode(TreeNodes *node)
{
//could probably even make it to where you delete a specific node by using it's ID
if(node != NULL)
{
if(node->NewBranch1 != NULL)
{
RemoveNode(node->NewBranch1);
}
if(node->NewBranch2 != NULL)
{
RemoveNode(node->NewBranch2);
}
cout << "Deleting Node" << node->m_NodeID << endl;
//delete node from memory
delete node;
//reset node
node = NULL;
}
}
TreeNodes.h:
using namespace std;
//tree node class
class TreeNodes
{
public:
//tree node functions
TreeNodes(int nodeID/*, string QA*/);
TreeNodes();
virtual ~TreeNodes();
int m_NodeID;
TreeNodes* NewBranch1;
TreeNodes* NewBranch2;
};
TreeNodes.cpp:
//contrctor
TreeNodes::TreeNodes()
{
NewBranch1 = NULL;
NewBranch2 = NULL;
m_NodeID = 0;
}
//deconstructor
TreeNodes::~TreeNodes()
{ }
//Step 3! Also step 7 hah!
TreeNodes::TreeNodes(int nodeID/*, string NQA*/)
{
//create tree node with a specific node ID
m_NodeID = nodeID;
//reset nodes/make sure! that they are null. I wont have any funny business #s -_-
NewBranch1 = NULL;
NewBranch2 = NULL;
}
Please correct me if I'm wrong but judging from the images at http://dms.irb.hr/tutorial/tut_dtrees.php and http://www.decisiontrees.net/?q=node/21 the actual decision logic should go in the nodes, not in the tree. You could model that by having polymorphic nodes, one for each decision there is to be made. With a bit of change to the tree construction and a minor minor modification for the decision delegation your code should be fine.
Basically you need to break everything down into stages and then modularise each part of the algorithm that you are trying to implement.
You can do this in pseudo-code or in code itself using functions/classes and stubs.
Each part of the algorithm you can then code up in some function and even test that function before adding it all together. You will basically end up with various functions or classes that are used for specific purposes in the algorithm implementation. So in your case for tree construction you will have one function that calculates entropy at a node, another that partitions the data into subsets at each node, etc.
I am talking in the general case here rather than specifically with respect to decision tree construction. Check out the book on Machine Learning by Mitchell if you need specific information on decision tree algorithms and the steps involved.
The pseudocode to implement a decision tree is as follows
createdecisiontree(data, attributes)
Select the attribute a with the highest information gain
for each value v of the attribute a
Create subset of data where data.a.val==v ( call it data2)
Remove the attribute a from the attribute list resulting in attribute_list2
Call CreateDecisionTree(data2, attribute_list2)
You will have to store nodes at each level with some code like
decisiontree[attr][val]=new_node