I am trying to port a TreeBuilder Python class to C++, keeping the structure as close as possible to the original. Here is a simplified Python version: https://onlinegdb.com/I4dg0hCtg
- The purpose of tree is to represent a generic game tree (Chess, Go, Checkers, etc.) It is passed forward to a class (Whatever), which builds an another representation based on some depth-wise statistics.
- Environment here is a mock class. In the real application, actions are passed back based on the game's rules
- The main reason of porting the code is speed. I am very new to C++, but as I know dynamic allocation on the heap costs more. I do not know if it worth (or even possible) to build such a tree without dynamic initialization.
- As far as I know using raw pointers has a little benefit of performance over smart pointers, but GC should be done by hand. My plan is after the trees are generated, and used by Whatever class (see my first point), Whatever class has to destroy the tree's node recursively. (Node's destructor should delete its children)
main.cc
#include "Node.h"
#include "Environment.h"
#include "Options.h"
#include "TreeBuilder.h"
int main() {
Node* node = new Node();
Environment* environment = new Environment();
node->environment = environment;
Options* options = new Options();
options->root = node;
TreeBuilder* treeBuilder = new TreeBuilder();
Node* tree = treeBuilder->buildTree(options);
tree->printTree();
return 0;
}
Node.h
#pragma once
#include <vector>
#include <iostream>
// Environment and Node has circular dependency, forward declaration
class Environment;
class Node
{
public:
Node * parent = nullptr;
int depth = 0;
std::vector < Node * >children;
Environment *environment = nullptr;
void printTree ();
~Node();
};
Node.cc
#include "Node.h"
void Node::printTree ()
{
// Print this node's depth
for (int i = 0; i < this->depth; i++)
{
std::cout << "\t";
}
std::cout << "- Depth: " << this->depth << std::endl;
// Recursively print the children
for (Node * child:this->children) {
child->printTree ();
}
}
Node::~Node() {
for (Node* child : children) {
delete child;
}
}
Environment.h
#pragma once
#include <vector>
#include "Node.h"
class Environment
{
public:
std::vector < int >getActions (Node * parent);
};
Environment.cc
#include "Environment.h"
std::vector<int> Environment::getActions(Node* parent) {
std::vector<int> actions;
if (parent->depth != 3) {
for (int x = 0; x <= parent->depth; x++) {
actions.push_back(x * 100);
}
}
return actions;
}
Options.h
#pragma once
#include "Node.h"
#include "Environment.h"
class Options
{
public:
Node * root = nullptr;
Environment *environment = nullptr;
};
TreeBuilder.h
#pragma once
#include "Node.h"
#include "Environment.h"
#include "Options.h"
class TreeBuilder
{
public:
Environment * environment = nullptr;
std::vector < Node * >getChildren (Node * parent);
void _buildTree (Node * node);
Node *buildTree (Options * options);
};
TreeBuilder.cc
#include "TreeBuilder.h"
#include <iostream>
std::vector < Node * >TreeBuilder::getChildren (Node * parent)
{
std::vector < Node * >children;
std::vector < int >
actions = this->environment->getActions (parent);
for (int x = 0; x < actions.size (); x++)
{
Node* node = new Node ();
node->parent = parent;
node->depth = parent->depth + 1;
node->environment = parent->environment;
children.push_back (node);
}
return children;
}
void
TreeBuilder::_buildTree (Node * node)
{
std::vector < Node * >children = this->getChildren (node);
node->children = children;
for (int i = 0; i < children.size (); i++)
{
this->_buildTree (children[i]);
}
}
Node* TreeBuilder::buildTree (Options * options)
{
Node *root = new Node ();
root->environment = options->root->environment;
this->environment = root->environment;
this->_buildTree (root);
return root;
}
Here is a playground version, https://onlinegdb.com/eHbJGXqN4, to see it in action.