Hello and welcome to what today will be a 15 minute coding challenge! I am going to attempt to program, from scratch, in JavaScript, the snake game. I don't think I've ever done this before, perhaps I have I've certainly made stuff that resembles it. But if you're not familiar with the Snake game, pause this video, Google it, play it - I think you'll find it. Okay, so let's just get started. I kinda want to spend some time getting to know you But I can't because I only have fourteen minutes and thirty seconds right now. So the first thing I want to do is—and I'm in a JavaScript framework called p5js— And p5js requires a setup function and a draw function. And the first thing I'm going to do is just make a canvas. That is, I dunno, 600 by 600 pixels. And we're going to go over here to look at this page There we go, there's a canvas there. It's empty. Boy, I've got to move faster than this. And, now we're going to give it a background. I just want to make sure things are up and running. So, I've got a canvas. Maybe I need to move this over, it's kind of under the timer but whatever, you get the point: There's a canvas. My timer is in the way. Okay, that's fine, it'll get better next time. So, Uhm— So, let's see what we want to do here. So, I want to do this with some object-oriented programming. kAnd I'm going to write something called a constructor function I have some tutorials about constructor functions if you don't know what that is. And what I'm going to do is make an object that has an x and a y And I'm also going to keep track of an xspeed and a yspeed. Because, what I want— and let's just make this one What I want is for this snake object to have some functions Like an update function And what happens in that update function is that x value simply changes by the xspeed value And the y value simply changes by the yspeed value Now, I want another function And I'm going to call it show Because what I want is for... Uhhh, I'm going to draw—
I know, I'm going to draw a rectangle A rectangle at this dot x, this dot y that's, I don't know, 10 by 10. And, I'm going to make that rectangle white. So, the point of this is, I want to have this simple object. It's a Snake object, it has an x and a y, an xspeed and a yspeed it's going to move around the window And its x and y move based on that x- and y-speed At the beginning of the program I can create a variable, I'm just going to name it s Maybe I would want to name it snake in a more normal day of my life But this is a— a little bit of a rushed little project here, so I'm just going to call it "var s" And I'm going to say s is a new Snake And... Uhmm... Then I'm going to say s dot update And s dot show And I might want to move this Snake object to another file, JavaScript file, at some point But I'm not going to worry about that too much. So, I'm sure we got some stuff wrong Well, you can see— Hey, look! There it is; there's my snake. It's moving across the window. Bye Snake! No, come back! No, go away! Okay, whatever So, now— So, that's pretty good. Okay, what happens in the Snake game? I think if you press the up arrow it goes up, if you press the down arrow it goes down left arrow— so we can add something in p5 We can add a function that's just sort of like a global event. called "keyPressed" And I can say I think it's key equals UP_ARROW Oh boy, I hope that's right. Oh, "keyCode" I think with these you want to check a built in variable in p5 called keyCode Then I want to say to my snake— I'm going to set a direction like move Zero along the x axis but negative one is up So you can see kind of this idea that I'm doing here, which is— Boy, I hope these are right. —which is this idea that the snake is going to have a new function That's going to allow me to set its direction And if I press up, I want to set its direction to zero, negative one Down: zero, one And then, I also want to do right and left. So let me quickly copy and paste this two more times. And I need a RIGHT_ARROW and a LEFT_ARROW And, if it's moving to the right its x is one and its y is zero. If it's moving to the left its x is negative one and its y is zero. So, of course, this isn't going to work now. We can run this, and I can start hitting keys, but it's going to say "s dot dir is not a function" So, I made up this idea of a function called dir for direction But I need to actually add it to my object And you know, let's be ambitious here Let's take this object and let's make a— This is a little bit dangerous what I'm doing here Let's make a seperate file. I'm going to call it snake.js And paste it back in there. And in my index.html I am also going to add a reference to snake.js This just allows me to organize my code a little bit differently. So, I can have sketch.js that just has setup, draw, and keyPressed And, I can have snake.js which is just the Snake object Oh, my timer thing is in the way of my code a little bit! I've got to work on this more. Okay, that's a little bit better. So, you can see, this is all the code—the constructor function—for making this kind of object. But, what was the point of doing that? I need to add another function called this dot dir And the point of that function was to receive two values: An x and a y And to use those two values to actually set this object's direction. So now, if I receive an x and a y, that's what the xspeed and yspeed become And if I go back to sketch.js, I can see here— There we go That's what I'm doing here. Okay, I got worried that my audio stopped working. But it seems to still be working. I feel like I'm a little bit loud today, let me turn this down. I shouldn't be doing this live in the middle of a video But, whatever. Okay, so now if I do this, we should see— Press down, press to the right, press down, press to the left You can see— now let's do something here Let's think about this for a second We want the snake— One thing we need to think about here and there's lots of different ways to implement this but the canvas, we really want to think of it as a grid Because the snake is actually going to be a large rectangle and as it moves it's going to move to the next spot in the grid, the next spot, then it moves down to the next spot I want some sort of variable to keep track of the size of this grid. I don't know what to call it, I could call it r Let's just call it r, let's call it scl to stand for scale I don't want to call it scale because scale is a built in function in p5 So, I'm going to come back over here And I'm going to add a variable, I'm going to call it scl for scale And I'm going to make it 20. And then, actually that's— it could be— it's a global variable And in the snake, I want to have everything based on that So, I want to move by the xspeed— xspeed and yspeed are really just the direction And then I want to draw the sname at that scale And now, if we go back to the browser And I hit refresh— woah it's so fast So— a couple things, let's just add some code really briefly to constrain it this dot x equals— in p5 there's a functon called constrain Where I can say "Hey, take this value x and constrain it between zero and the width." And, you know what, the width minus scale Because, I don't want it to actually go off It's a rectange drawn from the top left. And, uh, this dot y I want to constrain it to the height minus scale. How're we doing? We've got seven minutes left, we're getting somewhere. Okay, so now, we can see that, this is kind of working This is ultimately going to be my snake You know, I might not like what I'm about to do next in most cases but I think it's going to be fine to just really reduce the framerate. Because the snake game is kind of this low-res throwback thing So, I'm just going to globally in p5 in my setup I'm just going to say frameRate, and I'm going to say ten And, now I'm going to refresh it again And, you can see, this looks sort of like what the snake game might be Okay, so, we've got that I can now press the arrow keys and move it around What are the things that are missing now? In the snake game random bits of food appear When a bit of food appears, the snake tries to eat that food. When the snake eats that food, it gets a little bit longer And if the snake ever runs into itself, it dies Oh boy, that's a lot of stuff to do in six minutes Let's get started! Okay, so the first thing I want to do it, let's add a piece of food. So uhm, I want to— oh boy this'll— fine. fine! It's fine! It's fine! No problem, no problem, no problem. I kind of want to make a food object, but we don't have a lot of time maybe I'll refactor that in later let's make a uh— Let's make it a vector. So, I'm going to use createVector which is a quick and easy way to store both an x and a y Of course, I didn't use the vector in the snake, but whatever, it's fine We can be a little messy for fun today createVector, and I'm going to give it a random spot... in the window. And I'm going to, in draw, I'm going to— I'm going to make my food a nice purple or pink color. I'm going to draw food dot x food dot y and I'm going to make it the same size And now we can run it, we can see there's my food. I want to come and get my food now It's not exactly on the grid So, I should probably deal— let's make this correct. So, what I actually want to do in terms of its location is— I'm going to make a function called pickLocation And what that function is going to do is— First we need to know: what are the number of columns? So, it's the width of the window divided by that scale. Then the number of rows. Which is the height of the window divided by scale And then now, I want to create a piece of food that is [in] a random column and a random row And I also want to use floor with both of these. Oh, boy. This is getting to be a very long line of code here. But— so what did I just do? I wrote a function that picks a location because I want it to have— I want it to be only one of these spots in the grid So, I have to divide— this is zero, one, two, three, four Zero, one, two So I need to divide by how big that scale is to pick on of those integers And then, I actually need its actual location I want to multiply it by that scale to expand it back out. And the floor function is everywhere because I need these things to be whole numbers for this to work well So now, you can see, this should— the food— Well, that didn't seem to work! Are we sure I did that correctly‽ Ah, that's right— Eh, it's a little bit off I'm not going to worry about that right now Somebody will, uh, uhm— Somebody will correct me— correct this later. It's close enough, I'll fix that later because I only have four minutes left So, what I want to do now is figure out, uh I want to write a function s dot eat the food I want to do something— I expanded myself by five minutes today. Clearly that's not enough time. I want the snake to eat the food So, uhm, in the Snake object I need to add another function called eat Technical difficulties, but I'm back! And what I'm doing right now is I'm writing the function where the snake eats the food So, I need to check if the snake is at the same location as the food. And, okay, while I was having technical difficulties I realised I'm missing something important Which is, we have this whole pickLocation function but I didn't actually call it up here I need— the food's location to be at this thing that I spent all the time doing to make sure it's on the grid So, now that should solve the problem where the food isn't perfectly aligned in the grid with the snake. But let's go back to where we were: snake dot eat So, in the Snake object, this means, I need to write a function called eat and this function is going to receive a position, a vector, for where that food is and, I could check if they're in exactly the same space But I'm pretty sure that if I just use the distance between where the snake actually is (x and y) to where the food is (x and y) and if I just check that that distance is less than one pixel I should probably check two pixels or three pixels, but, whatever. I'm going to say return true. In other words, I want this function to tell me whether or not the snake reached the food And, if it's true what do I want to do with this information? So I want to come back here and say If the snake eats the food then I want to... what? Pick a new location Oh, you know what I realized? It's not— food does not equal pickLocation This function I wrote just sets the location for that food itself So I can just call pickLocation again, it's not returning anything Boy, my mind is getting fuzzy toward the end of these videos with only two minutes left. Let's see, I think we're going to be really close now. So, now we should see— I'm going to move Come on, be on the grid. Look at that, it's right on the grid! And I can eat that piece of food and it will go somewhere new This is fun to play. I don't have time to play this right now. I've got to keep moving. Okay, so what do I need to do? If it gets the food, it needs to become longer How do I keep track of a snake that has a length to it? Well I need some sort of array some sort of variable to keep track of how long is that snake So, let's go back to the code. We've got to go back to the snake object. And let's add something called this dot total, and that equals one. Because, the snake— And, you know what, I'm going to do something a little bit goofy. Which is that I'm going to keep the x and the y as the current location And the array that I'm going to store, the length of that array, is just its history. So, at first its zero as it only has its current location and the rest of it This is actually going to be useful later I just realized. And, I'm going to create another variable called tail which is going to be an array So, one thing for sure is If it eats the food, total should go up by one So, total should go up by one. Now, as it moves... I want to have a loop What goes in this loop? I definitely want to loop through that total And, actually, I want to do all of this right before... Right before it moves, oh boy. I kind of need to work this out, I only have thirty eight second but I need to work this out a little bit in my head here So, if this is the array and this is the snake's current location Maybe what I want is to put its current location in the last spot and shift everything down, the history down So, this is where it was four moments ago Three moments ago, two moments ago, one moment ago. That'll do. So, the first thing I need to do in this array is shift all the spots. So I really want to actually loop through total minus one And I want to say tail index i equals tail index i plus one Now, I'm sure my time has expired now, We're going to see how long this takes I'm going to click a little window here, say okay, and it stops at one second And it's going to say time expired And you know what— we'll leave that there. You'll see how long this video is and that's how long it took. Okay, so this is going to shift everything over by one And then when I'm done, I want the very last spot Which is tail [index] total minus one to equal createVector... this dot x, this dot y So this now allows,— that's its current location and then it can move And so now, when I'm drawing it in addition to drawing the rectangle at its current location, I need to also say Draw all the rect— draw its tail So, I want a rectangle at tail index i— And I realised i've made a classic JavaScript error Classic JavaScript error. I've got this thing called tail, which is really important But, it's this dot tail tail is part of the snake! So everywhere I put tail, I forgot this dot tail This happens to me all the time This is like, the bane of my existence Oh, and this dot total also! Oh, boy So, I think I probably— I'm looking around, this dot tail this dot tail, this dot tail, this dot tail this dot total, this dot total I should just have a big blinking sign in front of me that just says "this dot" "this dot" "this dot" "this dot" Okay, let's run this and see if this works now. Hit refresh, I don't know; I'm sure I missed something. Come on, eat that food, argh! total is not defined on line 12 So, there's a place that I forgot it line 12 this dot total plus plus let's hope we didn't miss— Okay, I've got to— I'm trying to look at the camera to make eye contact with you And, oh look at that, the snake is— Oh! Cannot read property x of undefined! snake.js:42 Okay, it extended by one But how come when it extended by two it didn't work? Line 42... This seems totally reasonable to me So what happened? This show is always happening... Okay, let's think about what order all this stuff is happening Maybe that's the issue? Oh boy, we've really got— This video is going to be a little bit long... because I'm going to debug this now Back again Little bit of splicing and dicing here but I'm going from where I— I'm going from where I want to add the tail of the snake So when the snake eats the food, the total goes up. total is keeping track of how many bits of history of the snake's length do we have left? So, I know what I want to do is: Any time this dot total does not— Okay, so one thing I always need to do, is I always need to shift and I actually have a diagram I'm just going to add this in here What I need to do here is— I always need to shift... ... the tail down I need to shift everything down in the history So, I'm going to say: this dot tail index i equals this dot tail index i plus one This is going to shift the spots down as the snake moves All of its spots shift down so I can get the new spot in the end of the array Right? Because, what I want, is to then say: this dot tail... ... index total minus one... ...equals... ... the new location So, I have this array that always shifts down And I get— I have this array that always shifts everything back and then the new spot goes in the end So, let me explain to you what I mean I have this diagram from my Technical Difficulty part But, err, I'm over here now Argggh Boy, everything is falling apart today Come on over here, button. So, in other words, I have this array Let's just imagine for a second that this is the history of where the snake has been. So, the new location is going to go in the end and all the other spots will shift down, and the oldest location will be deleted So, this is what I'm doing here Now, if however— The, uhm— If, however, the snake has eaten a piece of food It should actually get bigger and the new location should go over here So, if you think about this, what I'm saying is there's two different possibilities: If this dot total equals this dot tail dot length, meaning that no food has been eaten, so the total is the same as the existing array length then just shift everything over And give me my last spot. Otherwise, only put the new spot in and I don't need to shift So, in that sense this can go over here So, the shifting, this is a small tiny nuance but the shifting only happens— The shifting only happens if we haven't eaten any food because the new spot has to go in here, and everything has to get shifted over If we've eaten food, we just add a new spot on And now— oops, I'm tripping over. Now, we should be able to run this and see— Ah, but you know what? I need to add to draw— I need to also— In show— In the show function, I need to also draw a rectangle where... ... where everything is Oh, I'm losing steam here today. Uhm, okay, so I need to draw a rectangle at: this dot tail index i dot x and this dot tail index i dot y All these will be white. And, I was mucking around with this, let's make this 20 Refresh, oops! total is not defined! Ah, I forgot the "this dot" Err, and somewhere here... Erm, under line thirty this dot total - classic JavaScript error! Always forgetting the "this dot" Okay, here we go, now run this again. Come on, I'm going to eat this piece of food and my snake is going to be two rectangles long I'm going to eat another piece of food It's three rectangles long! Another piece of food... It's four rectangles long! Guess what now Now we need to figure out: When should the snake die? Now, I should do something if it hits the edge But, I'll add that in another day What I'm going to do right now is if the snake hits part of its body then it's dead So, this is something we can really just write a new function for this dot death I don't like the word death it seems so— Ah well, fine, we'll just use it as that's what it is. So, what I need to do is: Loop through every spot in the tail And remember the tail does not include its actual head Err this dot this dot tail dot length And, what I'm going to check and see is: I err— I'm going to look at each position which is this dot tail index i and let's look at the distance Again, just like how we checked if it ate the food Let's look at the distance between the x location and that particular spot on the tail If [the] distance is less than one Like if it's really gotten there Really you want to check if they're exactly equal but I'm just— If it's less than one then: I'm going to set toal back equal to zero And this.tail back equal to nothing So, we're just going to have it go back to nothing So, this is now checking If it's— if— If its front location has ever— right? If it's— If whatever is in the new spot is connected with any of these And we need to make sure we do this before we shift anything in because we don't want its current spot to have actually shifted into the array So, now we should be able to go back to sketch and I should be able to add right up here After update— let's do this— Yeah, I think— I don't think it should matter but I'm going to check it before I update it That makes sense, right? Uh, actually, do you know what I think I need to do? Is, I need to check it— No no no no no, this is no good This is actually— We've got to be thoughtful about this I actually want to check it when? I want to check it right here the moment x moves to the next spot Which is fine, because I want to check it— It does this after and then I check and then I do all this shifting So, that should actually be fine So we're just going to say "s dot death" right here And we're going to run this Now, I've got to get it to be a little bit long So I have to play this for a little bit and get good at it Uh-oh. Oh! Big problem! I can't hit the edge, hitting the edge is very bad Let's start over and let's just— I'm going to deal with the edges later, let's just not hit the edge Okay, I think once I'm four—length four— I can probably Ahh, go backwards Oh, no no no, I just died there Because I went backwards Okay everybody, hold on Let's— Let's— Let's have a little bit of a— Let's actually— Let's go back to this. Let's console log "Starting over" There's a really easy way to test this: Which is just to be able to go backwards, which I should probably protect against. So let's be— Let's be a length one... two.. Yeah, you can see I started over because I just went backwards So, we should probably not allow the snake to ever be able to go backwards So I'm going to add something in right now which is— I'm just going to add mousePressed And I'm going to say s dot— I'm going to sort of cheat By just saying, every time I press the mouse I'm going to add as if I'm eating food so we can really test to make sure this is working So, I'm just adding a lot of spots An now, you can see That works So, if I ever intersect one of my existing spots I have to start over So, there's— I'm going to end this now because we kind of have the complete game basically We have the snake object. It has a few bits of functionality, right? It can check to see if it found a piece of food by checking the distance between it and the food If it is, it increases its size We can move it. Which means it keeps track of its history shifting everything down It moves to the next spot and its keeping itself on the screen and it's also now checking to see if it's intersecting with any of its previous spots So, what's missing, that I will add later after the fact when you look at the code for this, Is it's missing: Number 1 is it shouldn't be allowed to go backwards. Maybe it should be, but you know, you as the user of the game should just know not to do that. And also, I need to deal with the edges. So I should also add something in this death function that if you hit an edge, maybe, of the window You also— You also, kind of, lose your life so to speak Which actually, I just realized is happening because of this constrain Because constrain keeps it in the same place as it was previously Which means it will then figure out that is— That it's intersecting where it was previously and call starting over So that's actually working. Okay, this— Uhm— So, uh— And, scene!
This reaffirmed my hatred of javascript
I can not handle this level of enthusiasm.
This guy is awesome, got lost after 7:30 but it was still super fun to watch.
I made this game in my high school programming class! Took me two weeks...
Just use mm:ss for the fucking timer!
As someone currently in school learning Java, I feel like I'll never be an actual programmer :(
Is this not a 100% rip off of this woman's live-code talk?
https://vimeo.com/105955605
I'm gonna hire this guy
I love that he decided to pause the video so he could do some serious debugging. Also, that p5 library looks pretty awesome.