Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Name Nodes #17

Open
xescugc opened this issue May 18, 2024 · 3 comments
Open

Name Nodes #17

xescugc opened this issue May 18, 2024 · 3 comments

Comments

@xescugc
Copy link

xescugc commented May 18, 2024

Is your feature request related to a problem? Please describe.

In any example I see of Behavior Trees, either graphic of code the Nodes always have names as it helps to describe what they are doing, and if the tree is a bit big, without names it's not that easy. I end up having to add comments just to know what each node is about on the construction of the tree.

Describe the solution you'd like

To not brake the API bht.New() and bht.NewNode() I would add an extension to those 2 with a bht.NewWithName() and bht.NewNodeWithName where the name string would be the first parameter so then it would make it easier to read on the code.

Now, the question is where is this name string stored? As Node is just a func () (Tick, []Node) so it cannot store anything. Reading the code I found the bht.Frame which is basically used for printing (from the code):

// This packages captures details about the caller of it's New and NewNode functions, embedding them into the
// nodes themselves, for tree printing / tracing purposes.

So basically adding there the Name string would be the best place as then you can also add it to the printing of the Nodes as then the output will be much more clear.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

@joeycumines
Copy link
Owner

joeycumines commented May 18, 2024

May I ask what your main goal is, re: visualising the tree?

The tree printer was primarily added as a quick debugging tool, and I've used it in the past to unit test the structure of subtrees that are built dynamically. The reason why it is a text format is because its core use case is simply the bt.Node.String method.
Since I define (and document) my trees in code, I've not had much use case for expanding it.

If you wish to attach rich metadata, there are a few options. It is not intended for frequent runtime use, due to the mechanism used, but I have implemented context.Context-like value support.
This mechanism is how the bt.Frame information is attached, and you can attach whatever you'd like (read the docs for context.Context for guidance on things like keys).
Attaching extra metadata should not be a significant performance concern, beyond the extra indirection / potential performance hits caused by additional function calls (up to one per WithValue, because that function wraps the bt.Node with an additional closure), depending what ends up inlined by the compiler. Accessing values needs to be performed with care, however, see the docs for details.

If your goal is to be able to output a visual representation of any behavior tree that is as rich and informative as possible, it may be worth considering defining your tree using a data structure more amenable to that. For example, your own Node struct in your own package could have a func (x *Node) BT() bt.Node { return x.bt } method, and x.bt could be a method like func (x *Node) bt() (bt.Tick, []bt.Node) { return x.Tick, x.children() }, where Tick an is just an exported field that you set to an arbitrary tick. This would give you a type safe way to attach whatever metadata you need, and would obviate any need to use the Value mechanism provided by this package. You could also attach this hypothetical Node tree to the actual bt.Node values, although that might be more tricky and/or of limited value, depending on what specifically you wanted to achieve.
Now that I think about it, Node.valueHandle doesn't need to be a method (could just be a function), and could be exported, as HandleValue or some such, so that it could be called within the hypothetical x.bt implementation, above.

Regardless of how you go about it (there's more possibilities than what I've listed), if you want the bt.Node.String method to behave in a certain way, you can set the bt.DefaultPrinter global. You can re-use the bt.TreePrinter and customise however you'd like, e.g. to pull in a name from a custom value key.

Just in case it's useful, here's an example of an implementation from one of my personal projects. I'm not attempting to dissuade you from more sophisticated visualisation, just providing some context re: the degree of complexity and style that I find tenable w/o anything more than code:

