This video hit me in a certain way. I never really used the canvas element much but this sparked my curiosity. The OOP techniques, the physics elements, and the keyboard-listeners for game-like functionality are also very interesting. Author has a sense of humor.
Stack: Vanilla JS and no libraries. JS is obviously not the best option but some good learning
Simple Physics to Move the Car
Html including a canvas
Select canvas with JS and use to make full height of window
Draw car on the canvas by getting context with “.getContext(“2d”) method”
Create a “Car” class and in “car.js” file
Add a Controls class with an “#addkeyBoardListeners” method that leverages a switch statement. Handle the “this” keyword with an arrow function.
Add an “update” method to the car object.
Define and call an “animate” function in the main.js file. This function leverages the “requestAnimationFrame” function.
Add speed and acceleration and friction properties to the Car class.
The friction has a bunch of if statements to adjust the speed.
Define an angle property in the car class to better handle max speed.
Add a very easy rotation ability to the car by adding a rotate property in the draw context.
Leverage “Math.sin” and “Math.cos” to move the car a little more realistically. This includes reversing the direction if left and right keys pressed when the car is moving backwards
Mentions Box2d as a physics library you could leverage
Road Generation as Drone Perspective
In the index.html file link to a new “road.js” file that contains a Road class that contains a width and lane count.
Add a draw method to the road to make a vertical line on both sides of the screen.
In the “main.js” file instantiate the new Road class. Order matters here.
Add a “for loop” to the draw method in the road class to handle a dynamic lane count. Uses something called a “lerp” function (linear interpolcation) and move to a “utils.js” file.
Add dashes to the middle lines of the road.
Create a “getLaneCenter” method to help keep a car in the center of a lane at the start.
Road should tell us where the borders are. Add borders property as an array to the road class. Also add “topLeft, topRight, bottomLeft, and bottomRight” properties.
Keep the car centered on the canvas even if the road is moving. Surprising how simple.
Add a new “sensor.js “file to define a Sensor class and link in html file.
Sensor will cast rays in different directions.
Add an update method that also leverages the “lerp” (linear interpolation) function.
More “Math.sin” and “Math.cos”
Draw the sensor rays.
Instantiate the sensor from the Car class constructor.
The car also has the ability to draw it’s own sensor.
Make the sensor rotate with the car.
Now we’re using “Math.PI”
Ternary because we can’t divide by zero.
Settles on 5 rays.
Adds and “castRays” method and calls in the “update” method to help keep the “update” method simpler.
Pass the road borders the update method
Iterate through these rays
Create a new “getReading” method.
See where these rays touch any borders.
We will write a very helpful “getIntersection” utility function later. that gives an x, y, and an offset.
If no touches then no reading, otherwise.
Use Array.map to store all of the touches in a “offsets” array.
Note: The Math.min() function does not work with arrays but you can “spread” the values in.
Return a touch that has a minimum offset using the “find” method.
Fairly complex code using newer JS so study and understand.
See where line would extend to so we can still see it.
69-minute mark he shows the “getIntersection” utility method that he recommends we copy/paste. This is where the magic happens. When the car moves and a ray touches a border the ray changes color at and beyond the intersection point. Impressive.
When the car is approaching the side of the road we want there to be some damage.
In the “car.js” file, add a “createPolygon” method to the Car class.
Did I just hear hypotenuse and tangent? Add radius, alpha properties in this “createPolygon” method.
More “Math.sin” and “Math.cos” and “Math.PI”
Update after we move the car.
Draw the car using the polygon.
Add a damaged property to the Car class and an “assessDamage” method
Implement a “polysIntersect” utility method that uses the “getIntersection” function. Here comes the modulus operator.
If the car is damaged have it change color.
Don’t have the car move too fast or this implementation will not work.
If you zoom the intersecting is not perfect but works.
Simulate traffic by adding an array of cars to the main.js file.
Tweak the animate function to draw with a for loop.
Add support in the Car class for “DUMMY” cars that have some very simple behavior.
Pass this to the Control class constructor as well.
Set a “maxSpeed” for the Dummy
Remove sensors for the Dummy cars. Conditional in the Car class constructor to see if “DUMMY.”
In the main.js file, where we call the “car.update” function, pass the function.
Prevent the assessing of damage for the traffic cars.
Change the color of a traffic car and update the sensors to handle traffic.
We don’t want to do the driving of the car ourselves.
Artificial neural network simulate the neurons in our brain. We have 86 billion neurons.
Include a new “network.js” file with a Level class.
The class has inputs, outputs, biases, weights that are all arrays.
Create a “Randomize” static method, static so that it can serialize (methods do not serialize).
Why negative value for biases?
Both weight and biases will be between -1 and +1.
Create a “feedForward” static method.
This code will work, but basic.
A quick story aside about why the -1 to +1 range. Also the hyperplane equation. And the plane equation. Linearly separable versus versus non-linearly-separable Libraries like tensorflow. Sigmoid function
In the “network.js” file, define a “NeuralNetwork” class
Instantiate the Neural Network class in the Car class.
Let the car make it’s own decisions.
In the Car class add a “useBrain” property.
The networks are random.
Debugging in the console is really hard with this setup.
In the “index.html” file, refactor things a bit. Duplicate an update the canvas id to include both a “carCanvas” and a “networkCanvas.” We are going to visualize the neural network in the new canvas. Do the same in the CSS file. make the netwrok canvas black.
In the “main.js” file, instantiate this new canvas and update the existing canvas by using right-click “rename symbol”.
Refactor the “ctx” to have both a “carCtx” and a “networkCtx”
Make the network canvas take up the full height same as the car canvas, also a little wider than the car canvas.
Copy in a “visualizer.js” file and leverage this with “Visualizer.drawNetwork()”
The visualizer supports animation so you can pass the animate function a time argument. We slow it down a bit by dividing time by 50. And now we have a visual reminder of what algorithm the model is using.
Parallelization – have many cars run simultaneously.
To the “main.js file,” create a “generateCars” function that takes in the number of cars, N.
Handle these new cars by drawing as part of a loop.
It’s a total mess. So draw them semi-transparently.
Emphasize one car by drawing again with maximum capacity and only draw the sensors for this car. So pass draw a third argument.
We want to focus on the best car. The one with the minimum Y value using “Math.min()”, the spread operator, and a map function.
Write code to save a good car in local storage. Serializing the “bestBrain” in local storage. Get rid of any existing one as well. Don’t forget “JSON.stringify()” and “JSON.parse()”
Use emojis as button icons to save and delete a car’s brain. Style the body with flexbox. Add some styles for the buttons. Now when we refresh the page we are running the car on the stored best brain.
Could be automated to continuously run and store the best one. But which one is the best. We are using the “y” minimum but we can maybe leverage other “fitness functions”
Add two more traffic cars.
But we need to go to “network.js” and add a static “mutate” method to mutate the network to try and find something close to the best car but still randomize a bit.
For each car have it’s brain pull from local storage and for every one but one use this new “mutate” method. We are using the existing best car to act as a starting point and modifying slightly to see if we ca do better. And we do so we can make this the new best car and try again and again.
Add some more traffic. You can ramp up the number of parallelizations from 100 to 1000 if your computer can handle. Ultimately you want to find a car that can get through all the traffic and then you can move N down to one and see the car move right through. Incredible.
There are more sophisticated genetic algorithms out there. For example, crossover takes two networks and mashes them together somehow.
On his channel he shows how to draw a better car in PowerPoint?