Coding Challenge 51.1: A* Pathfinding Algorithm - Part 1

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Hello welcome to a coding training coding challenge. What does the coding training bring us today? Forget about that. So what I'm going to do is this, it's something called A Star. Is a pathfinding algorithm. I'm actually recording this part of the video that you're watching after I did the challenge, was a bit of a mess. I have to admit, you what you're about to watch is quite a mess. It took me 2 and 1/2 hours to sort this out. I had a lot of bugs. But what are you going to watch is a little bit shorter, some of it's edited out. If you want to watch the full archive of the live stream, you can find a link to that in this video's description. This is the final result. What I'm using is an algorithm to find the optimal path from the top left corner of the screen, to the bottom right corner of the screen going around some obstacles. And you can see here this is that path. And there's a lot of pieces to this. The video is going to be in two parts. So the first part of the video, the only thing that I'm going to do is look at how to make the optimal path from the top left to the bottom right with no obstacles, and not being allowed to go diagonally. And then that video will finish. And the second part of the video will show you how to add diagonals, and how to add obstacles. And so enjoy, watch, if you don't see the second half published as part of my feed go check this video's description to be a link to it. So enjoy all aboard the code train, and coding train, or whatever it is, enjoy this A star challenge. So let's dive in and make the A star algorithm, or an implementation of it in JavaScript. So first of all, what is the A star algorithm? The A star algorithm is a search algorithm, meaning there's some space of possibilities and you want to search through that space for a particular needle in a Haystack, so to speak. Solution and afterwards solution out of all possible solutions. And typically speaking, there are a lot of search algorithms to be able to come back to others A star is typically applied to a pathfinding kind of search problem. So let's talk about what I mean by pathfinding. So here's one scenario, incidentally a scenario that A star can actually be applied to is trains, like how to make an optimal path of freight trains between a whole bunch of cities to get stuff from one city to another. So let's sort of imagine driving of cars in cities. So if you have a bunch of cities, and they're connected by roads in some arbitrary way, not everything is connected to everything. And you're starting at city A, and you want to get to city B, what is the optimal path to get there that's the shortest? So we could eyeball this and well actually there's only really one path, but I'll connect things a bit more, add some more cities. So you could see there's more possibilities here like this would be, if I were to start here and go like this, I'm going to get all the way to city B, but I can see that this would be shorter. So you could imagine an algorithm for just trying out every possibility. You could use recursion because recursion is this kind of like self-referential function, because you're at one city and you try all the roads out of that city and you get to other cities, and for those cities you try all the roads out of those cities, and for those cities you try all the roads out of those cities, and eventually some of those paths will make it to the end. And then you could keep track of how long it took you, and which one was the fastest. You can kind of do a search every single possibility. What I just described to you is essentially what's known as Dijkstra's algorithm. Dijkstra's is an algorithm that looks at every possible path, and tries to find the optimal one. It works and it will find you the optimal one, but it's not super efficient. So A star because it takes a long time. Like this didn't take that long, but if they're like thousands of nodes it takes a really long time. The A star algorithm is very similar to Dijkstra's algorithm but it uses a concept known as heuristics. I have no idea if I'm going to be able to spell this, heuristics. Heuristics is like a really fancy word, how you would use it you sound like you know what you're doing. But it's really just a word for describing- I would say one way to think about as an educated guess. So instead of having to check every single possible route, what if we could just sort of make a guess. This route is probably going to be the best, I kind of know if I go backwards, it's going to take me longer than if I go forwards. So I can skip checking the possibility that goes backwards. So this kind of heuristic. This kind of educated guessing allows you to skip checking a lot of the possibilities, and makes the algorithm much faster. And the algorithm is typically written with a formula. This formula is actually quite simple although any time you write a formula it starts to be like, Oh my God, is this really what we're doing today. But f of n equals g of n plus h to n. So I figure what all these things stand for the h stands for heuristic, the g, I don't know what it stands for maybe we'll look on the Wikipedia page and see, but so f of n it's like a cost function. Cost being like for each one of these nodes, how much does that node cost? In terms of our time in getting from point A to point B, the path we're trying to find. So if it takes, the g is the known amount. Like this distance for this node is g. If I then went to this node next, the g for this node is this distance plus that distance. That's the g. So the g is the like actual I would say cost, the time, the distance, from beginning to end. Now the h is how long does it take to-- g being like how long did it take me to get from the beginning, h is like how long is it going to take me to get to the end. So if I knew what g and h were not as a heuristic but as an actual value for every single node, I could see very quickly what's the fastest path is. The idea is, I'm traversing this tree, this node, this path, and I get to here, I don't actually know. I know how long it took me to get here, I don't know how long it's going to take me to get to the end. But I could make an educated guess, because I know where the end is. I know the end is over here, I don't know how to get there, but I know where it is. And what's my educated guess? How about just the actual distance between these two points. If there were no roads I could just go up in my helicopter, coding helicopter. That was really the same ring to it. That the coding training has a ring to it exactly, but anyway if I could go up in my helicopter just fly straight there, it would take this amount of time. And an important thing about your heuristic, your educated guess, in A star algorithm is you don't want it to ever overestimate the amount of time. If it underestimates it, you're OK and how the algorithm is going to work. If you overestimate it, you could have a problem in not getting the optimal answer at the end. And in this case, we can see that there's no way to overestimate if I'm using the actual distance. Because no matter what, even if there's a road from here to there, that's the actual distance. If there's a lot of other roads, there's a lot of other roads. So that's the basic idea. For every single one of these nodes, I want to figure out, I know how long it took me to get there, and then I want to guess how long it's going to take me to the end. So and the way this is going to work is if I'm here, I'm going to look at all of the possibilities, so I could go here, I could go here, or I could go here. And what I'm going to do is I'm going to look at what the value of how long it took me to get there, and the guess of how long it takes to the end of each one of these. And I can pretty much see that this one is going to win. So I'm going to pick the total cost of how long it took me to get there plus my guess of how much more I have to go, that's the way I'm going to go. And you'll see it's still going to leave the others as an option, it might have to back up and check some of those other ones later, but that will actually work. So let's look at actually trying to implement this with some code right now. So I have a Wikipedia page open to the A Star Search algorithm. And if I scroll down something that's going to be helpful to us is this pseudocode. So there's a lot of tutorials you can probably find, examples of the A star algorithm. I'm going to make my own. I'm going to kind of use this as something to refer back to as we start to program as a reminder, but I'll try to explain things as I go as well. The thing that I need to talk about, which I came over here unnecessarily is that the system that I'm to build, I am not going to build this node space type of map, it's going to be a little bit different. Let's say this is my Canvas. And let's say what I'm going to do is think about there are cities in a grid. The Canvas is a big grid of cities like this. And every single city is connected to every city around it. Like this, I won't keep drawing all those lines. So if I'm trying to get to here, I might figure out the optimal path you could imagine as something like this or whatever. And then at some point what am going to do is I'm going to add obstacles, and see how the A star algorithm can solve going around those obstacles. So this is a generic way of finding a path through any given pixel space, because pixel spaces are just grids. And we could do things like then add a sort of steering agent to follow the path, or that sort of thing. Or we could kind of create a non-traditional grid of network map like this. But I think it's going to be a classic implementation and demonstration is using a system like this. And this is a full grid I just didn't draw out all the pieces. So that's what I'm going to do. And we'll see how that goes. So coming back, the first thing that I want to do is I want to have a two dimensional array that can store information for every spot in the grid. So let me go to my code, and what I'm going to do is I'm going to create a variable called grid. And I'm going to use some Java syntax and you know what I'm going to do too, is I'm going to say I want to have some variables that say, let's start small, there's going to be five columns and five rows. So the grid is 5 by 5. Two dimensional arrays are sort of goofy in JavaScript. What I ultimately want to do is, I want to say things like grid 0, grid index 0, index 3, is like the first column and the fourth row. Counting starting from 0, zero is number one, zero, one, two, three is the fourth one. So I want to be able to refer to any spot in the grid by its column and row position. But the first thing I would do is just make the an array itself with a certain number of columns. So the array, is an array of certain number of columns. Each column has an array for all those spots that are in the rows. So what I will do here is I would say for var i equals 0. i Is less than columns i plus plus, and I would say grid index i is a new array, rows. So this is really making a 2D array, and if I then say console dot log grid, we can see what I've got here. Where am I? This is where the code is running, you can see what is a two dimensional array and it's 5 by 5. But it's an array of five arrays of five things in it. So that's the grid. Now what do I want to put in the grid, this is a question. So now what I want to do is every spot-- now here's the thing, remember this algorithm that I'm working on, the idea is that every single spot has a cost associated with it. How long did it take to get there. And what's its guess for how long it's going to take to the end. So I want to store that information in each one of these spots. I want an object that can know what's my. I'm a node, I'm a cell, I'm in this grid, what's my cost? So coming back over here I'm going to do another loop that's going to go through every column and every row. And I'm going to kind of do this in an object oriented way, I should say that a bunch of things I'm going to do in this video, I'm doing for ease, readability, and not necessarily for optimal efficiency. I'm definitely going to make the most efficient version of this algorithm, and I'll talk about some things maybe later at the end that will see that being more efficient. But I want to make each spot in the grid an object. I'm going to call it a spot. Let's call it a spot. I don't know what a good name for this would be. So what I need is like a constructor function to create an object. So every object will have a f value, a g value, and an h value. So those are the things that I'm going to count spot. Every spot I need to calculate the g, the h, and the f value. You need to know for each cell in the grid, what are those values? There are some other things I'm going to want to know. We're going to have to add a bunch of things actually, let's add things as we need them. So that I've done that. So now I have a grid of spots, and now what I need to do now-- here's the thing, the A star algorithm by definition is a loop. It's trying a lot of possibilities until it gets to the solution and then it's done. So what I want to do is have my example demonstrate and animate the process. So I'm going to do something a little bit different than probably what's found in that Wikipedia pseudocode. I forgot to mention I'm using a JavaScript framework called P5. And as P5 ask you to write a set up function, which is kind of the beginning and I make a Canvas, and draw is a function that loops, it's an animation loop. What you do like normally with like set request animation frame or something like that with native JavaScript. So what I'm going to do is I'm going to use the draw loop as the loop. So if I go back to this particular page, we can see here where is that loop. There's this loop here while open set is not empty. So this is it doing this thing to solve the problem. So here's the thing, what's going on. Open set, close set. So this is a concept that's part of the algorithm that's quite important. We have this idea of an open set, which is an array, or a map, or a list, or whatever the data structure is that you're using, I'm going to just use an array. And we have this idea of closed set. So the idea is that closed set stores a list of all the nodes that have finished being evaluated. So this is everything that's done that you don't need to ever revisit. That's going to be closed set. Open set are nodes that still need to be evaluated. We need to consider and evaluate them they're not closed yet. The algorithm is finished when open set is empty. There's nothing left to be evaluated. Actually that's not entirely right, so the algorithm is finished, there's two ways the algorithm can finish. One is I'm going to be searching through nodes. And if I ever arrive at the end I'm finished because I arrived at the end, I found the optimal path all the way there. That's the way this algorithm is working. However, if I have nothing left in open set to review, nothing left to look at, and I haven't arrived at the end I also have to stop. That means that means there's no solution. So it's possible we could build a maze of obstacles with no way to get to the actual end. And so if there's nothing left to evaluate you haven't got the end it's also. So these closed set starts as empty, open set starts with just one node. At the beginning the open set has just the starting node. So let's go into my code again, and let's create and make some of these global variables just so we can kind of look at them in the console if we need to or evaluate them, closed set. So I'm going to create two arrays, open set and closed set. And what I'm going to do right here in Setup, is I'm going to say I'm going to make a start and an end. And so a start is going to be the top left. THE start node is grid at 0, 0. And the end node is grid at columns minus 1 rows minus 1. So these could be picked randomly or through a variety of other ideas. But the idea is that I want to start here, and I want to get to there. That's I want to find the path from the top left to the bottom right of the window. So then what do I need to do? I need to say open set, push, start. So we're starting with open set. I'm going to look, that's the thing I'm going to iterate through and check every possibility there. And so let's look at the algorithm. We're going to talk about came from in a second, g score we're going to talk about that, f score these are a bunch of things. But here's really where I need to start. I need to say while open set is not empty, as long as there are spots to be evaluated, let me start evaluating those spots. So the thing is I don't need a while loop remember, I'm going to use the draw loop. So the draw is already looping I'm just going to say, if open set is not empty-- the way I'll say that is if open set dot length is greater than zero, great, we can keep going, otherwise no solution. I say no solution. There's nothing left and haven't gone there's no solution. Now what I also want to do just because I want to debug this as we're going, is I want to write something where I draw all the spots in the grid, and maybe I debug in some ways. This will be good for debugging. So I'm going to write another double loop. This is a long problem by the way, this is going to be a kind of a long video, you're still watching? This could be like multiple parts but maybe I should do multiple part videos. I don't know. Grid i, j, and I'm going to do show. So I'm going to write a function. Each spot is going to be a function to show itself. And what I'm going to do is, I'm going to say, this dot show equals function. So how can the spot show itself it doesn't know where it is. So I'm going to give each spot an x, in each spot a y. So when I create each spot I'm going to pass in the i and j. So each spot knows where it is. This is going to become important later actually. And then in the show function, I can draw a rectangle at this dot x, this dot y but here's the thing. I have a sort of scaling issue, which is that if my window is 400 by 400 but I only have 10 by 10 columns and rows and you figure out how wide each cell is. So I can do that with some global variables. I'll just make a variable w and h. And what I'll do is say that w equals the width divided by columns, and the number of columns or if it's 400 and I have 10 columns, each spot is 40 pixels wide. H is height divided by rows. This is pretty good. And so then I want to just multiply by that w, multiplied by that h, and then have it with in height. I probably need one variable to be honest because I'm making everything a square but whatever. Let's say fill 255, stroke zero, and let's run this again, we can see there's my grid. Maybe what I should do is I'm getting ridiculous here as I should make everything like one pixel left, so I can see the full grid but whatever. So you get the idea. There's the grid. Good call. Good job. So what I want to do is I also want to do some debugging, so what I'm going to do actually let's make this show function get a color, and forget about the stroke. I could keep the stroke but no stroke. So what I'm going to do is whenever I call the show function, I'm going to use a color. So in my debugging I could say I want everything to be white. But I also want to look at the closed list. And I also want to look at the closed lists and open set, closed set, open set, whatever, I can't remember what these are called. I'm going to say closed set index i dot show and I'll make these green, are closed will be red I guess. Somebody will come up with more beautiful and interesting colors and nicer design choices, and open set will be green for open. And so now if we do this, no stoke is not defined. No hey no stoke man. That does not make any sense, no stoke. There we go. So we can see things are looking up here. Now I can see that's my open set, nothing is closed yet. So I want to have this because I want to be able to debug it as it's going. I need to draw stuff to see how the algorithm is working plus this is going to open a lot of interesting visual possibilities for you guys. So let's keep going. Now what do we do? We've got to start thinking about this algorithm. So let's go back to the Wikipedia pseudocode. The idea here is current should be the node in the open set that has the lowest f. So in other words, I want to evaluate, here I am, I want to evaluate all the open set possibilities and what has the lowest f. What's going to be the best thing getting me to the end. So how do we find what something f is, and which has the lowest f. So first thing I can do is I can say for var equals 0 i is less than open set dot length i plus plus. So I'm going to say of our lowest index equals 0. So I'm going to assume in the open set-- there has to be something in the open set, I'm going to assume that the lowest one is the first one. The winner, the record keeper, I could say winner, index, or whatever, I don't know what to call it, lowest index. It probably has a variable name in the pseudocode. So what I'm going to do now is I need to say if open set dot index i dot f is less than open set lowest index dot f. Lowest in this is such a long-- let's just call it winner. Winner dot f if it's less than then the winner is i. so we're looking to see if we have which one is the lowest. That's good that's an easy algorithm, loop through everything, find the one that's the winner. What do we do next? So first thing we have to say, if the winner is the end we're done. So let's add that in right here. If open set winner equals end, remember end is a variable that's holding on to that last spot, then console dot log DONE. So that's where we're done. So we start with the open set, we look at all the what all the things in the open set, what's best, if the one that's best is actually the end, then we're great, then we're done. Now the next thing is, whichever one we found current is the node in the open set having the lowest score. So you know what I should do? I should say var current equals and this is really winning index, I'm going to say open set winner. So if current-- and, boy, I have lots of typos here. Remember if you want to test it something is equals in JavaScript you use double equals. If you really want to test, if you really want to be sure use the triple equals. So if current is the end we're done. Otherwise what do I want to do? I want to add close set, push current, and then I want to say open set remove current. This is not any actual real code. So here is one of the tricky things. What I want to do is, there is a function in JavaScript to add a particular object to an array, that's push. There is no function in JavaScript is just arbitrarily say, hey, this thing if it's in the array remove it. So and this is where I'm going to not do things so optimally, but what I'm going to do is I need to find that object and remove it. And I'm just going to write a function, remove from array, open set current. So I'm going to write my own function, I'm going to stick it just at the top here. Function remove from array and function array element, and then what I need to do is I need to loop through the array, but I want to loop through it backwards to say i in a second. i is the length of the array, length of the array minus 1 will be starting at the end going all the way down to zero, and if the array index i equals elt, then array splice i, 1. So what is that? I just really quickly wrote a function that loops through the array, since if it has that element, splice being a function to delete a particular element add an index from the array, and only want to delete that one array. I'm sure somebody can tell me a better way to do it. But then it will work for right now. Maybe I'll come back and optimize that later. But why do I go through it backwards? The reason why I go through it backwards is if I'm going through the array forwards, and I delete something, then all the elements come back and I'm moving forwards I could skip an array, so skipping elements. So I want to go through it backwards. So we want to remove it. Remove from array, current from open set, current add to closed set. So now what do we do? If I go back to the algorithm, I need to figure out what should I add to the open set. What new nodes do I need to evaluate. And if we think about this, if I have just gone if I started here, if this is the grid. We try to fill out a little more of this. Just so it doesn't look so empty. If this is the grid, what I want to do I've just evaluated this. It's my best node coz I only had one choice. That just went into closed set. It came out of open set. Now I need to figure out what are some nodes I need to check next, it's anything that's neighboring that particular node. This one, this one, or this one, as long as they are nodes we've already checked or finished with before. So how do I get the neighbors? So I need some way-- I'm in wrong keyboard, I need some way of figuring out what are all of the neighbors of current. Guess what, I think a nice way of doing this. One way of doing this would be to add a function in the let's add an array. So let's have each spot keep track of its neighbors. And let's add a function called add neighbors. What I'm going to do is do things like, so add neighbors from a particular grid, just pass it in. And so what I'm going to do is I'm going to say this dot neighbors push, grid, this dot i plus 1. I'm going to do something silly just to make it a little more readable. Say var i equals this dot i, var j equals this dot j, I might need those out. I'm losing my train of thought here. But this is fine. i plus 1, j. So there's four neighbors. i plus 1, j, i minus 1 j, i, j plus 1, i j minus 1. So these are the four neighbors. I want to add those. I want each as they emerge, but there's an issue, what if i is on the edge. So you know there's so many nice and probably concise and strange and optimal ways of writing this, but I'm going to do something really simple which just says, as long as i is less than columns minus 1, then I can add that neighbor. As long as i is greater than 0, then it's OK to add this neighbor. And then as long as j is less than rows minus 1, very tedious the other way of doing this I can add that neighbor as long as j is greater than 0. And you know what, I called these x and y. So this is one of these things about programming. You got to keep track of your stuff, and I'm not doing a very good job of naming things in such a way that making it easy on myself. I was calling it i and j, and then I call it x and y. So maybe I'll change these to i and j, because it's not really the x and y, the x and the y is really the location this start i times. So I think this is going to be better. And then if j is greater than 0 at this particular neighbor. So here now I can add all the neighbors. So this is a function for any given spot to add neighbors that are in a particular grid. Where do I do that? Well you might think-- I kind of want to do it here. But I have a feeling this is going to cause a big problem. The reason why I can't do it here, is because this is where I'm making all the spots, and I can't add the neighbors while making the spots because I want to make the spot that's next to it yet. So this is very inefficient I'm sure I could come up with a different way of doing this. But I'm just going to add a completely other loop. Just to do this again. And say add neighbors. So I want to make all the spots, loop through everything and have everything at its own neighbors. So now just let's refresh this and see I don't have any errors. Oh I know what the error is. I wrote it in such a way that it to pass and because maybe you want to add neighbors from a different grid and some other life that you have. I could just use it as a global variable. Great now I'm going to look at grid, and we can see that each spot has an f, g, and h, j, and a list of neighbors, and the neighbors-- something's wrong here. Oh look at this, look at this comma in a totally nonsensical location. That's not right at all. A lot of you are screaming at the computer I'm sure. Hopefully you were just saying in a nice way like, excuse me I might like to point out your small but subtle error there. Why debugging is good. Let's look at this again. Let's look at this, and let's look at this spot, and let's look at its neighbors. It's got three neighbors. And where is it? It's like the location 0 comma 1. So let's go back to the Wikipedia page. For each neighbor of current. So we want to see what to do about all the neighbors we're going to add to the open set. But before we put them in the open set we need to evaluate them. So first let's say for each neighbor of current. So what I'm going to do is, I'm going to go here, and where are we in the algorithm, sorry right here. So I want to get all the neighbors current dot neighbors. I'm just going to put this in a variable so I don't have to say current dot neighbors all the time. Var neighbors equals current dot neighbors. I'm going to use a for loop to check every neighbor. For var i equals 0, i is less than neighbors dot length, i plus plus. So this is every neighbor and then I'm just going to say neighbor equals neighbors index i. So this is me checking every neighbor. Now what do I need to do for every single neighbor. Well the first thing I need to do is that neighbor, if I'm moving to that neighbor, the g should increase by 1. I started here, it makes sense that g would be 0 here. The amount of time it takes me to get to this spot at the beginning from the beginning is 0. So if these are the neighbors, actually these are the only two neighbors. I'm not doing diagonals. So I don't know why circle that I just realized I'll add diagonals later I can see what that does. But so I can either go here, or I could go here. And the g for each one of these should be 1. So another way of saying it should be 1, it's whatever it was previously connected to plus 1. Because if it takes me like 10 steps to get to here, and then I'm going here, then this would be 11 steps. So the first thing I want to do is I want to say neighbor dot g equals current dot g plus 1. Except I don't entirely want to do this. So let's go back and look at the algorithm. There's a couple of things I need to check. First of all, what if that neighbor is in the closed set? If the neighbor is in the closed set, then I don't want to evaluate it. Because by that definition it's something that's done already. I don't need to revisit it, I've already visited there through some other optimal path. So let's add that in. So what I need to do is first say, now how do I say if an object is part of an object. So this is, again, where I would want to do some type of efficient search. This is a search algorithm, but a search within a search to figure out if something is in a list. So I want to make that more efficient somehow here. Because I want to say, so as long as closed set does not include the neighbor, then I can change its g value. Although can I really? So let's look at the algorithm here. Sorry back here. So you'll notice in the algorithm it says the tentative gscore. The reason why I actually don't want to just automatically give it a new gscore, is I might have gotten to that if it's not in the closed set, it still could be something that's been evaluated already, and I might have gotten to it in a bit more of an efficient way. So I need to check some things. Because if it's in the open set already but it's a neighbor, I might have gotten to it with a lower gscore and I want to keep that lower gscore. So what we're going to do is say, I'm going to actually say var, I'm going to call it tempG. So I want to keep track of what the G would be, and you'll notice in the algorithm that says current plus distance between current and neighbor, so in a more generic version of this algorithm where the nodes might have different distances between each other. I would have to evaluate that. But the way that I've done this over here. But you can see this pretty well. Is that everything has a distance of one. So I'm going to add one, now. Now what I need to do is I need to figure out, is this something that's actually already in the open list. So does the open set include that neighbor. So I want to check is it something I've evaluated before, because if it's something I've evaluated before I want to figure out is this a better g. Wherever kind of like way that I'm checking right now, have I gotten there more efficiently. So if tempG less than neighbor dot g. If that's the case, then I've got a better g. So now I can be that new g. I want to be the new g that's lower than my old g. Old g. So if we go back I get that right, so right if the tentative score is greater than gscore don't do anything. Just kind of doing it in the algorithms, the pseudocode describe it a little different way. But so then, if it's not in there, if it's not in the open list then great. Neighbor dot g should have that score, and open set, push that neighbor. So all of these things they're either already in the open set, and if they're already in the open set of things to be evaluated still, I should see if I've gotten there faster than maybe I did previously. And if they're not in the open set then, I've gotten there and add it to the open set. So once I've done all of this, and we've got to check to make sure I'm still within the right if statement. I'm still within this if statement of if it's a neighbor that's not in the closed set, as long as it's something-- and now we can see is here's what I need to do. What I need to do is figure out, what it's gscore and what's it's fscore, that's interesting. So what I'm going to do here is-- so I need to figure out what is fscore. It's time for the heuristic. Are you guys excited? You've watched I don't a really long video so far, and I'm only now arriving at the point where you need to calculate the heuristic. So I'm going to say neighbor dot h, the heuristic is the heuristic, I just want to use that word that sounds so fancy, between the neighbor and the end. So here's where I now need to make an educated guess. How long can I guess that it's going to take me to get to the end. So there are a lot of different kinds of heuristics. And they'll give you different results. But the heuristic I'm going to use just right now is just the raw Euclidean distance. I'm going to change it later, because you're going to see I will get slightly different results. But I'm just going to say, what's the raw of distance? So I know that that's always going to be less than what it actually is. But it's a good educated guess. So coming back here I need to write a function. And I'm zoomed in. I'm going to write a function at the top, function heuristic and between points A and B. So I want to say var d equals the distance and distance is a p5 function between a dot i a dot j, b dot i, b dot j. So that's just do what's known as Euclidean distance it uses the Pythagorean theorem, just like how long is the line between those two points. And then return D. So that's the heuristic. So if I come back now into my algorithm. The h-- I have a run this is why I don't recommend, usually I like to run the code a lot in between every direction on the errors but here we go. That neighbors h is the distance between A and the end. And then that neighbor's f, it's g plus it's h. So the whole point of this, is to know how long did it take for me to get there, what's my guess for how long it's going to take to get to the end, add those two things together, and that's the sort of score of this particular node. And once I've done that what do I do? I need to add that to the open set, I don't see that anywhere in here. I did that already. Open set add neighbor. I did add that here. So it's either already in there and I'm updating its values, or it's not in there and I'm updating it. So there's a little bit of something like this doesn't need to happen again, if I'm not updating the g because the heuristic is never going to change. But it's fine it'll work anyway. So now that that's good, I think we're good. Came from, so I'll get to that later. We're going to need that came from thing. So let's just run this see what happens. Well, it made it to the end, hold on OK. So let's make a bigger grid. Did that really work. Yeah oh that's just doesn't look right to me. OK well, I think there's a bug in my code, which I'm going to have to find definitely, but I can't be 100% sure I sort of missed a key part of this, which I really would like to add. So if I go back to the algorithm here, you'll notice this now this pseudo code is written with a particular kind of style. I don't want to get too far in to. But you can see that, these are like these maps. Like this is a map of all the g scores for every spot, this is all the f scores for every spot. I've done it differently, in a different way I have this two dimensional array of all these spot objects each of those have properties. But one of the properties that each spot needs, is to keep track of where it came from. Like as I'm going through this, what was the node that was previous to it. And you think of that as like a parent node, or the previous node because eventually once I get to the end and it's done, I want to be able to trace back and find what that optimal path was. So what I need to do here is add-- where am I? What I need to do in the code is I need to say, neighbor dot previous equals current. So I want to just use previous, I could say came from, or parent but may say previous. Where this neighbor came from is current. And so you know what I might do up here just to be clear about this, as I say this previous equals undefined. That's unnecessary because by definition will be undefined, or null, or whatever, but I just want to have that in there. So what I'm going to do then is when it finishes, this is where it finishes. Wherever the open sets, whenever the best the item in the open set, the spot in the open set, with the highest fscore, the gscore and the hscore combined, the lowest that is the best one. If that happens to be the end, then I'm done. So now what I need to do is I need to find the path, and the way that I'm going to do that, is I'm going to make this a global variable just again so I can kind of evaluate this stuff. I'm going to say the path. And so what I want to do when I find the path, where so much code it would be nice to organize this better. Is I'm going to say path is a new array, and I'm going to say var, I don't think I can mess anything up by messing with current, but I just say temp equals current, because at the end. And as long as temp has a previous, as long as there exists a previous what I should do and then I should also say path dot push temp, path dot push, temp dot previous, and then temp equals temp previous. So this is an algorithm I kind of do this in my head here a little bit. I think I need to explain this right. What I'm doing is I'm starting with an empty list, and I'm going to put the end in the list. And then the end is connected to the one before it, so that goes in the list. And that was connected the one before it, so that one goes in the list. So as long as there is a one before it, put the one before it and now the thing that I'm checking is the one that was before it. So this is a nice little algorithm to sort of backtrack over that path. And then what I should be able to do is when I'm done, I should be able to say if you know where I want to draw this. I'm going to do this down here. I'm scrolling around like a crazy person. I'm just going to initialize the path as an empty array up here. So I want to say, as long as I know I can say, I want to loop through the path, and I want to say path index i dot show with a color, 0, 0, 255. So again I'm not being far from about the colors. The closed set are red, the open set are green, and the path is blue. So let's see what happens here. Oh you know what, I should evaluate that path always. Why not? To see what the current path is. It's only going to do it when it gets to the end. It makes sense, that makes sense as the path. I think. I got to check, I feel like there's a bug in the code. So one thing as I'm just realizing is I can be done, and I can say like no loop to stop the looping. But there's no reason why I shouldn't just evaluate what the current path is like every frame. And I can actually just do that right here. So let's do that. Well, what am I getting. So I think things are actually working and maybe we're going to see some improvements if I add a few more things to the code, and also kind of add some obstacles. So let's just keep going with this. So what I'm going to do, a couple of things I want to do, first of all this really isn't a good heuristic. Where's my heuristic function, here it is. Using the Euclidean distance, which is actually just kind of measuring that distance, isn't really accurate when I can only make steps that aren't diagonal like this. So there's actually a distance that's called a Manhattan distance or taxicab distance, which is just measuring the difference in x and the difference in y. So let me put that heuristic in which I think will give us some results that look a bit more like what we might expect. So I'm going to put that into that's the absolute value of the difference between the i values of the x values, plus the absolute value of the difference between the j or the y values. So I can run this now, and we're going to see we can see it trying to solve this problem. The funny thing is, the reason why it's covering this whole space is because in a way like the top and the bottom are equivalent in distance so it ends up checking everywhere, but we'll be able to see here that if I were to just change the end spot for example to where do I set the end. The end spot to like the fourth row. Then actually what it's doing is it's optimally finding that path very quickly. It doesn't have to check every possibility because what are we doing, we're using the A star algorithm. So there's a couple of things that I want to add to this. Number one, let's add some obstacles. So obstacles are really going to help us understand how this is working. And number two, let's think about adding diagonals as well, and how that works if we add diagonals.
Info
Channel: The Coding Train
Views: 2,216,433
Rating: 4.9218731 out of 5
Keywords: JavaScript (Programming Language), live, programming, daniel shiffman, creative coding, p5.js, coding challenge, p5.js tutorial, p5js, javascript (programming language), tutorial, javascript, algorithms, coding, challenges, A*, A* algorithm, A* algorithm javascript, A* algorithm js, A* pathfinding javascript, A* search, A* pathfinding, Astar algorithm, A* search algorithm, coding train, the coding train, code challenge, Astar search, Astar pathfinding, A* coding challenge, pathfinding
Id: aKYlikFAV4k
Channel Id: undefined
Length: 48min 42sec (2922 seconds)
Published: Mon Jan 16 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.