func (x *Service) start() bt.Ticker {
	// stream state managed via bt

	// subscribe is the core logic per stream, used in two spots (we wait for it to finish)
	// note it always fails, triggering the teardown
	subscribe := bt.New(
		// memorize the sequence - so we keep running for to completion
		bt.Memorize(bt.Sequence),
		// guard invalid state (so we only run it once per stream)
		bt.New(bt.Not(x.streamIsNil)),
		bt.New(bt.Not(x.streamIsDone)),
		bt.New(bt.Not(x.streamIsDisabled)),
		bt.New(bt.Async(x.streamAccount)),
		bt.New(bt.Selector), // fails
	)

	// manages the (re)connection lifecycle
	lifecycle := bt.New(
		bt.Selector,
		// init and run stream
		bt.New(
			bt.Sequence,
			// guard failures
			bt.New(
				bt.Selector,
				bt.New(x.streamIsNil),
				bt.New(bt.Not(x.streamIsDone)),
			),
			// connect if necessary
			bt.New(
				bt.Selector,
				bt.New(bt.Not(x.streamIsNil)),
				bt.New(x.setupStream),
			),
			// subscribes to the stream, runs to completion (see below above)
			subscribe,
		),
		// teardown stream if necessary (and possible)
		bt.New(
			bt.Selector,
			bt.New(x.streamIsNil),
			// ensure we wait for subscribe to complete
			bt.New(func([]bt.Node) (bt.Status, error) {
				stat, err := subscribe.Tick()
				if err != nil {
					return 0, err
				}
				if stat == bt.Running {
					// wait for subscribe to complete
					return bt.Success, nil
				}
				// allow teardown to proceed
				return bt.Failure, nil
			}),
			// note: this is the only thing that nils the stream
			bt.New(x.teardownStream),
		),
		// debug failure - should never reach here
		bt.New(func(children []bt.Node) (bt.Status, error) {
			return bt.Failure, errors.New(`malformed bt`)
		}),
	)

	// Shuts the active stream, if necessary.
	//
	// Returns success only if the following is all true:
	//
	//   1. disabled api
	//   2. stream is nil (has been torn down)
	//   3. shutdown isn't running
	disable := bt.New(
		bt.Memorize(bt.Sequence),
		bt.New(x.streamIsDisabled),
		bt.New(
			bt.Memorize(bt.Selector),
			bt.New(
				bt.Sequence,
				bt.New(x.streamIsNil),
				bt.New(x.setStreamClosed(true)),
			),
			bt.New(
				bt.Memorize(bt.Sequence),
				x.shutdownStream(time.Minute*3), // also handles updating x.state
				bt.New(bt.Selector),             // always fails
			),
		),
	)

	root := bt.New(
		bt.Selector,
		disable,
		bt.New(
			bt.Sequence,
			bt.New(x.setStreamClosed(false)),
			lifecycle,
		),
	)

	return bt.NewTicker(x.ctx, time.Millisecond*100, root)
}

@xescugc
Copy link
Author

xescugc commented May 20, 2024

This was opened also relating it to #18 at the end.

I knew about the ctx but as you mention (and checking the docs) looked like it should not be used for that, and also other libs, like the one you shared on another issue https://github.com/BehaviorTree/BehaviorTree.CPP also have names so I thought it would be a good idea to have those for the nodes, as IMO it makes sense.

