In this course, you will learn how to build a
self driving car simulation. By making every component yourself with no libraries. This course
is a great way to learn about neural networks. You will also learn about modern JavaScript
techniques, Raju teaches this course. He has a PhD in computer science, and is one
of the more creative tech YouTubers out there. Hi, I'm Raju, and welcome to the self
driving car, no libraries JavaScript course. It's a course where I teach you how to make a
self driving car. The and will do it without using any kind of libraries so that you really
understand what's going on under the hood. Now, before you say anything, yeah, it won't be a
real car you drive outside. But making this kind of simulation is maybe even harder than doing
it for real. Let me explain. In this course, you'll learn how to implement simple physics
to move the car in the beginning will control the car ourselves using the keyboard to get
the field that we want. I'll also teach how to create the road and how to follow the car on
the road like a drone is filming it from above will then simulate some sensors on the car.
These sensors work by raycasting and detect if the car is too close to the side of the road or
to other cars in traffic. To implement these will use a formula for a segment intersection. Don't
worry, I'll explain the math in a nice visual way. You'll see we'll implement collision detection
using the same segment intersection formula. It's great, it works with any orientation and
can even support other shapes as well. Now all of these are things you don't have to worry
about if you're working with real hardware. The world gives you the best
physics better than any simulation. There are plenty of rows to drive on,
we don't need to create them ourselves. And nowadays, cars have a bunch of sensors that
tell if something is touching or around the car. Of course, if you want to build
all of these yourself, then yeah, making the simulation is easier. But what are you
Elon Musk? Anyway, the second half of the course is the really interesting part, you'll learn
how to create and visualize a neural network, an algorithm inspired by the biological neural
networks in our brains. I'll briefly explain how our brains work to the best of my ability and
teach you how to code something similar ourselves, then we'll optimize the brain by trial and error. Yeah, to keep things simple, we just try
randomly generating brains until we get something that works. We'll parallelize this step
and use hundreds of cars at once to save time. By the way, this is something you can't do with
real hardware unless you're really, really rich. And that's why car companies use simulations
to train their neural networks as well. I'll also teach you a basic genetic algorithm to
optimize faster. But don't expect anything too fancy. This entire system is complex, but I made
it so that each component is relatively simple in and of itself. I made this course keeping
in mind my high school days back in Romania. So the knowledge you get from school should
be enough depending on the curriculum. We'll work with JavaScript because it's so easy to get
started. You just need an editor and a browser, which you probably have already. I'll teach you
many modern JavaScript techniques. So this course is perfect if you want to become a better
coder, as well as a machine learning expert. Now, JavaScript is not the best choice
when doing artificial intelligence. But since we're not using any libraries, it
doesn't really matter. If you want to learn these in a more useful language, like Python, then come
study at Karelia University of Applied Sciences there, I teach the same thing in Python as well.
And we also study machine learning libraries and even use hardware components. But now start your
engines because the course is about to start. I'll first teach you how to implement the car
driving mechanics. You learn to make a car that moves like this. It's not really that
hard. You'll see. I'll refresh your memory with some basic physics and trigonometry.
And you'll be able to code this in no time. We begin with this simple project setup. We have
three files right here, index, HTML main, Jas, and style css, and they're all empty for now. And
I'm using Visual Studio code to write this. And on the right, I have Google Chrome, and you
need to have the developer tools open there. It's really important. So if you plan to follow
along, have a similar setup. You can use another editor if you want to If I recommend this Visual
Studio Code nowadays, let's begin with basic HTML. In the head, I'm going to write a title for
this. And it's going to be self driving car, no libraries. Really, everything here is going to
be just written by us, linking now the stylesheet. And let me close the head and in the
body, I'm going to define a canvas, we are mostly going to use this canvas in this
project here, I'm also linking the JavaScript file like this. And we are done with the HTML for
the moment, at least, refresh the page. And not much happens rarely. It says here self
driving car, no libraries in the in the tab of the browser, but nothing appears in the page
itself. Apparently, if you open this elements, you will see that the canvas is actually
there. It's just transparent. And the body actually has this orange small margin there, I
want to get rid of that margin and also make this canvas visible somehow. So let's go to this style
css next, and write some styles, I will remove the margin for the body. And let's make the body
have a dark gray background overflow to hidden. And I'm going to use text align to center for now.
Overflow is important. Otherwise, we might get the scroll bar coming if the canvas is too big.
And I don't want that. And I want everything to align to center in the middle of the page. Now my
canvas should have a different colored background, maybe we set it to light gray like this. And save,
refresh. And now you can actually see the canvas without needing to inspect these elements right
here. I'm gonna move back to the console, it's probably the most important thing in the developer
tools, I want my canvas to stretch the full screen vertically. So I'm gonna go in main.js,
right here. And I'm going to get a reference to our canvas with ID my canvas we defined
previously. And I'm going to set the height of this to be the full window inner height like this.
And the width is going to be just 200 pixels, it will be like a road going upwards, we will need
the space on the side to draw the neural network later. Now if I save this and refresh, you can
see it appearing there exactly how we want it. Let's next pretend that we have a car, and we want
to draw it on the canvas. To draw on the canvas, we first need to get the drawing context, we will
just use the to the context in this whole project. So I'm going to have a reference to this canvas
context right here and the city X. And it's going to contain all the methods we need to draw the
things that we need to draw in this project. And if we would have a car, we would say
something like this, let's get the car at, for example, 100x 100 y, and the car
maybe has a 30, width, and 50 height. All of these are in pixels. And let's draw the
car using this context. But we don't have the car yet. This is the thing that we are going to
need to define next, the car object. And we are going to do this first by going inside the index
html here and preparing to import the new file. Car Jas like this. And now I'm going to add a new
file here. Car JS in here. I'm going to say class car. So we are defining our car class. And the
constructor for this class is going to have four different parameters. So we have here the
X and the Y and a width and a height. So these are properties of the car where we want it to be
in how big should the car be? And we are going to store these as attributes inside of the object
like this so that the car remembers where it is and how big it is. Now, remember, we also had
this draw being called. So that draw was a method. And it looks something like this, it gets context
as a parameter. And in here, we begin path. And we are going to draw a car as a simple rectangle.
For now, the rectangle is going to start at an x&y location. So I'm going to use this
dot x minus this dot width divided by two. So the x of the car is going to be the
center inside the car, and it's going to have parts in front behind, left and right, so on
the top bottom, left and right, the y value, I subtract half the height. So here, we also need
to specify what is the width and the height of this rectangle like this, and ask the context to
fill it like so. And if I'm going to save this and refresh the page, you can see the car there, or
at least the black rectangle. For now, the car is here at 100 100. So on computer screens, the
y axis actually goes downwards. And the 00 point relative to this canvas is actually right up
here. Now, this is really boring as such. So I'm going to teach you next how to move the car using
the keyboard arrow keys. So for now, let's just pretend that we have here some controls
like this controls is equal to new controls. And let me link a new file here in
index controls J S, and also create controls, J S. And we will do a similar thing as
previously. So we need a class for these controls. And the constructor, the controls object
will have four different attributes, one of it will know if it's going
forward, one of it for the left, one of it for the right, one for the reverse, like
this. And I set them to false initially, but this will change depending on what you press on the
keyboard. So I'm next going to define a method for adding so called keyboard listeners,
so that we have a piece of code that checks whenever we press a key or release a
key. So I'm going to call here, add keyboard listeners. And this is going to be a method inside
of the controls class, like so. The reason I put here this hashtag in front is because this is
a private method. You can't access this from outside that this controls class and you shouldn't
it's its own responsibility. So how does this add keyboard listeners look like? Well, we will add
a key down event to the document and set this to be equal to this arrow function. I will
explain arrow functions in just a second. But let's implement this first. So
depending on the key that was pressed, if this was the arrow left key, we are
going to set this left to true and break and do the same thing for the arrow right
key like this. The arrow up key for forward and the arrow down key for reverse and that's it. But we also need
to know when we release a key. So I'm going to actually copy this code
here like so. And this is going to be on key up. And all of these things
are going to be set to false. I want to debug if all of this code that we wrote
works. So I'm going to put here a console table. This, I'm going to output this entire object into
the console in a table format. When I press a key, and when I release a key, I save the file refresh.
And every time you press something like now I'm pressing the up key. Now I released the app key.
Now I'm pressing the Down key, I released the down key left. And right, you can see the values in
the console have changed. So this code seems to be working. Great. But why is it working? So let's
look a little bit up here. And this part of the code may be a little strange to you, if you're
not familiar with this arrow function notation. This is actually the same thing as writing
here, function of event like this. So we say that unki down, this is the function that you're
going to call. But if you write it like this, this here stops referring to the object here
to the controls object, it actually refers to this function right here. And if I undo and use
this arrow function notation, this continues to refer to this object right here. So it's a
difference in how these two type of functions work in JavaScript. And it's convenient to have
it in this way. Now, controls are triggering, but let's move the car with them. So I'm going
to go to car Jas. And we are going to write here an update method. This update method is going to
check if the controls say forward, let's just move the car upward. So that means y minus equals to
remember that y increases downwards on a computer. And if these controls are reverse, then
we add to y so that it goes downwards. Now this won't do anything just yet, we need
to go to main js here and start to animate. So I'm going to right here animate and define
an animate function like this. And inside here, I'm going to update the car. And then I'm going
to draw the car using this code we had previously like this and call request animation frame
with animate like so. Request Animation Frame calls the Animate method again and again
many times per second, it gives the illusion of movement that we want. Now we can save the
file, refresh the page. And when I press down, something happens. So the car updates and redraws
itself. But all the past positions of the car are still there. And if I press up now takes a while
but then it reaches again the top part and you start seeing it again. But it leaves this black
trail there. So we need to fix this. And actually, we need to fix something else as well. If I'm
going to resize this console here, you will see that this canvas here doesn't stretch to fill
the screen vertically anymore. This bothers me. But it's, it's great, because we can fix both
of these issues by moving this piece of code from here to here. When you resize the canvas in
this way, it means that it also becomes cleared. So if I refresh now and press the Down
key, the car is actually seemingly moving and it doesn't leave that trail anymore. Also,
if I resize this console here, it looks proper. You might have to click on the screen here before
you can press the top and down arrows. Sometimes these listeners don't activate them unless you
click inside the page. Okay, problem now is that this doesn't move like a car at all. We need to
work on this cars don't move so that you press on something And then immediately stop, I'm going
to go to the car constructor here and give the car a speed attribute and an acceleration value
like this. And here, instead of changing the y value on forward, we are going to say that
the speed will increase by the acceleration, like this. And I'm going to copy this here below.
But with minus, and here, I will say this dot y minus equals this dot speed, I save the file,
refresh the page, I press down. I press up, we, okay, we need to stop the car from going
too fast and implement some friction. So I'm going to go here and say, max
speed equals to something like three and the friction of, let's say, 0.05, like this.
And in the update method, I'm going to move here and say, if the speed is more than this max speed,
this speed becomes max speed, we are capping it the same if the speed is less than minus this max
speed. But in reverse, I want the car to be not as fast, I'm just gonna divide by two here. If it's
less than this, then I'm gonna cap it to be minus max speed divided by two, maybe you want to have
a max reverse speed or something declared on top, feel free to do it, I think it's overkill.
Also, there's no such thing as a negative speed, like what we have here, the negative sign is
just to indicate that the car is going backwards. Luckily, this is another course on physics,
otherwise, I might get in trouble for this. Next thing we do is check if the speed is
greater than zero, I will decrease it by the friction like this. And if the speed is less
than zero, then speed increases by the friction, like this. Now I save the file, refresh the
page, press it down, and the car is moving. I released and it's slowed down a bit, and then
it stopped. I'm pressing now up, and I release. You can see it's still moving a little bit before
it stops. And I think that this fields quite okay. But at the moment, the car is only going
forward and backward and the left and right, don't do anything. One small issue though,
let me see if I can get it to happen. Okay, if you release in just the right way,
you will see this, let me zoom in a little bit more. See, the car is actually always
moving, it's constantly moving, no matter if we press a key or not, actually, if we don't
press any key. And it's moving by by a very, very, very small value. And that's because
if the speed is not equal to zero, exactly, then this friction is going to bounce it
around, and eventually is going to move it forwards by a very, very small value. So
to fix this, we can actually right here. If this speed has absolute value that is less
than the friction just have it becomes zero like so. And this is going to work, no need to
test anymore. Let's implement the left and right controls next. And say as before, if we press
left, x gets minus two. And if we press WRITE, x gets plus two, notice how I implement the
basic thing first, and make it more advanced afterwards. I really want you to get the idea that
everything can be improved, but that you can make a complex system even with easy components. I
mean, by the end of this course you will have a self driving car no matter if it moves like
now, or if we implement better physics into it. Now If I refresh the zoom out a
bit. Now, if I press left and right, the car moves in a very, very funny,
non realistic way. But this is actually how cars move in some games, for example. So if
you want to make a self driving car for a game, then this might be enough for you. But I'm
gonna teach you how to do better than this. And, actually, I don't like this for one important
reason, we are breaking the laws of physics, we have here, the value for this maximum
speed. And we can reach that maximum speed by going forward like this. But if I
press right now, I'm actually going faster than the maximum speed, but diagonally,
because I'm going to maximum speed vertically and a little bit to the left in this case.
So this is bad for many reasons. Let me teach you how to fix it. We are gonna go up here and
define an angle, let's set it to zero. And here, instead of doing this, we are actually going to
modify the angle to increase by 0.03, for example. And here we have angle minus equals 0.03. So
this angle works according to the unit circle. But in our case, the value of zero is
upward. So it's a unit circle that is rotated 90 degrees counterclockwise, if you will.
But keep this image of the unit circle in mind of the rotated unit circle in mind, because this
is going to be our coordinate system for making rotations. Speaking of which, we can make a very
easy rotation like this using the Canvas context. First, we save the context, then we translate
to the point where we want the rotation to be centered at. And now I'm going to tell the context
to rotate by minus this angle. Next, I'm going to remove here, this dot x and this dot y, because
we're already translating to that point. And one last thing we need to do is here, add the call
to context restore. Because otherwise, on each frame of the animation, we are going to translate
and rotate and translate and rotate and translate and rotate. And this is going to do some infinite
series of translations and rotations. That will look pretty funny, maybe, but not what we want.
So let me save this refresh. And now when I press left, my car rotates counterclockwise. And
when I press right, it's rotates clockwise. So we have something there. It's not yet good.
It's more like, like a tank that can rotate in place. cars can do that. But more importantly,
if I rotate like this, and press the Down key, my car just moves downwards in this weird way. And
this is not correct. And same goes for upwards, we want the car to actually move in the direction
of this angle. So I'm going to implement this. Based on the unit circle again, I'm going
to make X go minus the sine of the angle times the speed. So unit circle has a radius of
one, this sign is between minus one and one. So we need to scale this by the value of the speed
as well. And then for y, we do the same thing, but with the cosine here, according to the unit
circle. Now we don't need this anymore. I can save the file, refresh the page, rotate to the left,
press down and look at that. The car is moving properly. And I can actually start to
feel like I'm driving a car. Mm hmm. But backwards, it's flipped. Like, look
at this. If I press backwards and right, the car turns left. That's an That's how it
happens in in real life. So we need to fix that. And also this rotating in place. Next, I'm going
to just put here, if this speed is not zero, I'm going to calculate a flip. So I'm
going to say the value of this flip is one or minus one, depending on the speed,
so that I can flip the controls backwards. And if if this is the case, then
here, I can just multiply by the flip. If the flip is positive one, then this doesn't
do anything. So when the car is moving forward, when it has a positive speed, then this won't
change. But when we go backwards, what essentially happens is these signs are flipped. So let me
close this here, and test again. Now if I press left and right, nothing happens, because our speed
is zero. But if I go backwards, and to the right, it goes just the way I want. And now
I'm doing these kinds of, I'm trying to change the direction in the car, similar to how
it would happen. When driving the car, play around with it. And if there's something you don't like,
change it, especially if you're good at physics. But if you're not, you can try using libraries
here as well. books to the is a really good one, I think. But I'm happy with this, there's one
thing that we don't need anymore. That's the debug here in the controls, I'm just going to remove
this, because now I'm confident that the controls work as I want them to, I'm going to save this
file. And in car, this update method starts to be quite big, and more things are going to be coming
here later. So one good thing would be to group all this code here in a separate method, I'm going
to cut it, and the method is going to be called move, because that's what this is doing. It's
moving the car, according to the controls. And the move method is going to be a private method,
it has the hashtag there. And I paste the code that we had previously in here, I save
this refresh, and everything still works. As previously, I'll show you how to
do the road next. So in index html, we are going to say here that we include road
Jas a new file that we have to also create here, road Jas, like this. And in here, we are going to
start to write our road class. So very similar as before, we have a constructor here. And I want the
roads to be centered around an x value, and have a width. Now, these are going to be attributes
that the roads should remember. And actually, I want it to have also a number of lanes. So let's
write here something like this. And define here, a lane count that has a default value of three.
For now at least, it's useful to have a few more attributes here that we can pre compute and
use later in our calculations. So for example, we can have a value for left that is half the
width less than x and a right that is half the width more than x. And I want the road to go
infinitely upwards and downwards. So let me just define here a constant for infinity, really
large number. There's actually one in JavaScript already but I found that when drawing things
with infinite size, weird things happen. So let's just have a very, very big value here. Like
this. Our top is going to be minus infinity and bottom is going to be plus infinity. Remember
that why on the computer grows downwards. Now to draw the road I'm going to make a draw
method here similar what we had the car draw method. And I'm going to set here a line width of
five. So a relatively thick line. And I'm going to make it white. That's how the lines on the road
usually are. And let's begin a path and move to left and top and the line to left
and bottom. So we are now drawing a vertical line on the left side of
the screen. I'm going to copy this, below like this. And I'm also going to draw
a line on the right side of the screen, like so. And we are done with this route
js for now, I'm going to move in the main js file. And above where we define this car, I'm
now going to say a road is equal to a new road centered in half the width of the canvas.
So that's going to be our center X. And it's going to have width of the whole canvas
width, I'm going to now go down here and write road draw on the canvas as well before the car so
that the road comes first and then the car on top of it. Let's now save this refresh. And almost
nothing happens. But you should be able to see here on the sides, some white lines there.
Now they're exactly at the side of the road, I want to leave a little bit of margin there. So
I'm gonna make here this with smaller, let's say 90% of the canvas width, save this, and refresh.
It looks better already. But we said that our roads should have some lanes right here in the
constructor, the third argument, it has by default three lanes, and we will need to make them appear
as well. I'll show you, we go back down here. And I'm going to write a simple for
loop going from zero to this ln count, inclusive. So notice here I have
less or equals to this lane count. And now I want to know what is the x coordinate
of each of these lines of these vertical lines that we are going to draw. And depending on the
lane count, we will have more or less of these lines, but also the x values are different.
We get these using linear interpolation, I'll show you. We say here, x is equal
to linear interpolation, or ulurp, we will write this function Don't worry, it's not
not complicated. And we will interpolate from left to right, so we need to get values between
left and right. According to a percentage, this percentage is going to be i divided by this
lane count like this. Think about it. This last value from here is going to be between zero and
one when I becomes this lane count, then this is going to be one. And with all the in between
values, you just get percent values over here. So how this slurp function looks like is function
ulurp. It's a funny name, but they've seen it used by game developers at least. And that's it. So
you have the value of a and then the difference between B and A times this percentage t. So when
t is zero, this part here is zero, you only have a when t is one, this minus a from here will cancel
out and you're just left with b so zero and one or zero and 100% just give you the two endpoints. And
for example, when t is in the middle, then this difference is just half of that difference. So
it's gonna move half away from a and so on. It's a really simple function, and I've used it in Very
many different projects, I really recommend that you have it in your utilities as well. So let's
see if it works first, and then I'll move it in the utils file, it doesn't belong here. We'll be
using this many times throughout this project. So here, I'm going to align this part
like so. But replace here left with x value like this, and also this one. And
we don't need this piece of code anymore. Closing this here, and I think that we
can save and test and look at that we have three lanes on our road, I'll move this function
now in a utils file. So let me create it utils.js, paste it here. Say save the file, go to index
html, include it, like this. And now if I refresh, everything still works. Let me go next to row j,
s, and add dashes to the middle lines, like so I'm going to check if I is greater than
zero and less than this line count, like this. And in that case, I'm going to
set line Dash. And I'm going to open an array 20 and 20. And this means that our dash will
have 20 pixels, and then a break of 20 pixels and another dash and so on. So let me close this
otherwise, for the borders, we are going to put line dash equal to an empty array like
that, I'm going to save this refresh. And there are the dashes. Let's test also with the
lane count of four just to see if it works. It's quite nice. But you can see the car is off center.
Now, I would like it to be on lane, right. So it would be useful to have a method that tells us
what is the center of a given lane, let's do it. Get Lane center with a given index. So this
will start from left to right starting at zero, I'm going to get a helper variable first, the
line width equal to a width divided by the line count like this. And then using that, I'm going
to say this left plus half the line width. So I want to be starting these in the middle of the
first lane. Plus, I'm going to multiply the lane index by the line width like this. And this is
going to give us four different line indices, an offset of line width away from the middle of
the first line, oh, not line with lane width. Line Width is something else. It's the
thickness of the line. Okay. And to test this, we can go in main Jas. And here instead of passing
this 100 for the X of the car, we can write road get Lane center. And for example, maybe we right
here three, to put it on the rightmost lane, save this refresh, and the car is on the
right lane, right. Let me go to the road and move this to have a lane count of
three. Again, I liked that value more. And now we see a problem the car
has gone. It's actually here. And Lane index three, which is now outside the screen.
So this is something you may want to have fixed for example, by going here and using the minimum
function and saying you want the minimum between the lane index and this lane count minus one.
With this, the car is going to go on the right most possible Lane even if you accidentally tell
it to go more than that. But I prefer my car to be in the center. So let's put this on lane one
here. And we'll keep it here for quite some time. Now This looks nice. And driving on
this road definitely feels feels nice. But in the future, I want to detect these
borders of the road here, and also have collision detections with them. Because naturally
we want the car to, to explode and impact. So basically, it would be nice if the road object
could tell us where these borders are. And I'm going to do that. Next, I'm going to go here in
the road in the constructor, and I'm going to say, this borders is equal to an array, I'm
going to put these borders in an array, we now have just two left and right, but think
about it, maybe we want to have highways, and then you have another board there in the middle,
or maybe some complicated situation requires more for some reason. So I'm just going to use an array
and you can experiment with different things. And the first thing in this array is
going to be a segment. So top left, bottom left points form a segment. And
I'm going to make this also an array, something like this. And the reason for
using arrays so much here is that our line segment here is straight. But maybe you
want to experiment with something else, like adding curves on the road. Actually,
that would be a nice homework for you to do. So I'm gonna keep a race here like this, the
borders for now are just two segments made of two points each. And these are
going to be here defined like so. And I just copy this three more times
and say two, top right. Bottom left. Bottom Right. And here we have, right, I'm just
going to copy this down here as well. And here we have bottom, and I'm gonna copy this here as well,
I will save this, and nothing really changed. Not yet at least, because these borders are just here.
If we say road borders, for example, they are just in memory. And they are easy to access. Whenever
we need them, we just ask the road, hey, where are your borders, and it can give them to us like
this. So the left border here with the X of 10, and the right border with x of 190. And plus
minus infinity for these great, but because these borders are here, now, it makes sense here
at the bottom to draw them separately. Because if we change them, like maybe if you're gonna make
them curved, this drawing here won't reflect that. So I'm going to change this to be here
starting at one and going to lane count, minus one. And I'm going to
keep just the line dash here, like this. And then I'm going to move
below this and say context set line dash to empty array. And now I'm going to show
you a for each syntax, we are going to go through the borders for each border.
Let's call it border, I'm going to use this arrow notation again. And I'm going to say
context, begin the path. Move to the first point in the board their X and the Y and do align
to the second point in the board their x and y stroke. And that's it. Now keep in mind
that this doesn't change anything. So all the code that we are writing now is
just to make everything more consistent, but I will teach you a cool trick. Next
we're gonna make it look like there's a camera above the car and it's gonna
follow the car as it moves on the road. So I'm gonna go here to main Jas, before
drawing the road. And I'm going to say save the context and translate nothing on
X but minus the y value of the car. And below drawing these things, I'm going to say,
restore again, like this. And now if I save, my car moved up there. But if I accelerate,
you can see that the car actually stand still. Unless I go left and right, but the road is
the thing that's moving. So what I want to do is actually not keep the car up there, but maybe
move it somewhere down, like for example, CAN bus height times 50%. So this is now centering the car
there. As I'm driving, this feels really great. But maybe a better value would be something
like 0.7. Because we in this way, we see more of what is ahead of the car. And later when we add
traffic, we want to be able to see those things, and figure out if the car will do the right things
or not. Really funny how easy this last thing was. To add sensors to the car, I'm going to go to
index html and include here. Sensor Jas, save this file, and create a file called sensor Jas liked
this. And inside here, I'm gonna define my sensor. And the constructor will take the car as the argument. And the reason is that I
want the sensor to know where the car is, it's attached to the car, and we're going
to be using the car properties to update it. So let's store here the car and other attributes
will be the array count. So our sensor will cast arrays in different directions. Let's just
have three of these arrays for now. And then the array length. So usually sensors like this
have a range after which they don't work anymore. So if they don't sense anything in this 100
pixels range, then they can see beyond that, and array spread. So here I'm setting pi divided
by four, this is the same as 45 degrees. And that means the angle that we are going to spread
these rays that are being casted by the sensor. So I'm going to keep here also an array called
arrays. And this is going to keep each individual array one by one after we create them. I'm next
going to create an update method like this. And we begin by setting these arrays to an empty
array. So this arrays is what we are going to start to populate next. And to do that I'm going
to loop from zero to this array count, like this. And I'm going to figure out the angle of each
individual array. And to do that, I'm going to use the loop function, our utility function from
earlier here, that gives us a value between A and B, depending on T, right. So if I go now back to
sensors, the angle here is going to be between Ray spread divided by two, remember the unit
circle, the rotated unit circle and minus Ray spread divided by two and the t value. Let's
have it i divided by this array count minus one this time, because I is not going to become
equal to array count. So the maximum value for I is this array count minus one
actually need to remember this, it's quite important. And with this, we can
calculate the start point for for our array. And that's just going to be the car X and
Y like this and an endpoint of the array. For this endpoint. I'm going to take the car x
as the starting point. And then using the unit circle, I'm going to take this angle that we made
previously, and multiply it by the the length so unit circle, just one big soul radius. We won't
see anything we need to scale it up by the array length here. And we do the very same thing In
with the y, but with the cosine there instead. Now with the start and end point available, I'm
going to push these inside of the array to form a segment. So notice here, I'm using this way
of defining the segment, using an array here in the same way that we used for the borders
here in road Jas, the road borders are also going to be made of segments defined in this
way, it's good to be consistent like this. Now, we also need to be able to draw
our sensor. And I'm going to say draw. And we go through all of the rays like this
beginner path. And I'm going to put a line with two. And I'm going to draw these arrays using
yellow. I will move to the arrays start location, x and y. So the first thing in the array
of IRA is its start location. And then I will line to the race and location. So one with
x and y there like that. And now I can stroke and close these like so I seem to be missing
here a curly brace. Let's save this file, and now instantiate the sensor, I will go
to car and inside the car constructor here above the controls, I think
it's it's good. I'm going to say this sensor is equal to new sensor
of this. So I'm passing the car through this. And then in the update method here,
I'm also going to be telling the sensor to update like this. And in the draw, in addition to drawing
the car, I'm also going to be telling the sensor to draw itself. So the car has now the
responsibility to draw its own sensor. Let's save this refresh. Okay, so we see now
three lines popping out from the center of the car like this. And they're at the 45 degree
angle, which is good. And when we move the car, maybe this is not what you want to happen. Maybe
it is what you want to happen. I don't know, you can experiment with these things. But I
would like the sensor to rotate with the car. So that when the car moves, the sensor also moves.
But nowadays with gyroscopic technology, you could probably have this kind of sensor as well if you
if you actually want. So to fix this, it's really easy. I just add here to the array angle plus this
car angle like this. And it works as expected. Let's play a little bit with these values up here
more, for example, let's have 30 segments and make them a bit longer like this. Wow. So many
rays coming from there. It's like the sun. Let's actually go all the way. So by The Times two. This
is now spreading these rays in all directions. So all 360 degrees, or pi times two there. Let's put
it to have by so a 90 degree angle. I think I I liked that. And let's try with Ray count of one.
We'll have a surprise here. It doesn't work. Two works just fine. But one doesn't. And
the reason for that is this thing here, this array count minus one. If array count is one
then this becomes zero and we can divide by zero. So what I'm going to do is I'm just going to
replace this line. If this array count is one I'm going to just return half, I want my array
to be straight up in the middle between the array span. Otherwise, I just do the exact same thing
as previously. So now it works, no, no problems. And we got it to work, we needed to do a little
bit of more thinking right here, because of this division by zero. Let's try with more arrays
to see if that still works. Maybe five. Okay. Nice. Now, before we get them to work to actually
detect that there are some road borders here. I want to refactor this a little bit. So all the
code here in this update method, I'm going to cut it and I'm going to say here, this dot private
method, cast race, like this, and cast race. He's going to get this code right here.
This doesn't change anything, everything still works. But now our update method here is
simpler and more code can go into it. And we can read it more clearly. Now, to be able to detect
the road borders, we need to know where they are. And at the moment, the sensor has no idea, we need
to go to main Jas. And the first pass to the car update method, the road borders like this. Now I'm
going to go to car Jas and have the road borders here, like this. And I'm going to take them
and pass them to the sensor update as well. So now in the sensor js, the update method
receives them as well. And now we can use them to detect with the sensor if the road
borders are close or not, I'm going to add here, an array for the readings. So these readings
are going to be some values for each array, telling if there is a border there or not.
And how far is now in this update method. I'm going to say readings is equal to an
empty array, I initialize it here. And then I will iterate through all the arrays
like this. And add to this readings array, or reading. So we will get readings
using a new method that we need to define that takes a array like this, and the borders
as parameters. And now we're gonna go down here and write our get reading method with a array and
the given borders. So now what I'm going to do is check to see where this array touches the
road borders. And now we have just two borders, one on each side. So one array can only touch
both of them if the car goes off screen like this, but it's good to consider multiple intersections
anyway, especially because later we'll have traffic. And we'll handle that using the same
strategy as well. What we'll do is find all these touches, and then keep the closest one to
the sensor. And that will be our reading in the same way that the real sensor would work.
So let's see where this array touches any borders, these touches is going to be an empty array. And
I'm going to go through all the borders one by one, we just have to at the moment, but it's good
to write it like this. And we say that the touch is equal to get intersection, this is going to be
a function that we will need to write it's going to be a very useful utility function, you'll
see and the intersection will be between the array of zero and the array of one. So this is
gonna be one segment, so far. And another segment the I wrote border zero and I wrote border one
like this. Now if there is a touch, we will add to our touches. So this get intersection
function may return know if the segments don't intersect. So that case nothing gets added
to this touches array. Now, if we have absolutely no touches with the given array, Arrays, then
that means that there is no reading here, we don't encounter anything with this array. So I'm
going to say here return no like this. Otherwise. And now I'm going to teach you a
little bit more modern JavaScript, you'll see this get intersection doesn't
just return the intersection point, but also an offset how far the
point is from this array of zero, which is pretty much the center of the car. So we
do get with this get intersection, three things, an x, a y, and this offset value that will be
quite useful. So I just want all the offsets from all the touches here in one array. And we can
use modern JavaScript methods to get that. Like this, the array map method basically
goes through all the elements from this array. And for each element, it takes its offset.
Now this part here returns a new array. In this case, it's called offsets. And that's it,
we don't have to write a lot of code to get this result here. And I think that's great. Now,
we want to know from all of these offsets, the minimum one, like if array touches many
different things, like many borders or cars will be later when we add the traffic. So we want to
know the nearest one after the rate touches that all the other ones don't exist, basically.
So we are going to get the minimum offset using the math minimum method. This minimum method
here doesn't accept an array as an argument. But it does work with many, many different values.
So this dot, dot dot operator is spreading the array into many different individual
values. And finally, I'm going to return the touch that has this minimum offset using
this find method here. So going through all the touches, element by element, if the offset
of that touch is equal to this minimum offset, it will return that touch. In this
way, this code is pretty complex take a while to look over it. But because
we're using modern JavaScript methods, here, the code is really short. So try to get used to
the syntax, because you will see it everywhere, especially if you work with React. Now I'm
going to draw these readings somehow here in the draw method, let, and is equal to the rays
and points pretty much. But if there is a reading, I'm going to set and to the value of that
reading, remember the readings, what comes from this get intersection function will be three
things, the x, the y and the offset. So if we say this here, we essentially pass the x and y to
this and, and becomes a point with an X and Y attributes. I'm going to now make this
yellow segment until end, right here. Like so. And I still want to know
where this line would have continued, I think it's nice to visualize that.
So I'm going to copy all of this. And I'm going to just make it, let's say black.
And here I will draw from the tip of where the end of the array could be to this end point. If
it's a reading or not, if not, then this is just going to be a line that is so small, you can't
see it. Now, if I save all of this, and refresh, it works for me, but it doesn't work for you. And
that's because I have here in the utility file a secret function that I added without telling you.
Sorry, let me just indent this a little bit like so so that you can actually see it. If you want to
implement this yourself by watching on the screen right now. I will make a separate video for this
because I think the topic is very interesting, and I have a nice way to teach it as well. But
it works. Look, when I'm moving my car like this, the race are going to the borders. And immediately
when they are reaching the board, there's there, they are turning black, like so. And only the
yellow part is really interesting. And it's what we will use later when we define the neural
network. I'll show you how to implement collisions next. That is when the car is approaching the side
of the road here. I wanted to get damaged somehow. So there's a good news and bad news. Good news
is that I'm going to teach you how to detect collisions using the segment intersection code
from previously. The bad news is that the way that we draw the car here, by rotating the context, we
actually don't know where the corners of the car are, like, what are their coordinates, and we're
going to need to figure those out first. So in car js, I'm going to create here a new
method, and I will call this private method, Create Polygon, this polygon will have a list
of points an array of points. So one point per corner of the car. And great news is that you can
add more points, you can have different shapes. And this method I'm going to teach you is going
to work. So if you look at this car right here, let me refresh so that it points upwards. And then
we can use this distance right here as a radius, like, it's pretty much the same distance no
matter which of the corners we are looking at. And the other thing that we will need to figure
out is this angle, like what is this angle, knowing the width and the height. So
this radius is actually very easy to get using the hypoid method here. So this is the
hypotenuse of the triangle with width and height, and I'm dividing it by two, because I just need
half of it. And the angle here is a little bit more tricky, maybe. So the tangent of this angle
is actually the width divided by the height. So we can use the arc tangent method,
this eight and two method from the math library to give us the angle, knowing the width
and the height, like this. And I don't need to divide this by two because that angle is the same
no matter how you look at it. Now with these, I can add my first point here, that x is
going to be the center x of the Car minus the sine of this car angle minus this
alpha value. So I'm combining here, the alpha angle, and the car angle as well,
and then multiplying these by the radius. And the same thing goes with y. But here I'm
using the cosine, like this. So this was the top right point. And we need to do the next ones as
well. So here, let's just add alpha, like this. Next point, I'm going to keep minus alpha there,
but add here, math, pi plus, or 180 degrees. And the last one, I will copy this one
from here and change this alpha to a plus. Like so. And now we just return the points. And that's it. We want to update
this after we move the car like this. So this car will have a polygon attribute that
will be generated in this way. And to draw the car, we can now use this polygon instead. So
instead of this trick here where we translate the Rotate and then just draw a rectangle here,
essentially losing the coordinate points, we can remove all of this and just draw our polygon
points in order I'll show you, we do begin path, move to the first point in the polygon, like this. And now loop through all remaining points. So notice that I'm starting here at one, because
I already moved to the first one previously. And here I'm just going to line to the eighth
polygons, x and y, like so. And I'm going to film, save the file, refresh the page. Here, nothing
happens. So everything still works. But in a different way, in a better way, I'll
show you. If we go up here, for example, and just change one of these points, let's change
this one by multiplying this radius by three, then our car will look like that. It's kind of
kind of funny. So we couldn't do this previously. And and now we can do crazy things like this,
like give complex shapes to our car. And we can, of course, add more points if we want. But I'm
not going to do that it's your job to experiment. What I will do is teach how to do the
intersection between this car polygon and this line segment on the right, and
detect if the car is damaged or not. So let's go up here and say this damaged is equal
to false, all cars are not damaged to begin with. And then here, after we have our
polygon, I'm going to assess damage a method that we're going to need to write with
the road borders. So now, here, let's define this assess damage method, given some roads, borders,
like so and loop through all the borders. And check if there is an intersection between
this polygon and the road border of i The road border, then we return true.
If the code reaches here, we return false. Now we need to implement this Polly's
intersect function here. And it will be a really useful utility function that takes two polygons
here as parameters. Now, notice here that this road borders of it is actually not
the polygon. It's a line segment, but it will be general enough for it to work. So
let's go now to utils J S. And I'm going to define our polies intersect method with poly one
and poly two, like so we look through all of the points in poly one. And for each of them,
we check all the points in poly two. And we are going to see if they touch or not using
our get intersection function from up here. So we say here poly one of I and poly one of
i plus one modulo the length. So this may look tricky, but all the things that I'm doing here
is I'm taking one point in the first polygon, and then the next point in the first polygon. So
I'm making segments essentially from one point after the other. But at the end, this will give
an error when we are reaching is equal to poly one length minus one, because adding one to that then
it will go over the array. But if we use here, the modulo operator like this, then that value
will become zero. And that's actually great because the last point in the polygon needs
to connect to the first point in the polygon, like point zero. So this code solves two things.
And I will continue and put the same thing for poly two with the J this time. So essentially
I'm taking all the segments that make the first polygon and comparing them against every segment
of the other polygon. And if there is a touch, then I'm going to return true. Otherwise,
if all of those checks have returned No, then I will return here. False. So our policies
don't intersect. Now to see if this works, I'm going to go back to my car JS here. And
actually in the draw method, the first thing that I will do is see if this is a damaged car,
I'm going to set the field style to gray color. Otherwise, I will set the field style to one
black color, like this saving the refresh, and it changes color. And it changes color immediately
when the car touches the border, even with this complicated shape, because we are using each
segment forming this polygon and comparing it with the border of the road. Now this method
is quite general. And it works for complicated polygons as well. But it can become slow if you
have very many points. So then you would need to do some optimization here, maybe look at the
bounding boxes, first star, or something like that. And it's really reliable, as long as your
object doesn't move too quickly. For example, like if our car would move so fast that it could
actually jump over a border or over another car in traffic, then you will need a different collision
detection strategy for that. But we won't let that happen. And if I zoom in here, a lot I should
mention this, because you may wonder why the car doesn't detect a collision now.
And the reason for that is that, well, that line, that border of the road,
it's a line, it's a mathematical line, which actually has no thickness. And here we are
drawing it with relatively thick thickness. And that's why it looks like it's intersecting even
though it's not, you could fix this if you want by drawing the borders of the road with
actual, very thin, infinitely long rectangles, put, I'm not gonna do something like that this
is great for our purpose, let me zoom back out. Change this car to be a rectangle. Again, I don't
want to keep it in this crazy shape. And now in the update method, I will not allow it to move
if it's damaged. So I'm going to say here, if not damaged, then do all these things. Sensor, maybe it's good to be like that. So the
sensor will still work even if the car is damaged. Let's see if this has any consequences. If I
refresh, now, the car is a rectangle again. And if I go to the side of the road, everything
stops. So now I can play with the arrow keys, nothing happens my car is useless. We really
want to take any kind of impact seriously in this so I am rendering the car useless at this
stage. Now the car works, it has sensors, it's detecting the borders, and it's getting damaged on
impact. But this is quite lonely, don't you think? It's time to add some traffic without it making
it self driving cars not much of a challenge. You just press the up arrow key and perfect
driving. So let's add the traffic next. I'm gonna go in main Jas. And up here I'm going to write
const traffic is equal to and this is going to be an array of cars pretty much. I'm going to add the
nother car here that is on the same lane as our card that we were used to, but I'm gonna put it in
front and it's gonna be our first obstacle that we need to avoid. Now here, we need to update this
as well. So I'm going to go through all the cars in our traffic and I'm going to tell each of them
to update and keep in mind the road borders or you can also pass there An empty array instead
of the board, there's, if you want the traffic to be invulnerable to everything. But I'm gonna
keep it there for now, let's see later. And we need to draw these as well. So I'm going
to go here, another for loop going through all the things in the traffic. Now we just have
one thing, but this is going to be a general code. And it's going to draw each of them on the canvas
like now I save the file, refresh the page, and there is another car in front of
us. And if we press the arrow keys, something strange happens. We are not controlling
our car anymore, we are controlling the other car. And that's because the key listeners are being
overwritten from the original car to the last car in the traffic, we're going to need to specify
which car gets these controls and which not. So I'm going to go up here and
tell this car to have here. Keys, I'm going to say this one to be just
the dummy, maybe we will have it move slowly or standstill or something like that. Dummy cars
will have some very simple behavior. And now we are going to go to car js. And in the constructor
here, I'm going to also pass control type. So either keys or dummy. And I'm going to pass this
to the controls as well. And in controls Jas. This constructor will get type like so. And here,
we are going to switch according to the type. And we are going to say that in
case it was keys, we are doing that break. And in case this is a dumping, I'm going
to set forward to true and break. Now I save this refresh a hint, the current front, the current
front moved, I actually can't catch it. Because we have the same movement speed, I
mean, max speed. So it would be nice to go here in the car constructor. And maybe specify
here, a value for this max speed. Let's have it default to three, but allow us to change it from
the main file. So in main Jas here, I'm going to set a value of two for the dummy, save this
refresh. And now I can actually catch that dummy. So far, the sensors are not reading this
dummy car, and we're not even getting damaged by it. We'll take care of that in
a sec. But first, this scene is becoming a bit too crowded with all these lines. And
the dummy car doesn't really need a sensor. So I'm going to disable this by going
to our car J S. And if control type is not dummy, then we will equip this car with
the sensor. And later where we update the sensor. We will check if this sensor exists.
And only then update it. And same goes down below for the trolling. So if there is
a sensor, then we are going to draw it. And now if I refresh that car in front doesn't
have any sensors doesn't draw any sensors. It's just a dummy going at fixed max speed
there on the road. Let's interact with it. I'm gonna go here in main Jas. And where we
update here our car, I'm also going to pass the traffic and because I'm going
to write some general code here, this is going to be needed to hear in traffic
update as well. But I'm just gonna pass nothing here because if I do pass traffic here, then that
means that car in traffic is going to interact with itself. So it's just going to get damaged
by itself. And I would need to write some code to prevent that. I'm also thinking about later,
when we do some more complex simulations. And we have a bunch of cars, I don't want to have a lot
of broken down cars on the road and blockades. So I think that I don't want the traffic to get
damaged when it's touching other traffic or us. So I'm leaving it empty. Here in case
of the traffic, I'm saving this file, and then I'm going to our car. And in our
update method, here, I'm going to pass traffic as well. And this is going to be needed
inside the assess damage. Here, we will assess damage with the traffic as well. But also, in
the sensor update, the sensor will perceive the traffic as well. Let's do this assess the
damage first, it's quite easy. I'm going to pass traffic. And I'm just going to copy this code
and replace here road borders with traffic. And here road borders with traffic of i dot
polygon. I think it's right, I save this refresh. And now when I press up, I'm catching,
catching catching that car. And when we collide, I'm damaged, it's not damaged. Because
our car it's not in its traffic list. But feel free to play with the code then
make it like that if you wish. Now let's do the sensors as well. So the sensor update here
gets the traffic. I'm gonna go now to sensor js and pass the traffic here as well.
And also to this get reading here, let me just write it a bit nicer.
So you can see this on screen. Like so. And the get reading has now
a traffic to deal with as well. So our touches, the things that the rays are
going to intersect may be road borders, but now polygon segments from the traffic cars. Let's
do this. I'm gonna go here below. And I will say I going through the traffic like this,
I will get a poorly. I'm doing this just so that I don't repeat traffic Have I got
polygon everywhere. And now I'm going to go through all of the points in that polygon and get
an intersection value using our utility function from the array of zero and array of one so this
is the same as there. But here we are going to take the polygon J point and J point plus one with
our module shrink. Now if there is a value here, then we add it to our touches as well.
And that's it. Let's Save and test okay, because the traffic cars are
black and my array turns black is it touches the nearest segment of its
polygon. I don't see things very clearly. Let me color the cars differently so that
our car is blue and the traffic is red. I'm gonna go in main Jas. And here let's
draw this one as blue and the traffic as red like this. And inside car Jas.
I'm going to go down to our troll method here and say color and the non damaged
color is going to be color. We save this refresh. And now we can actually see that the
array turning black like that and our sensors are reading that there is dancing in front of it.
They are working from all different angles. Let me try a trick I saw once in what was it fast and
furious, maybe it was much better in that movie. How it went right through us. We now have a
complete simulation, a drivable car with sensors that can see the road borders and other cars in
traffic. But so far we've been doing the driving, and that's about to change. Next, I'll give you
a brief lesson in how our brains work. And then I'll show you how to code something similar,
an artificial neural network, you'll learn the components of this kind of network and how to
connect it to the car so it can move by itself. Neural networks are computing systems inspired
by the biological neural networks in our brain. These are some neurons, these branch
like structures received the signals. When stimulated enough, a neuron will
fire a signal through its axon. So a single neuron does something really simple, and
intelligence only comes when they work as a team. You see, your brain has 86 billion neurons. You
also have quite many of them in your spinal cord, then sensory organs like in your eyes and yours.
These sensors send signal to some neurons that pass it to the brain. There the processing happens
like a chain reaction where some neurons fire some don't eventually signals arriving to motor
neurons that pass them through your spinal cord and make some muscles contract in very specific
ways. Hey, a lot happened in just a split second, let me try to slow this down and
explain it to you. Pay attention. sensors inside the year pick up
compression waves from the air and sends signals to the brain which figures out the
direction based on different intensities. signals then traveled to neck muscles tell them
to contract and turn the head in that direction. The eye catches a glimpse of what's happening
and use signals travel to the brain. peripheral vision is blurry. But good enough information
exists to detect some kind of object approaching and new signals travel to facial muscles to
contract and protect the eyes from getting hurt. Eyes are really important. At the same time,
the brain begins to kind of defense protocol by sending signals throughout the body. Time passes
and the image becomes more clear. The brain does pattern matching and recognizes the object as a
bowl. Contextual and historical information will play an important role in what happens next. Let's
stick to the first one. The brain concludes there is no threat and uses knowledge about physics it
learned during its lifetime to predict where the object is headed. It then sends new signals to
the muscles to contract in slightly different ways to catch the object instead of blocking it.
And the rest is history. One history where the brain did the good thing and not much happens
afterwards. But sometimes this happens and the brain learns it did something wrong and configures
itself. So the same thing doesn't happen again. Hopefully, anyway, I'm feeling really good about
myself now that I know my brain can do something like that in an instant. hope you do too. I work
cars neural network, we'll do something like that. Neurons on the first layer will be connected
to the sensors, they will send signals forward a few times, and the last layer will be
connected to the car controls to make it actually do something. Spoiler alert, we'll be
working with relatively small networks here. But that's okay, because we actually don't
need very large networks to solve this problem. Now implementing all this in one shot is well,
scary. But it's easier if you break it into levels like a building. Each level has a floor
a ceiling and connections in between. The ceiling of one level is the floor of the
next man so on. Now, let's start to code one of these levels. I'm going to go in index html
and include a new file here. Let's call it network Jas and create the file here like this. Inside the file, we begin to write our level
class. And a level has a layer of input neurons and the layer of output neurons. Their numbers
doesn't necessarily match. So I'll specify these parameters here like this to define The actual
neurons, we use simple arrays of values like this, we need one for the inputs,
and one for the outputs. And I'll also add one for the biases,
each output neuron has a bias, a value above which it will fire. So I'm
defining these as an array, like this. Now, when coding this, I'm actually going to
connect every input neuron to every output neuron. That's not the case in our biological brains,
but these connections will have weights. So a weight of zero means pretty much the same
thing. So let's have here, weights equals to an empty array. And going through all of the
inputs, I'm going to prepare here, an empty array, the size of the output count. So
for each input node, I'm gonna have output count number of connections. And now what
we have here so far is actually only a shell for the brain to function, these weights and biases
need to be set to some real values. So for that, I'm actually going to simply randomize for now,
we're going to have a random brain to begin with. And to randomize, I'm actually going to define a
static method here. This is different from what we usually do, but we do it because I want
to serialize this object afterwards, and the methods don't serialize. So here, given
a level, I'm going to go through its inputs like this. And for each input, I'm going
to go through its outputs like this. And for every input output pair, I'm going
to set the weight to a random value between minus one and one. And to do that, I'm gonna
write here, math dot random, which gives us a value between zero and one, multiply this by
two. So now we have a value between zero and two, and subtract one. And now we have a value between
minus one and one. And actually biases are going to be in the same range as well. And you may
wonder why negative values. And here's one reason for it, think what should happen here, the car
sees something with the front sensor, so it should turn to avoid collision. But which way, negative
weights connected to these sensors on the right could send the message that don't turn to the
right. So the remaining option is to turn left, both weights and biases can be between minus one
and one. And there is a nice mathematical reason for this, which I will explain soon. Now, these
inputs, they will be the values that we get from the car sensors. And what we need to do is compute
the outputs using these weights and biases that we defined. Those are random for now. But in a smart
brain, they will have some kind of structure, we compute the output values using a feed forward
algorithm. And it's actually quite simple. Given some inputs, these given inputs,
and I'm passing here the level as well, I'm going to go through all of the level inputs,
and I'm first going to just set them to these given inputs, these will be the values that come
from the sensor. So we do that using a simple for loop like this. And now to get the outputs
we are going to loop through every output like this. And we are going to calculate some
kind of sum between the value of the inputs and the weights, I'll show you. So this sum is
zero in the beginning. And now we are going to use another variable j to loop through the inputs like
this. And the sum will actually add the product between the JSON input and the weight between
the Chained input and the output like so. And we repeat this with every input neuron. So,
in the end, what we will have to do is check is this sum greater than the bias of this output
neuron and if so, we are going to set the output neuron to one so we are essential Returning it on.
Otherwise, the output neuron is going to be set to zero. It's as simple as that. And now I just want
to close this and return here, the outputs for convenience. And I need to also close this level
class right here. Now this code will work. And we'll leave it as such. But I'm going to tell you
a story. And it's a bit scary. So brace yourself. You see, scientists like to write it by adding the
bias here and comparing with zero. Because biases can go either way, any structure implemented
like this can be implemented this way as well. And this, what we have now is
the hyperplane equation. But don't be scared. You see, in a very, very
simple network, this is the line equation, a very simple function. The one and
only wait here controls the slope, and the bias controls the y intercept, we
have a function like this for each output, these neurons will fire if the
value of the function is above zero. And with weights and biases between minus one and
one, you can implement any situation. That's why that rage. Now, when you have two sensors, you
have a plane in 3d space. Think about the plane that goes out of water like this. If the value of
the two sensors is on dry land, then the neuron will fire. When you add more sensors, you have
higher dimensions that are harder to visualize. But the math still works. And you
can have as many sensors as you want. I could go on with this story for hours and tell
you that scientists don't use binary values here, but instead allow neurons to fire all the time
just at different amounts. I mean, it happens here for these first ones, so why not for the other
ones as well. Only the last ones need to be binary to give you a clear yes or no answer. But networks
are more powerful if these are left as such, actually not exactly as such, because the values
here tend to go a bit out of control and a more complex function is often used to bring them back
to scale. Now the story may turn into an actual horror, if I start to tell you about what the
layers actually do. Moving from linearly separable cases to non linearly separable ones, and fine,
fine, I stopped this now. But if you want to experiment with things like that, you probably
want to try some kind of library, like TensorFlow, and there are actually more of them out there.
Also, let me know if you're ready for more advanced content like this, and maybe I'll make
it someday. Meanwhile, I recommend three blue one brown video on neural networks, it focuses more
on the mathematics and uses things like vectors, matrices, and other things that tend to scare
students off for some reason, I try to avoid these here. But if you plan to master machine learning,
someday, you're gonna have to learn them. Sorry. Now let's get back to completing our neural
network code. So far, we have just one level. Let's go now up here in the beginning, and define
a neural network made out of many of these. So I'm going to say here, class neural network,
like so. And the constructor is going to get an array of neuron counts. So this is going
to be the number of neurons in each layer. And I will define here, the levels, I'm going to
make my neural network out of an array of levels, those we defined earlier. And for
each level, I'm going to specify the input and output count like this. So I'm
adding here, a new level with the neuron counts from the eyes index, and the neuron counts from
the i plus one index. That's it. Simple as that. And here, we're going to need a feed forward
algorithm as well. And this is also going to be really easy. So in the same way, given inputs
and the network, I'm going to get the outputs by calling the feed forward method from the
level with the given inputs and the network's first level like so. So this is now calling
the first level All to produce its outputs. But then I'm going to loop through the remaining
levels. So notice that here, I'm starting at i equals to one. So looping through the remaining
levels like this, I'm going to update this outputs with the feed forward result. From the level, I
like this, let me close this, return the final outputs. And we are done with the feed forward
method and with the neural network object. So what we're doing here is essentially putting
in the output of the previous level, into the new level as the input. And the final outputs will
tell us if the car should go forward, backward to left or right. Now let's connect this network to
our car sensors. So I'm going to go to car here, all the way up in the constructor. And where we're
saying that we defined sensors for our car, I'm also going to say this dot grain is equal to new
neural network. And here we have to specify our array of neuron counts the size of the layers.
So in the first layer, we are going to have this dot sensor that array count. And here you
can have as many layers as you want, but I'm just going to add two of them. one hidden layer, and
the output layer, which will have four neurons, one for forward, one for backward, one for left,
and one for right. Let's close this, like so. And now, after we update the sensor here,
I'm going to first take out the offsets from the sensor readings. Remember, our readings
had three things an x of y, and the offset of where the reading was. And from each sensor
reading, let's call it S, I'm going to check if it is no, then I'm just going to return zero, there
is no reading here, the sensor goes as far as possible and doesn't see anything. Otherwise,
I'm going to return here, one minus the sensor offset. I'm doing this because I want our neurons
to receive low values if the object is far away, and higher values close to
one if the object is close, kind of like this flashlight. When we pointed
at the wall, we see the light coming back at us. And if we get closer, the light that bounces back
is stronger. That's how sensors work in practice. Now to see what the neural network has to say,
we just say outputs is equal to a neural network, feed forward these offsets with this brain. And
I'm going to log here these outputs to test for now, if I save the file, and refresh the page, I'm
gonna get there at the bottom in the console, some array of values for values, one for forward, one
for left, one for right and one for backwards. So at the moment, the car should actually go forward,
left and right at the same time. But it can't do that because the brain is not yet connected to
the controls. It's kind of like when I had the knee surgery a while back and they paralyzed me
from the waist down. While the anesthetic was wearing off, I really tried moving my feet brain
was working normally, but motor neurons were just not sending the signals properly. To make
the car do its own decisions, we are going to go here in main js. And instead of using the
keys to control the car, let's write here, a AI for artificial intelligence. I'm going to save
this file and then car js. Up in the constructor. I'm going to define here an attribute called
use brain that is going to be equal to control type double equals AI. So if our control
type is AI, it means that the brain that the car actually has already is going
to be in use. So down here in update if This brain is in use, we are going to say the
controls forward, this is going to be equal to output six zero controls left, this
is going to be equal to outputs of one controls, right, this is going to be called
two outputs of two and controls. Reverse, I think it was, is equal to outputs of
three, let me just check this real quick. Yes, it's reverse, saving this file, refresh
the page. And nothing happens. But if you look in the output, you'll see that this array has
four zeros in it. So nothing is on forward, reverse left or right, nothing is pressed. Let's
try to refresh again, remember, these networks are random so we can get anything out of this.
Okay, this was interesting. So we have their right and reverse turned on. So this was a card
that just went backwards and to the right, okay, here, we have a car going forwards,
left, and then reverse. So the forward then reverse actually cancel each other
out, and the car is standing still. Okay. Now, this is really interesting, we have a
car that goes forward. So it went forward in the beginning. But look what happens. Now, when
those two sensors are touching the car in front, the network somehow triggers the brake. It's
amazing, this was randomly generated, and we were lucky to get the really nice behavior out
of this. So our car is supplying the brake, every time it gets really close to the other car, and
just out of luck, it's not colliding with it. So it's doing this kind of stoking motion there. Now,
this debugging here is not really very useful, printing out the outputs like this. And actually
debugging things like this is really hard, you could go here and say, for example, car dot
brain, and look at it and see that yes, it does have these two levels that we defined previously.
And the first level has some inputs, some biases. So we have the inputs, here, you can see 000, for
the middle ones, and some values for the first and the last one, which is great, we visually
see that one as well. And the outputs should be for the first level here. One, one, and all the
other ones are zero. But then the second level is going to get those inputs, the 110000. And this
network with these biases, random values between minus one and one. And these weights, also many,
many random values between minus one and one, produce the result that you see here. So
going forward, and reverse and left and right, everything just cancels each other out, then
our car with all this brain does nothing. So you can see how debugging like this is
very, very difficult. And we don't really get the full picture unless we really start to
analyze all these numbers here. And that's why it's gonna be really great. When we visualize
something here, I mean, make something that at a glance, we can see this entire network in
action. So I'm gonna go to index html here. And I'm actually going to refactor things a bit. This,
my canvas from the beginning, is gonna be called car canvas like this. And we're gonna use another
canvas on the right to display the network. So we're gonna visualize a neural network in
this one. Now, let me save this and go to our CSS file. And my canvas will be renamed to
car canvas. And I'm going to copy this as network canvas and to distinguish between
them. Let's make this background black. Like this, now, in main Jas, I'm going to
go here at the top and say that this all we did so far is on the car canvas. And I'm actually
going to refactor everywhere it says canvas here, by pressing right click Rename symbol and saying
here, car canvas like this, and this CT x is going to be renamed to car seat dx, like this. And I'm
gonna copy this part right here for the network Canvas. So here we have network, Canvas, and
network canvas. And here also network canvas. And I think this one can be a little bit wider.
Let's see if we can fit 300 pixels wide Canvas next to our car Canvas from previously,
then here, the car context is going to need network context next to it. So network
context coming from our network canvas, right. And one more thing we need to do here,
when we make our car Canvas have the same size as the screen vertically, I'm going to do the same
thing for our network accounts. Save the file, refresh the page, and we have our car on the
left doing something, whatever it's doing, and every time we refresh, it's probably gonna do
something different. Probably not something smart. And on the right, we have a blank blank canvas
that also stretches quite nicely with the size of the screen. And what will be really great is if
you can go here, after drawing all the car related stuff, and saying visualizer draw me a network
on the network context, and put there the car brain like this. And now I'm actually going to add
some code here that does the visualization for us. I'm not teaching it here, because I think we
would be sidetracking too much. But it's really well described on my channel and their source
code in the description. So saving everything refreshed the page, and we have network
that decides our car should go in reverse. quite boring, let's try refreshing again. This network says it should go forward, left
and then reverse. So basically, it stands still forward right and reverse. Right then
reverse. That was kind of interesting. One more thing is that this visualization
also supports animation. So we could animate this visualizer if you want by going
here and specifying a time argument, this value comes from the request animation
frame method here. And here we can say network, C dx dot line dash offset is equal to
time. And when we do this, all by, okay, this is a bit too fast. Let's slow down this
a bit by dividing here by 50. And actually, this animation should go the other way around. So
I'm going to specify here minus time divided by 50. And now it's a visual reminder that this
is implementing the feed forward algorithm. This thing here, one by one, these randomly
generated networks and expecting some miracle is not very convenient. So let's write some code
to parallelize this and have many cars running simultaneously, like in different parallel
universes. I'm going to define here a function called Generate cars that accepts and as the
argument and I'm going to have an empty array of cars at first. And then for i from one to N,
I'm going to push a new car into this array and the car is going to be centered in the middle lane
at 100 Why with 30, width and 50 height like so, and they are all going to be AI cars. And
here I just return these cars like so. And up here where we have car defined we are actually
going to define cars instead equal to generate cars have n like this and our n is equal to
100. So we are going to have 100 cars going in parallel, and hopefully one of them is going to
do something right, we need to update these cars here. So where we have here car dot update, we
are going to write a loop and say, let i is equal to zero, i is less than cars dot length, i plus
plus. And then we have here cars of hi update, like so. And we are going to do the same
for the drawing down here, drawing them as blue like this. But we still have two
individual car objects here from previously. And I only need one car here. So let's
just replace it with the first car in the array for now. Otherwise, this code will not
work. So this is for translating the canvas on one car from the 100. And this is just printing
the brain of this one car somewhere on the right, save the file, refresh the page, and then how its
total mesh. One thing we can do to make this nicer is actually drawed them semi transparent. So I'm
going to go here and say car city x dot global alpha is equal to 0.2. And after I'm done drawing
these cars, I'm going to set global Alpha back to one. So it doesn't affect what I'm going to
be drawing afterwards or on the next frame. So saving this and refreshing, it's a little bit
better, but still quite much happening there. I think that I want to emphasize one
car, like for example, the zeroeth car, this one that we are using everywhere, pretty
much by drawing it again with the maximum opacity. But also I want this one to be the only car
for which we draw the sensors. So I'm going to modify this draw method of the car next,
and say here, a third parameter for draw sensor. This is by default set to false. And
here, if this sensor exists, and draw sensor, only then we are going to draw its sensor.
And now it's a little bit clearer, not so many yellow lines there. And you can see something
interesting already, that this car right here, car zero is not the most interesting one, the most
interesting one went somewhere up there, and it was chasing the other red car. So back to main js,
here, our cars have zero choices not really great, we want to focus on the best car, the one that
goes upwards, the most or the one with the minimum y value. And I'm going to define this best car
here and say, best car is equal to find the car whose y value is equal to
the minimum value, okay? Of all the Y values of the cars. So this may seem a
little bit scary. So what is happening here, cars dot map, see going to see dot y this means that we
are creating a new array with only the y values of the cars. That's it. And here we are spreading
this array because the math main method here doesn't work with arrays. It works with many,
many different values no matter how many you want to pass it, but not with actual arrays.
And that's why we need to spread it here. And we just find the car whose y value is the
minimum value of all the y values. The code is not really complicated. But if you're not used
to this modern syntax, then this can be a little bit daunting at first. I added here because
you will see it if you work with JavaScript. So now with this best car, I'm going to copy
it everywhere. We were using cars of zero previously so we translate the canvas to the
best cars. Why like this. And then here we are drawing the best car without transparency so
that we clearly see it. And with the sensors, we are also drawing the brain of this best
car. And that's it, I'm going to save this and refresh. And now our canvas is always centered
on the car that goes upwards, the most, in this case, these stalking cars that are behind this
red one. All right, let's try to refresh again. And again, I'm waiting for a
car that passes this red car, but then continues to go in a straight
line somehow. So just by trial and error, I think that we're going to be able to
get to something like that, let's see. Okay, we got something, it's fidgeting quite
a lot, but definitely better than all other cars we had so far, it passed the car in the
traffic, and it's just going forward forever, pretty much. But now the problem is that when I
leave the browser, this car is going to be gone. And all my experimenting was for nothing.
So let's write some code to save this car. I'm going to use local storage for
this. So I'm going to say, local storage set item, the best brain that we have. So far,
we're going to store it in this best brain attribute and local storage. And the value is
going to be a JSON string coming from the best cars brain. So I'm essentially serializing that
brain into local storage. And come to think of it we might need to destroy the saved brain as well.
So I'm going to discard, maybe it's a better word, the value from local storage with this other
function, I'm sure is going to be needed sooner or later. Now this best car is actually not
accessible here. So let me just update its value here. But define it here as a global variable.
Like this, I'm using lit because it's changing. And I'm gonna set it to be the first car in the
beginning, but it's gonna update on every frame. Now, if we have in local storage
of brain or best brain stored, I'm going to set the brain of this best
car to the value and I'm using JSON parse because I am parsing the JSON string we saved
previously, local storage only works with strings like this. Now, in index html, I'm going to put
these buttons vertically between the two canvases. So here, I'm going to use a div to define river
vertical buttons with ID vertical buttons. And I'm going to say here, the first
button is going to have an unclick of safe like this and our second button is going
to have an on click of this card, like so. And here I'm actually going to use emojis
for these boughten contents like this with the save icon and trash like you can find
emojis like this just by searching online. So I'm gonna save this file and go to
the CSS next, and remove this text align from the body because we're going to use flex
display instead. So I'm going to remove this and say, display, flex, justify content and align
items to center like this. And now our vertical buttons are going to go here with display flex and
flex direction column So this is by default row. And making it column means that our buttons will
go below each other. And here we define our button next. So I like to remove the border and give it
a round shape. So a radius there. And a padding, this is some value that I found goes nicely
with the emojis that we use, and the margin of two pixels. And the cursor is set to pointer
so that our arrow icon changes into a hand cursor when the mouse is over it. And speaking of
hovering with the mouse, let's set hover for the button so that it gets blue background
like this. So save the file, refresh the page. And looks like we have another shaky car over
here. But we are not going to lose it this time. Because we can press the save button. And
this means that now inside of our local storage, we have this cars brain. So now when I'm going
to refresh the page, what that means is that here inside of main js, where we are loading the
value from local storage, we still get the car from previously. Now what we are doing here,
could we actually fully automated so that the computer tries again and again, and always keeps
the best. But let's think about what the goal is. I mean, what will a good network be able to do?
Let's look at the definition of the best car here. We're just using the y coordinate. But you can
use other functions here as well. These are called fitness functions. For example, you
could measure the travel distance instead. Doing that may result in cars that go backwards
as an easy way to avoid the obstacles. Or just go around in circles forever, going infinitely
far, but also going nowhere at the same time. So need to be careful when choosing a fitness
function. I'm using the y value here. But if the road would be turning, it wouldn't work anymore.
So think about these and play around with the code and try to think about other stuff as well. Like
could you come up with a fitness function that keeps rewarding the car for going upwards,
but penalizes for going too much sideways, or even better penalizes if you're
not in the center of the lane. These are all homework ideas for you. But
now I think we're ready for more traffic. Let's go here in our traffic array, put a comma at
the end and add two more cars. One is going to be on lane zero, and minus say 300 and the other
one at the same vertical level. But lane two, I'm going to save this refresh.
And now let's see what happens almost there. But that looks like even our
shaky car didn't make it. So let's try again. It looks like there are just not enough cars
passing that first car in traffic. What we would like to have is more cars like this shaky car.
But not exactly like it so that one of them may be is gonna stand the chance and pass the
second and third car in the traffic. So let's go now to network Jas. And I will teach
you how to mutate a network. And it might sound fancy, but it's really really easy. I'll
show you. We right here a method called mutate, which takes a network as a parameter and an
amount. Let's default this to one or 100%. This will just randomize everything what we had
previously. But if you set this value to like 10%, then you're gonna get the network that is
close to the original one that we pass it here. So how does this work? Well, we are going to go
through all the levels of the network using a for each loop like this. And for each level, I'm
going to use this arrow function notation here. I'm going to iterate through all the biases and
then through all the weights later, but first with the biases and I'm going to say that the AI
bias is going to be equal to learn, we're going to use linear interpolation again. And I will
interpolate between the current value of the bias, whatever that is, and a random value between minus
one and one, as we did in the randomizing, but now we're using loop to go from the current value of
the bias to words, whatever that random value is, depending on this amount right here. As
simple as that. And I'm just going to repeat the same thing for all the weights. So I
just go through all the IJ weights like this. And say that those weights, that
weight of ing scoring to be equal to linear interpolation between the current value of
the weight and a value between minus one and one random value. And again, depending on this
amount, so if this amount is going to be let me see, did I close this properly? Nope. So if
this amount is going to be 100%, then we just get this part right here. And this mutate is pretty
much our randomized method that we had previously here for a level. But now it works for the entire
network. So if the amount is zero, then our biases, and our weights just stayed the same. But
if this value is somewhere in between, then it's going to move away from these biases, depending
on this amount. So let me save this file now. And go back to main js, where we are reading the
brain from the local storage here. And actually, we are going to loop through all the cars and say,
let i is equal to zero, less than cars dot length, i plus plus. So for each car here, cars
of high hits brain, it's going to become equal to that from the local storage. So this
as such is going to be really, really boring, right? Because it means that if we refresh now, we
just see 100 cars that are overlapping each other, and they're, that are acting exactly the same. But I'm going to put here a condition and say
that if AI is not zero, so the brain that we have in local storage will be kept for the case when
I zero and that is our best car initialization there. But for all the other ones, I'm going
to mutate and say neural network, dot mutate the car, have I brain by an amount, let's
say 0.1. And now let's see what happens. You can see many cars this time, but they are
all following our shaky car quite closely. This one, none of them was able to go beyond
that second car in traffic. Let's try again. Nope, it looks like this 0.1 might be too low.
Basically, our new networks are too similar to the one that we already have in memory. So let's try
0.2. Now our cars look a little bit more different than before, but still quite similar to our shaky
car. And look at that we have a new best car. Maybe. I hope it doesn't hit the border
now. Yes, it figured out it has to turn. That's great. So I'm going to save this new car
right now. And when I'm going to refresh, now, our new car the more improved car which
wasn't even shaking is stored in memory and it's going to mutate and try to become even
better if possible. But our scenario now with three cars is not a match anymore, so we can try
even harder one. Let's go here and copy this. Maybe The four more times. And here let's have
two cars at minus 500. And the last at minus 700. And we are going to have here maybe on
lane zero and Lane one, and then on lane one and lane two. So I'm just thinking how to
make a complicated scenario that involves all kinds of twists and turns. So let's save
this and refresh. And now let's see what happens okay, I'm gonna save this I did it before
this other boring car over took it. So that's something that you may want
to pay attention to. Let's refresh nothing I think we want the lower
value for the mutation here. So that more cars are going to be like that one
that almost wented past these other two cars. You can see quite many of them here
now. Still nothing. Let's try again okay, this is interesting. I would
say this is a new winner now. It got to this stage and now it's going
in line with the other cars. Let's save it. It's taking too long. Let me try with 1000
cars. I think this should work on my computer if your computer is too slow, then just
stick with 100 cars and repeat more. Ah, almost there. You Yes, yes, we did it. We did it.
Oh, no, no, no, no, no, no, no. I should have saved that one. I think it could
have mutated into something good eventually. Hmm. Let me save this. It's clearly better. Come on. I'm hopeful that this time
we're going to make it. Let's see. Okay, two cars there now. Will one
of them stop before hit? Yes. Yes, that car stopped just before hitting the
sides there. Let me save this. Try again. And now we have quite many of
them that are quite similar. Okay, let's keep just one car here and see it in
action without any other things disturbing it. Okay, that was quite, quite close there. Oh, yes. Somehow I thought it's gonna get damaged
even though I know that it's the best car from previously so it won't get damaged. There are many
more sophisticated genetic algorithms out there. For example, some algorithms use crossover. So
they take two networks and mush them together somehow. The hope is that maybe one car knows how
to turn left really well and another one knows how to turn right. So combining these networks
could give you a new one that can do both. Thank you can implement this. If you got this
far. I think You can try it out and let me know how it went. We are now actually done, I
won't be teaching you any more algorithms, you have many things you can play around with.
And I'm quite happy with the car we have right now it's making its own decisions, and it's avoiding
all other cars. It couldn't be better like if it would recognize the lanes, but I'll let you worry
about that. Meanwhile, I'm going to relax and show you how to draw a car in PowerPoint. Since you've
got this far, all I can say is congratulations. Hope you took things seriously. And by now
you've learned how to simulate driving mechanics detect collisions and create artificial
sensors. You also know about neural networks, what they can do and how to optimize
them using a simple genetic algorithm. But we just scratched the surface here, and
I haven't really taught you why the neural network works. I mean, sure, we built one.
And because it's so complex, you can assume some smart behavior is possible. But how does it
really work? We have such a great system now that it's a shame to put it to waste. So I'm thinking
about making a new course where we start off with a very simple network and gradually go bigger
and bigger and see what each of them can do. We could also try new things like
teaching the car to stop before impact, how to respect the lanes, maybe even how
to park and other cool stuff like that. What do you think? Would you like a course
like that to practice even more JavaScript and become a master of making diagrams that
show what's inside these so called black boxes. And if you want something else right now, I do
have a lot of content on YouTube. This course teaches how to build location based applications.
In this one, we make an augmented reality piano. And here we use the camera to make a new kind of
puzzle game. I have many small projects as well, but it's really hard to keep track. Maybe they're
better organized on my website. I don't know. By the way, on the website, you can find a more
enhanced version of the self driving car. Check it out if curious. Anyway, thanks for watching.
Please share this course with anyone you think is interested. And since you got this far, you're
probably subscribed already. So see you guys