0

I'm implementing a kind of tree search that requires being able to get a "most promising node" from a tree and then doing something with that node in order to update the remainder of the tree for the next iteration.

Problem: an object pointer Board* has vector attributes that seem to change between the return of the function producing them, and the Board* value holding them in the calling environment.

My output:

>>g++ -std=c++17 -o tmp.out tests/test.cpp // <- require c++17 for other parts of the project
>>./tmp.out 
Best leaf by tree traversal has score: 8
Best leaf associated state has -1977735524 values in its attribute vector though! (Should be 4)

What I'd expect:

>>g++ -std=c++17 -o tmp.out tests/test.cpp // <- require c++17 for other parts of the project
>>./tmp.out 
Best leaf by tree traversal has score: 8
Best leaf associated state has 4 values in its attribute vector though! (Should be 4)
#include <iostream>
#include <vector>
#include <queue>

using namespace std;

class Board{
    vector<int> attribute;
    string name;
public:
    Board(){
        attribute = {1,2,3,4};
        name = "nonempty name";
    }
    Board getcopy(){
        return *this;
    }
    int AttrLen(){
        return attribute.size();
    }
};

class Node{
    Board* board;

    Node* parent;

    std::vector<Node*> children;

    int depth=0;

    int score=0;

    bool terminal=false;
public:
    Node(Node* _parent, Board* _board,int _depth){
        parent = _parent;
        board = _board;

        depth = _depth;

        // randomize score
        score = rand() % 10;

        if(depth<2){
            for(int _=0;_<2;_++){
                Board new_state = board -> getcopy();
                children.push_back(new Node(this,&new_state,depth+1));
            }
        } else {
            children = {};
            terminal=true;
        }
    }
    int getScore(){
        return score;
    }
    bool isTerminal(){
        return terminal;
    }
    Node* getBestChild(){
        if(!terminal){
            if(children[0] ->getScore() > children[1] -> getScore()){
                return children[0];
            } else {
                return children[1];
            }
        } else {
            return nullptr;
        }
    }
    Board* getStateptr(){
        return board;
    }

};

int main(){

    // make a board
    Board my_board = Board();

    // make a root node
    Node root = Node(nullptr, &my_board, 0);

    Node* best_child = root.getBestChild();
    while(!best_child -> isTerminal()){
        best_child = best_child -> getBestChild();
    }

    cout << "Best leaf by tree traversal has score: " << best_child -> getScore() << endl;
    cout << "Best leaf associated state has " << best_child -> getStateptr() ->AttrLen() << " values in its attribute vector though! (Should be 4)" << endl;

}
magikarp
  • 21
  • 2
  • 2
    Please post a [mcve] – 463035818_is_not_an_ai Jun 05 '20 at 15:42
  • Right now this code is failing for reasons I suspect are the same as the reason my real code is failing. Of primary interest to me is why the vector attributes on `Board*` are changing size between return and calling-environment instantiation. I tried to make it so the printouts show what I'm talking about. – magikarp Jun 06 '20 at 08:48
  • when I compile and run this I get a wall of output, but I have no clue what you think is wrong about it. Please include outptu and expected output in the question. And please try to reduce the code, I suposse not all of it is needed to reproduce the problem – 463035818_is_not_an_ai Jun 06 '20 at 12:47
  • ah yes, I suppose that's important. I reduced printouts and tried to make printouts more readability and added in my output/expected output. – magikarp Jun 06 '20 at 13:44
  • `new_child` is a `Node*`, why would it print `` ? – 463035818_is_not_an_ai Jun 06 '20 at 13:45
  • writing too fast and not editing. Changed to `addr` for clarity – magikarp Jun 06 '20 at 14:29
  • I tried way simplifying but maintaining the original problem (vector attribute changing). – magikarp Jun 06 '20 at 16:52

1 Answers1

0

Not sure if this is the only problem, but here

for(int _=0;_<2;_++){
    Board new_state = board -> getcopy();
    children.push_back(new Node(this,&new_state,depth+1));
}

Your create a copy of Board which only lives inside the for loop. It gets destroyed automatically at }. Hence the pointers you store in the node are dangling. The point to objects that are long gone.

Note that your getcopy is a bit weird. You should use a copy constructor instead. In general, when your type mangages resources via raw pointers (not sure if it actually is the case) then you need to respect the rule of 3/5. In any case you can copy a board simply by writing Board new_state = *board; (assuming the compiler generated copy constructor does the right thing).

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • I think this is the right track. This other post about trying to build c++ trees points out the same kind of issue. I think I have to go back to the drawing board and either try to avoid copying the state at all (which I rarely have to do in my implementation anyway) or find a way to handle vector attributes that makes them friendly for copying. https://stackoverflow.com/questions/51111109/c-tree-data-structure – magikarp Jun 06 '20 at 17:21