My examples are much smaller (and most likely it can be improved too haha I'm still building it):

	return bht.New(
		bht.Shuffle(bht.Selector, nil),
		// Units
		bht.New(
			bht.Selector,
			// Update
			bht.New(
				bht.Selector,
				b.updateUnits()...,
			),
			// Summon
			bht.New(
				bht.Shuffle(bht.Selector, nil),
				b.summonUnits()...,
			),
		),
		// Towers
		bht.New(
			bht.Shuffle(bht.Selector, nil),
			// Update
			bht.New(
				bht.Sequence,
				bht.New(b.findTowerToUpdate()),
				bht.New(b.updateTower()),
			),
			// Place
			bht.New(
				bht.Selector,
				b.placeTowers()...,
			),
		),
	)

It's a bot for a game I'm building hehe.

I have to rely on // to know where I'm on the tree and also I have a few nodes that are generated from a list of elements which end up in an output of the graph beeing a bit unreadable as those nodes use the same function but are parametrized, so I end up seeing a log of the same function name:

[0x571c8e bot.go:52 0x572220 shuffle.go:31]  github.com/xescugc/maze-wars/server/bot.(*Bot).Node | github.com/xescugc/maze-wars/server/bot.(*Bot).Node.Shuffle.func1
├── [0x571a12 bot.go:55 0x4cc100 selector.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).Node | github.com/joeycumines/go-behaviortree.Selector
│   ├── [0x571958 bot.go:58 0x4cc100 selector.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).Node | github.com/joeycumines/go-behaviortree.Selector
│   │   ├── [0x57258a bot.go:94 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/joeycumines/go-behaviortree.Sequence
│   │   │   ├── [0x5725d4 bot.go:96 0x572880 bot.go:104]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).canUpdateUnit.func2
│   │   │   └── [0x57266b bot.go:97 0x572820 bot.go:115]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).updateUnit.func3
│   │   ├── [0x57258a bot.go:94 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/joeycumines/go-behaviortree.Sequence
│   │   │   ├── [0x5725d4 bot.go:96 0x572880 bot.go:104]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).canUpdateUnit.func2
│   │   │   └── [0x57266b bot.go:97 0x572820 bot.go:115]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).updateUnit.func3
│   │   ├── [0x57258a bot.go:94 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/joeycumines/go-behaviortree.Sequence
│   │   │   ├── [0x5725d4 bot.go:96 0x572880 bot.go:104]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).canUpdateUnit.func2
│   │   │   └── [0x57266b bot.go:97 0x572820 bot.go:115]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).updateUnit.func3
│   │   ├── [0x57258a bot.go:94 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/joeycumines/go-behaviortree.Sequence
│   │   │   ├── [0x5725d4 bot.go:96 0x572880 bot.go:104]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).canUpdateUnit.func2
│   │   │   └── [0x57266b bot.go:97 0x572820 bot.go:115]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).updateUnit.func3
│   │   ├── [0x57258a bot.go:94 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/joeycumines/go-behaviortree.Sequence
│   │   │   ├── [0x5725d4 bot.go:96 0x572880 bot.go:104]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).canUpdateUnit.func2
│   │   │   └── [0x57266b bot.go:97 0x572820 bot.go:115]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).updateUnit.func3
│   │   ├── [0x57258a bot.go:94 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/joeycumines/go-behaviortree.Sequence
│   │   │   ├── [0x5725d4 bot.go:96 0x572880 bot.go:104]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).canUpdateUnit.func2
│   │   │   └── [0x57266b bot.go:97 0x572820 bot.go:115]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).updateUnit.func3
│   │   ├── [0x57258a bot.go:94 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/joeycumines/go-behaviortree.Sequence
│   │   │   ├── [0x5725d4 bot.go:96 0x572880 bot.go:104]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).canUpdateUnit.func2
│   │   │   └── [0x57266b bot.go:97 0x572820 bot.go:115]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).updateUnit.func3
│   │   ├── [0x57258a bot.go:94 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/joeycumines/go-behaviortree.Sequence
│   │   │   ├── [0x5725d4 bot.go:96 0x572880 bot.go:104]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).canUpdateUnit.func2
│   │   │   └── [0x57266b bot.go:97 0x572820 bot.go:115]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).updateUnit.func3
│   │   ├── [0x57258a bot.go:94 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/joeycumines/go-behaviortree.Sequence
│   │   │   ├── [0x5725d4 bot.go:96 0x572880 bot.go:104]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).canUpdateUnit.func2
│   │   │   └── [0x57266b bot.go:97 0x572820 bot.go:115]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).updateUnit.func3
│   │   └── [0x57258a bot.go:94 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/joeycumines/go-behaviortree.Sequence
│   │       ├── [0x5725d4 bot.go:96 0x572880 bot.go:104]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).canUpdateUnit.func2
│   │       └── [0x57266b bot.go:97 0x572820 bot.go:115]  github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).updateUnits.(*Bot).updateUnit.func3
│   └── [0x5719fc bot.go:63 0x5720a0 shuffle.go:31 ]  github.com/xescugc/maze-wars/server/bot.(*Bot).Node | github.com/xescugc/maze-wars/server/bot.(*Bot).Node.Shuffle.func2
│       ├── [0x572a34 bot.go:124 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/joeycumines/go-behaviortree.Sequence
│       │   ├── [0x572a99 bot.go:126 0x572dc0 bot.go:134]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).canSummonUnit.func1
│       │   └── [0x572b29 bot.go:127 0x572cc0 bot.go:145]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).summonUnit.func2
│       ├── [0x572a34 bot.go:124 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/joeycumines/go-behaviortree.Sequence
│       │   ├── [0x572a99 bot.go:126 0x572dc0 bot.go:134]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).canSummonUnit.func1
│       │   └── [0x572b29 bot.go:127 0x572cc0 bot.go:145]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).summonUnit.func2
│       ├── [0x572a34 bot.go:124 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/joeycumines/go-behaviortree.Sequence
│       │   ├── [0x572a99 bot.go:126 0x572dc0 bot.go:134]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).canSummonUnit.func1
│       │   └── [0x572b29 bot.go:127 0x572cc0 bot.go:145]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).summonUnit.func2
│       ├── [0x572a34 bot.go:124 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/joeycumines/go-behaviortree.Sequence
│       │   ├── [0x572a99 bot.go:126 0x572dc0 bot.go:134]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).canSummonUnit.func1
│       │   └── [0x572b29 bot.go:127 0x572cc0 bot.go:145]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).summonUnit.func2
│       ├── [0x572a34 bot.go:124 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/joeycumines/go-behaviortree.Sequence
│       │   ├── [0x572a99 bot.go:126 0x572dc0 bot.go:134]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).canSummonUnit.func1
│       │   └── [0x572b29 bot.go:127 0x572cc0 bot.go:145]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).summonUnit.func2
│       ├── [0x572a34 bot.go:124 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/joeycumines/go-behaviortree.Sequence
│       │   ├── [0x572a99 bot.go:126 0x572dc0 bot.go:134]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).canSummonUnit.func1
│       │   └── [0x572b29 bot.go:127 0x572cc0 bot.go:145]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).summonUnit.func2
│       ├── [0x572a34 bot.go:124 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/joeycumines/go-behaviortree.Sequence
│       │   ├── [0x572a99 bot.go:126 0x572dc0 bot.go:134]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).canSummonUnit.func1
│       │   └── [0x572b29 bot.go:127 0x572cc0 bot.go:145]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).summonUnit.func2
│       ├── [0x572a34 bot.go:124 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/joeycumines/go-behaviortree.Sequence
│       │   ├── [0x572a99 bot.go:126 0x572dc0 bot.go:134]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).canSummonUnit.func1
│       │   └── [0x572b29 bot.go:127 0x572cc0 bot.go:145]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).summonUnit.func2
│       ├── [0x572a34 bot.go:124 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/joeycumines/go-behaviortree.Sequence
│       │   ├── [0x572a99 bot.go:126 0x572dc0 bot.go:134]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).canSummonUnit.func1
│       │   └── [0x572b29 bot.go:127 0x572cc0 bot.go:145]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).summonUnit.func2
│       └── [0x572a34 bot.go:124 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/joeycumines/go-behaviortree.Sequence
│           ├── [0x572a99 bot.go:126 0x572dc0 bot.go:134]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).canSummonUnit.func1
│           └── [0x572b29 bot.go:127 0x572cc0 bot.go:145]  github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits | github.com/xescugc/maze-wars/server/bot.(*Bot).summonUnits.(*Bot).summonUnit.func2
└── [0x571c2f bot.go:69 0x571f20 shuffle.go:31 ]  github.com/xescugc/maze-wars/server/bot.(*Bot).Node | github.com/xescugc/maze-wars/server/bot.(*Bot).Node.Shuffle.func3
    ├── [0x571b9b bot.go:72 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).Node | github.com/joeycumines/go-behaviortree.Sequence
    │   ├── [0x571b33 bot.go:74 0x571dc0 bot.go:156]  github.com/xescugc/maze-wars/server/bot.(*Bot).Node | github.com/xescugc/maze-wars/server/bot.(*Bot).Node.(*Bot).findTowerToUpdate.func4
    │   └── [0x571b8b bot.go:75 0x571d20 bot.go:170]  github.com/xescugc/maze-wars/server/bot.(*Bot).Node | github.com/xescugc/maze-wars/server/bot.(*Bot).Node.(*Bot).updateTower.func5
    └── [0x571c17 bot.go:78 0x4cc100 selector.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).Node | github.com/joeycumines/go-behaviortree.Selector
        ├── [0x572f14 bot.go:180 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).placeTowers | github.com/joeycumines/go-behaviortree.Sequence
        │   ├── [0x572f79 bot.go:182 0x573280 bot.go:190]  github.com/xescugc/maze-wars/server/bot.(*Bot).placeTowers | github.com/xescugc/maze-wars/server/bot.(*Bot).placeTowers.(*Bot).canPlaceTower.func1
        │   └── [0x573009 bot.go:183 0x5731a0 bot.go:201]  github.com/xescugc/maze-wars/server/bot.(*Bot).placeTowers | github.com/xescugc/maze-wars/server/bot.(*Bot).placeTowers.(*Bot).placeTower.func2
        └── [0x572f14 bot.go:180 0x4cc1c0 sequence.go:21]  github.com/xescugc/maze-wars/server/bot.(*Bot).placeTowers | github.com/joeycumines/go-behaviortree.Sequence
            ├── [0x572f79 bot.go:182 0x573280 bot.go:190]  github.com/xescugc/maze-wars/server/bot.(*Bot).placeTowers | github.com/xescugc/maze-wars/server/bot.(*Bot).placeTowers.(*Bot).canPlaceTower.func1
            └── [0x573009 bot.go:183 0x5731a0 bot.go:201]  github.com/xescugc/maze-wars/server/bot.(*Bot).placeTowers | github.com/xescugc/maze-wars/server/bot.(*Bot).placeTowers.(*Bot).placeTower.func2

Having this names would allow to a more descriptive way to define the nodes and also the ones that are parametrized functions have a dedicated name instead of all of them have the same one.

For example this is the docs I have of what I want to do (and also related to why I opened the DOT issue hehe):
Behavior Tree drawio

@joeycumines
Copy link
Owner

Gotcha.

Regarding naming nodes, it doesn't seem unreasonable to want to name them, and it could be a purely optional feature.

@xescugc what do you think of something like:

  • Either a new (unexported) value key, e.g. type vkName struct{} (like the existing vkFrame)
  • func (Node) Name() string
  • Existing (default) tree printer attaching the name if available
  • Something like func (Node) WithName(name string) Node, and possibly also func NewNamed(name string, tick Tick, children ...Node) Node (the former because it is reasonable to want to name nodes not created with New or NewNode, and the latter because that way the name is at the start of the node definition)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants