Hi, welcome to this new video. We are going to continue working on our ecosystem simulation project. This is the third video, so by all means start in the first two. And we're going to start talking about how we can drive the behavior of this agent or this entity, where we're calling the herbivore. And we have looked at several kind of styles of behaviors. In the case of the particle, the forces in the environment would actually dictate the behavior of the particle. We've looked also at state machines in the past as systems that we can actually transition between a handful of states. So the concept of a state machine is very powerful, and we could certainly write an airbar using a state machine. But I thought it would be interesting to expand upon the different data structures and the different kind of behavioral algorithms that we can use right? So we're going to introduce the notion of a decision tree in this project. So let's discuss what is a decision tree, right? A decision tree would be a flowchart-like structure that has certain hierarchy, right? And we're going to construct a new class called the node that is going to have two types of representation, one is going to be a condition, right, that might lead towards a yes node and a no node, right? And the terminal nodes, which we're going to call the leaves are going to be action, they're going to execute certain code, right? So, the condition nodes might only be like a gate between moving down the flowchart, but until we eventually get to a point where we can actually execute an action,, right? All of them are going to be part of a classical node, but we're going to be calling the topmost node, the root node, and we're going to have maybe that being the node evaluated. And as we move through the tree structure and answering these questions, yes or no, we're going to be moving down towards general nodes, which are condition nodes, and finally the leaves which are the terminal nodes of our tree, right? So how would we implement that in the herbivore, right? So let's think of, this is actually what we're going to write, it's a very simple tree-like, but it's very expandable, it's something that you can really scale to be a much more complex brain, right? If you think of it, what we're modeling at this point is the brain of the herbivore, right? So the herbivore will start asking, am I awake? And if no, meaning it's sleeping, it's going to continue resting for executing the resting action, which potentially will reach to a point where it wakes up, right? But if it's in fact awake, yes then it's going to ask another question, am I hungry, right? And if the answer is yes, the behavior is going to be seeking food, right? If the answer is no, we're going to move to a random or wander around, right? So we're going to be structuring these behaviors within this decision tree. And as you can see, we're going to transition between what is the current note being evaluated from awake. If I'm awake, yes, move down to hungry, and finally to like maybe I'm not hungry, and then I'll start wandering around, right? So in this video we're going to construct or write the kind of foundation of the decision tree. And later we're going to be spending a bit of time writing the wander function, the seek food function, each one of the kind of actions specifically. So we're going to break it down gradually, right? So let's jump into processing and see how we can actually write this node class and implement this structure that we're drawing here. So I'm continuing with the code that we left from last video, so we have this kind of ongrowing population of herbivores. One of the things that you might remember, we didn't write the move function, we will come back to this in a minute. But basically what we want to do is start kind of think about this herbivore's brain, right? So let's just create a new tab and we're going to call this node, and this node it's not going to be a class that we can see, right? Until now we actually [INAUDIBLE] spoiled that most of the classes that we create are kind of very visible classes, right? But right now let's think that we're going to be doing a class that represents some internal way of thinking for this herbivore, right? So we're going to call it the class node, and let's define it with a constructor. And here because I want it to be quite flexible, a node could be a condition or action, I'm not going to require that. The construction is going to have a lot of flexibility, right? We're going to give some default values to elements, so let's do something like a name, which is going to be optional, and it's going to be optional because I'm going to be giving the default of none, right? So if I don't provide a name, that name, it's already kind of having some information there that's something that we can do to actually make constructors very flexible in Python. So action it's going to be known as well, Yes node That's also going to be known. And I'll explain to you in a minute what we're doing here, right? So no node, it's going to be null, so all of them are going to be empty. A little bit the screen here so There we go. If you feel like this is actually too long and you want to just break it apart, you can do this. And it might be better for us to do so like this here as well. We don't need an indentation here. It's just to make visible all the different things that the constructor is trying to do. So let's just copy-paste some of these variables now. So self.name equals name and self.action equals action. Self.yes node equals yes node. So I'm going to finish writing this and we can spend a bit of time understanding what really is going on here right? So I want to have enough flexibility to debug to visualize the name of the node in which I'm currently at. So the name is actually completely unnecessary. But it's going to be useful as we're flowing through the nodes and we can give nodes a name and see like show me which node you're currently in and it's going to tell us You're in node one, or you're in the root node or you're in the leaf one right? So That might be useful, come handy to understand what is the current node being active. So in each one of these cases, this is basically the data that we actually need within each node. And we're going to be using that in the herbivore to construct the decision tree, right? So the action, we're going to give it a string, a name, something like wander, right, seek food, right, or rest, right? And this is going to be a string variable. The condition node here is going to be, am I hungry? It's a question, right? It's going to be a type of condition, or am I awake, am I checking for, right? This condition will take us into two nodes, right? So as much as this is a node object, it will have reference to other two nodes, the yes no and the not no, sorry, yes node and the no node, right? So that's what is actually creating the tree, is that some nodes will have references to other nodes, right? So these will all come together as we start putting the tree, we start writing the tree. So finally, the condition variable might be that the herbivore might have already a variable such as is hungry, right? And we're going to create variables specifically to be able to see how the node controls a condition such as hunger, hunger level and things like that sort, right? So this is going to be our node. Let's just try to build a decision tree with this in mind, right? So the first thing I like to do in the herbivore is just create some variables, right? So I'm going to create a self.isAwake, which is going to be a Boolean, and it's going to be True. So it's going to start awake, right? self.isHungry, And that's going to be false, right? So, great, we have two variables that we are considering part of our conditions. And now between the run and the display, but it could be anywhere, really, let's just first write the build_decision_tree system. So in order to kind of start building the decision tree, we're going to need to import. We're going to import the node class that we just created. And let's define a leaf node first, because we're going to start from the bottom up, as some of the further nodes might need to make references to some of these previous nodes, right? So we're going to say the leaf node 1, it's going to be a node. And here we need to provide the data. Let's just spell out what we're saying. We're going to say name = l1, so it's going to refer to the leaf 1, and it's going to have an action = wonder, Right? And that's it, we've created a node in our decision tree, right? We're manually constructing this decision tree. Do we need to provide more information? No, because in the constructor, these are not mandatory fields because they actually have a default value. We are assuming that all of them are complete with the non-variable, right? So when the information that we're actually going to be giving is only the name, which again is optional, but only in this case, the action node. So a leaf node in this case is going to be an action node. So let's just copy paste this to do three nodes. So we're going to call leaf2 and leaf3, and then leaf2 and leaf3 are going to be, in this case, Seek food. And in this case, Rest, right? So these are the three actions that we are considering. Let's create a node, 1, which is going to be name n1, just to remember that, some kind of short name that we can remember. And this is going to be a condition node. So it's going to be a condition, and this is a string, is hungry. And some of these might be unnecessary, but we're going to just try to make it in such a way that it becomes a little bit more didactic, hopefully. So if the condition is hungry, yesNode. So the yesNode will be seek food, right, so leaf2. So let's say that leaf2 is going to be the condition, right? If I'm hungry, I'm going to go to the seek food, which is an action, right? And if noNode, I'm going to say if I'm not hungry, I'm going to go to l1, or leaf1, which is going to be the action of wandering, right? And finally, we're going to say that the condition_variable, and again, sorry, I'm going to have to break it down into lines to be more. And I don't know if this helps, but I don't want to be scrolling out of screen. So the variable that we're going to be adding here is the isHungry variable. So self.isHungry, right? Because that's a variable that we have now internally to the herbivore to evaluate this condition, right? That's the node 1, and we finally have the root, right? So here we revise the diagram that we drew about this tree structure. It has a root, it has a node1 that branches, the root kind of goes into node1 or directly into the action rest, right? Because the root is also a condition node, so let's write it. So node with the name equals to root, and the condition, Is Awake. There we go, is Awake, yesNode. So if I'm awake, I'm going to go into the node1, right? If I'm not awake, I'm going to continue performing the action of resting. So noNode will be the leaf3, right? noNode will be leaf3. So let's just break it down as we've done with the previous one as well. And there's a condition variable here, too. So if we look at what we wrote here, the condition variable isAwake, The variable that we're going to be using to kind of evaluate this condition, right? So at this point we could return root, right? So this function, whenever we execute this function, let's just double check that we're not running into errors, right? So I'm going to, let's call it once, we could say up here, self.current_node, self.build_decision_tree. Oops, So we're going to create a variable called the current_node. If you remember, there's one node being active and we're going to construct the whole tree and start at the top. The root node is going to be the node that has all these kind of branching paths, that this one takes us to the node 1 and this one will take us to leaf1 and 2, right, but depending on the internal state of the herbivore, right? So like at this point we're not executing any actions, so we shouldn't see any changes, but we really want to check if the code is actually creating any errors. So we're not running into errors, again, we're not seeing any motion. This is not the entire story, the decision tree, now that it's been constructed, right, it needs the second part of it, which is running based on the current node, right? We're going to write a function called let's update_current_node, right? And here's the part where we would say, are you a condition or are you an action, right? This is kind of the question. If you're a condition, evaluate if your current state, it's true or false, your variable. And based on that dictate if you move to the next node, right? And if you're an action, we're going to print a statement. It's like the animal is currently, or the herbivore is currently performing a wonder state or seeking, depending on the variable. So let's just write it as we go. So we're going to start with an if statement. Here we're going to say if the self.current_node right? So because we want to be evaluating the current_node, right? Make sure that we spell it correctly if the current_node has an internal variable, so conditional variable, condition variable, right? Notice that not all nodes have a conditional variable, right? Only the conditional nodes These two have a conditional variable. The other ones have a none, right? So we're going to say if it's not none, right, knowing that that defines a node as a condition node having a condition variable. Sorry, I put two dots here, we will do something, right? And we could say else or elif here, If the current_node.action is not none, something else happens. So here, this is an action node and this is a condition node. Right, simple enough at this point, we could say a print line. We could print the action. We could just say, Print that action. The action, as we can see, it's a string, so it would be Wonder, Seek Food, right? So that's good enough, but in their condition the condition is a little bit more complex because it needs to be able to kind of navigate towards the next node. It's kind of like sequentially moves to the next node and the next node. So let's just say that if that current variable that we're evaluating, right, we're first asking, do you have that conditional variable, right? And if you have that, let's evaluate that. Is that true or false, right? If it's true, the self.current_node, let's just copy paste that again. If it's true, the current_node will become the yesnode of that node. Right, so we know that that node has a yesnode, so that's if it's true, else, We're going to go down the ladder towards the nonode, right? So that's it, basically it's a simple kind of like flowchart, right? We're saying if you have a condition means that your condition node, is that condition true? Yes, well, then you are the next current_node. So the next time around we're going to evaluate the next node, and it's going to keep on doing this kind of branching path structure until it reaches an action node. And when it reaches an action node, it will perform the action, right? We're going to discuss how actions can potentially trigger a return to a previous node or a return to the root, depending on how we want to implement the behavior, right? So let's see, just to conclude, it's already getting a bit long. So let's just add this function to our, To our run. So, meaning that we are executing this function because we basically have a decision tree. Notice that the decision tree we build it at the beginning once, and now with the current node, it's going to be used in the update current, right? We're having an error here. I think this is just the spelling here is not instead of int not. Let's see, right, so you see we are getting now here in the console the print of Wander, right? Why are we getting Wander? Because we are saying awake state true Is Hungry false, right? I think that if we would put this to true as a default state for the herbivores, we would get the behavior Seek Food. So the leaf which we end up with based on these current variables navigating through the chart as we have laid it out, if you are hungry, you're going to end up in a seek food state. If you are not, you're going to be wandering, right? So those are the two main functions. We have a decision tree in place, so now we can actually start executing some of those actions and working on that behavior. So I'll see you in the next video.