Placeholder Image

字幕列表 影片播放

  • COLTON OGDEN: All right.

  • Hello, world.

  • This is CS50 on Twitch.

  • My name is Colton Ogden.

  • Today we're going to resume what we did last week with Minesweeper.

  • Prior to today's stream, we had a lot of awesome language chat

  • in the, uh-- well, language-- we had a lot of language

  • discussion in the chat, a lot of folks from different parts of the world.

  • Going to shout out--

  • [INAUDIBLE] long discussion, here.

  • Shout out to everybody who popped in early.

  • So let me just make sure I'm going to the right place.

  • [INAUDIBLE]-- regulars, of course.

  • Acid Jack, who said "Greetings from Germany,"

  • and we had a little bit of a German discussion.

  • Learned that "nabend" means "guten abend."

  • It's a shorthand version of that, which is awesome.

  • Who else do we have, here?

  • We had-- let me scroll down--

  • [INAUDIBLE],, from Uzbekistan-- so, some representation

  • from Uzbekistan, which is awesome.

  • We have, in addition to that--

  • Not Sure, of course.

  • We have [INAUDIBLE],, representing the Hindi language-- didn't

  • specify the country, but Hindi.

  • [INAUDIBLE] namaste.

  • [INAUDIBLE] actually was the one who said "namaste," so, apologies.

  • And then [INAUDIBLE].

  • And speaking of [INAUDIBLE],, happy belated birthday, [INAUDIBLE]..

  • We missed saying happy birthday to you on Friday.

  • I don't remember if it was Friday that you were sick

  • or Thursday that you were sick.

  • Hope you're feeling much better, but happy birthday.

  • Happy belated birthday.

  • Sorry that I missed it on the actual day itself.

  • Hope you had an amazing birthday, and hope you're feeling fantastic again.

  • Mission Vision-- greeting from Austria.

  • There we go.

  • A lot of European representation today, which is awesome.

  • We're going to find a list of upcoming Twitch broadcasts.

  • That's facebook.com/CS50.

  • They're all listed as events and will also

  • be listed as video, live video events, in addition to that.

  • People are talking about where they've been to in Europe.

  • So, awesome.

  • I think we're all caught up.

  • We had a lot of awesome prestream chat.

  • But, yeah, we're all set.

  • So last week what we did--

  • I'm going to switch to my computer, here-- boom.

  • We talked about the game Minesweeper, and we implemented a similar,

  • beginning version of it-- not the full game, not the whole gameplay loop,

  • but it looked something similar to this--

  • If I can get this working.

  • So this is just the grid, kind of by itself.

  • The last feature that we implemented actually

  • was being able to highlight a given rectangle on the screen,

  • so that visually you can see where you're trying to click on a given

  • tile-- which is pretty important.

  • You know, visual feedback like that.

  • Subtle little things add up.

  • So the next thing, I guess, the next big feature, would be--

  • and probably going to be the majority of today's actual--

  • the more challenging aspect of today's stream

  • is going to be the recursive sort of reveal loop that goes on

  • and, when we click a tile, showing all the tiles that we--

  • assuming we haven't clicked on a bomb, which would be unfortunate.

  • But if we haven't clicked on a bomb and we

  • click on a blank tile or a numbered tile, if it's a numbered tile

  • we just reveal the number.

  • If it's a blank tile, we reveal all the blank tiles that are adjacent to it

  • and recursively keep revealing tiles until we reveal numbers or blank tiles.

  • Right?

  • And so that is the gist of what we have to implement, today, the core of what

  • we have to implement, today.

  • Nice things to also implement, per what some folks mentioned last time,

  • would be flags.

  • So, in the actual game, you can click on a given tile

  • to indicate that it's a bomb, that you've

  • predicted that's a bomb, based on the neighboring numbers and whatnot,

  • so that you don't accidentally click on it in the future.

  • Which would be unfortunate.

  • We can even make it such that, if you even try to click on a flagged tile,

  • it just won't let you do that, if it's flagged.

  • It'll just be completely error-proof, in that way.

  • And then having a game-over screen, if we do click on a bomb-- so,

  • you know, showing that we've lost, showing how many points we had,

  • and then maybe allowing us to restart the map.

  • So these will be all features that we could try to implement today,

  • as we go along.

  • And probably the biggest piece would be the reveal loop to the game.

  • So that's probably what we're going to start with, today.

  • I think if we bite that off first, everything else will be kind of easy.

  • [INAUDIBLE] says "Hello from Gujarat, India."

  • Awesome!

  • Hello.

  • Thank you for joining us.

  • We got some--

  • [INAUDIBLE] I'm not sure what language that is.

  • [LAUGH] [INAUDIBLE] says "genuinely blessed."

  • That's awesome.

  • Well, again, apologies we didn't wish you a happy birthday on Friday.

  • It would have been much better, but it was indeed Friday, right?

  • If I'm not mistaken?

  • But, yes, happy belated birthday.

  • OK, cool.

  • So this is the game I'm going to.

  • So, again, we had some little sort of debug output,

  • at the bottom of the window, there, the x-y of our cursor,

  • the highlighting tile, whether it was true or not,

  • and then the actual grid index.

  • And that's kind of a common thing, in games.

  • You know, it's not always super-easy to write to a console,

  • you know, to write to a terminal like this

  • and debug your game at the same time.

  • It makes sense, a lot of the time, to actually integrate

  • the output of your game, the debugging output, into the window itself.

  • So, in this case, this is actually drawn text,

  • using a font, just like we would have drawn anything else.

  • And it's not CLI output, in the normal debugging sense.

  • This is a different type of output.

  • But it works better, and it allows us to more instantaneously see things, too.

  • Because we were making a change every time we move the mouse,

  • and doing that through the CLI would get ugly and weighty.

  • It's just much easier.

  • You've probably seen it in many games that have a debug menu or a debug mode.

  • And that's kind of the purpose of that, to actually integrate the debugging

  • output into the executable-- make your life a lot easier,

  • if you're testing things.

  • Games like Skyrim and Fallout actually let you examine variables at runtime,

  • when you're playing the game, through a special integrated console.

  • And that's cool, too.

  • And that gives you a lot of freedom.

  • You can actually mess with the game objects

  • and do all sorts of really crazy fun stuff.

  • Let me just make sure that I haven't missed anything in the chat.

  • Cool, cool.

  • All right!

  • Awesome.

  • Well, I'm glad we have a healthy number of folks

  • already, so let's get implementing the next feature.

  • So what we have so far, again, is this grid of squares.

  • And my goal is, when I click any of these squares, I want it to--

  • the first step, the very first thing that we should do,

  • is we should just reveal it.

  • Right?

  • So just set Reveal to True.

  • And that should be pretty easy.

  • Right?

  • So let me just go over to--

  • it should be in the game grids.

  • So, if we recall, the game grid had an update function,

  • and in the update function we essentially had--

  • we were checking the x pause and y pause of the mouse.

  • And that's where these two variables are, here, up top.

  • The x pause and y pause both get returned from the push:toGame function,

  • which basically gets the love.mouse.getposition function.

  • So this returns our actual mouse in its native pixel size--

  • so, in our 1,280-by-720p window, it'll give us the 1,280-by-720p pixels,

  • the coordinates for our mouse, and--

  • But push, remember, it's a virtual resolution,

  • so that actually isn't going to map evenly to our 1,280p-by-720.

  • And so, instead, we wanted to convert that 1280-by-720

  • into this 384-by-216 pixel size.

  • And we do that using the--

  • foo go back to the game grid.

  • We did it with the push:toGame function.

  • So that takes in native resolution and gives us

  • our virtual-resolution x-y mouse coordinate.

  • So you can actually interact with our game

  • and pretend sort of like we're using it at a native resolution,

  • but we're actually just spoofing it, using texturing--

  • using a stretch texture, rather.

  • So, in here, we're already sort of looking for the mouse.

  • And what we want to do is we want to check basically on click.

  • And normally how we do that is we override the love.mouse--

  • I believe it's love.mousepressed.

  • So this function, love.mousepressed.

  • And it takes in a key, I believe.

  • And I'm not 100% sure, so what I'm going to do--

  • and this is what I encourage you to do, as you're developing in Lua

  • and Love2D--

  • is to go to the Love2D docs.

  • So I would type in "Love2D mouse"--

  • uh, what would it be?

  • "mousepressed, I guess-- one word.

  • Because that's the name of the function.

  • You can see, here, it's the first link in Google.

  • Going to click on that.

  • And it actually takes in a x, a y button is touch and presses.

  • Interesting.

  • OK.

  • So why is it returning an x and a y, if it just--

  • interesting.

  • Oh, OK, no, I was getting confused.

  • So these are all values that it gives you back.

  • So, just like-- how do we say--

  • just like the love.keypressed function gives you the key back,

  • so that you can use it in your function, this gives you not only just

  • the button-- so, for example, 1, I think, by default is left-click--

  • Yeah, it says right here.

  • 1 is the primary mouse button, so primary mouse button

  • is usually left-click.

  • 2, being the secondary mouse button, that's right-click.

  • 3, being the middle mouse button--

  • And if you have a mouse with a ton of buttons,

  • you'll get more indices that you can access through this API.

  • But we only really care about button.

  • But if we wanted to, if we wanted to say,

  • OK, is the x and the y within a certain bounds, and have we

  • pressed a certain button?

  • We have access to that in here.

  • But we're not going to really worry about that too much, right now.

  • All I want to do is say "love.mousepressed,"

  • and then I just want the--

  • so, "x, y, button, istouch," and "presses."

  • All right, presses is cool, too, because that actually

  • lets you detect whether or not it's a double-click versus a single-click.

  • And this might be important for certain games.

  • It's more of a GUI feature than anything else, but, I mean, games like--

  • even I've seen it in, like, Minecraft, for example.

  • Like, you double-click a specific block, it does something different

  • than a single-click.

  • We don't care about that.

  • We just care about the button.

  • So what I'm going to do is I'm actually going

  • to do something very similar to what we've done before,

  • with keeping the ability to press keys outside of main.lua,

  • which is a thing that you need to do a lot of the time.

  • So normally you would have this function, right, love.keypress,

  • which takes in a key, and you can define what behavior

  • you want to have happen when a particular key is pressed.

  • But this only works in main.lua.

  • You only have access to this love.keypressed function in main.lua.

  • And so, if I wanted to check for a single key press outside of main,

  • I need a function that I can use globally--

  • a different kind of function.

  • --our streaming server did, so Facebook probably just went down.

  • YouTube and Twitch should be back up, but let me know in the chat

  • if you can hear me.

  • We make sure that we're back up and running successfully.

  • It looks like we're up and running.

  • [LAUGH] We have some people in the chat saying that they can't see it,

  • but I think it's a little bit delayed.

  • Let's just make sure.

  • Cool.

  • OK, so people can see it.

  • Yeah, that's a bug with Facebook.

  • Facebook, for some reason, keeps kicking us

  • off the platf-- whenever we stream to Facebook,

  • we keep getting booted off, essentially.

  • And I'm not sure why, but I just took a screenshot of the bug.

  • I'm going to send it to somebody on our team, and they can take a look at it.

  • Anyway, we're back up and running.

  • What I was saying before the stream got kicked off

  • was that, in Love2D, you have a single place

  • to define key a keypressed callback function,

  • but we want that behavior across multiple files for our mouse.

  • Right?

  • So I'm going to do just that, by first doing a couple of things.

  • I'm going to say love.mouse.keysPressed equals an empty table.

  • And we've done this before.

  • We did this in another stream, with keys.

  • At least I think we did this in another stream, with keys.

  • But I'm going to define an empty table, here, called "keysPressed," as part

  • of the mouse namespace.

  • And this is just my own table.

  • This does not exist in love.mouse but, because Lua's a dynamically typed

  • language, I can add this easily.

  • So I just say love.mouse.keysPressed is equal to an empty table.

  • And then I can say, in here, whenever we do press a button on our mouse,

  • I can say love.mouse--

  • actually, what I should do, I'm going to call this "buttonspressed," just

  • to keep the verbiage consistent.

  • I'm going to say love.mouse.buttonspressed

  • at index button is equal to True.

  • So now, whenever we press a mouse button, at any time--

  • because this is in main, this will happen no matter which class

  • we're in, in which object we're referring to--

  • this table will get updated with--

  • on just that one frame.

  • Because, at the end of update, we're going to clear it.

  • I'm going to say love.mouse.buttonspressed is

  • equal to an empty table, again.

  • And what this does is this actually lets me click a mouse, and then

  • the next frame it'll be gone but we can therefore

  • check, on a frame-by-frame basis, whether a given key or a given button

  • on our mouse was pressed just one time.

  • Right?

  • Because that's the only way that this mousepressed key fires.

  • It was just the one time, right?

  • I'm going to, after that, define another function.

  • So I have to say love.mouse.waspressed, button.

  • And then this is a brand-new function, so I'm writing this myself,

  • and I'm adding it to love.mouse, just like I added the buttonspressed table.

  • In this case, this is a function, a Boolean function,

  • which is just going to return love.mouse.buttonspressed, button.

  • Right?

  • So I can say love.mouse.waspressed 1, at any time, any class,

  • and it will refer to this function, here, and basically check that table,

  • on that frame, if that button was pressed.

  • And if it was, then we did a single-click,

  • and we can check for it anywhere, not just in main.lua.

  • So let me make sure I have that chat open, here, so I can keep up with it.

  • So, sorry that the stream went down.

  • I do apologize for that.

  • Could you also do it by saying "equals False," says [INAUDIBLE]..

  • Um-- in this case, no, because we're going to-- basically this, what this

  • is, here, this return, love.mouse.buttonspressed

  • at index button, we want this to be true, because we're basically saying,

  • was that pressed, on that given frame?

  • "Was" kind of implies a truthy Boolean return value,

  • and so it's kind of ultimately what we want to do.

  • And every other button is going to be false,

  • assuming that we haven't pressed any other button, anyway.

  • And so we only really care about the individual button

  • that we've pressed on that frame-- just the one.

  • We don't want to worry about the other ones,

  • and therefore set them all to False.

  • JL97 says hello.

  • Hello, JL, glad to have you with us.

  • "Hello from New Delhi, India.

  • Is it still worth watching the stream, if I missed the previous one?

  • I really loved CS50X 2018, by the way.

  • So props to the team, for that."

  • I would say, you're going to be a little bit lost, in this stream, I think,

  • [INAUDIBLE].

  • You can tune in and maybe get some stuff kind of passively by watching it,

  • but it'd be better for you to watch the first part and then the second part.

  • So, that way, you have the full context.

  • Optionally, you can download the repo.

  • So I actually have everything added to GitHub at the following you URL.

  • Let me go ahead and write that.

  • Github.com/coltonoscopy/minesweeper stream.

  • So that URL has the code for what we have as our starting point today.

  • So if you want to clone that--

  • and you are already familiar with game programming, at all--

  • then definitely check it out.

  • But if you haven't done any game programming yet,

  • I would maybe watch some of the earlier streams,

  • just to familiarize yourself beforehand.

  • OK.

  • "This is a bit too early for me.

  • Might tune in after the kids in bed," [INAUDIBLE]..

  • I totally understand, [INAUDIBLE].

  • Thanks for popping in.

  • [INAUDIBLE] says "Hey, everyone.

  • Hi, Colton."

  • Hey, [INAUDIBLE],, good to have you with us.

  • [INAUDIBLE] says, "didn't know you had a kid."

  • Um--?

  • "No, I mean, when you put the empty table afterwards,

  • to make it work for only one frame," says [INAUDIBLE]..

  • So the empty table is an update.

  • And we wouldn't really be able to make that work.

  • Right?

  • Because in update, what we're doing is we're clearing the entire table.

  • And when we set a given key or a given button to True or False,

  • that's just for one key in that table.

  • It's not for the whole table at large.

  • So we couldn't really set it to False.

  • It wouldn't really make sense.

  • And, if you're referring to setting this to False,

  • then this wouldn't work on the next frame--

  • if that makes sense.

  • Because, now, love.mouse.buttonspressed would be a Boolean value.

  • It wouldn't be a table.

  • And so we couldn't index into it, with this.

  • If that's what you're referring to--

  • if I'm understanding your suggestion correctly.

  • [INAUDIBLE] says "I lose.

  • Which language is it?"

  • This is Lua.

  • So, if you're unfamiliar, it's Lua.

  • And we're using the Love framework, which is at love2d.org.

  • You can download it right here.

  • Super-awesome game framework.

  • If you're completely new, check out maybe part 1 to this stream

  • or maybe some of the other streams that we've looked at in the future.

  • And [INAUDIBLE] and Indraready5, thank you very much for following.

  • Appreciate it.

  • [INAUDIBLE] says that everything is good.

  • All right, perfect.

  • So, back to where we were.

  • Now, I have this function that I've defined--

  • love.mouse.waspressed.

  • Just to make sure that it's working, I'm going to, in my gamegrid.lua,

  • basically say, if love.mouse.waspressed 1--

  • whoops-- then, [LAUGH] I should say--

  • I'm just going to do a quit, right here.

  • So this means that the love.mouse.waspressed function

  • is globally accessible, and so I can use it from within the gamegrid class.

  • Going to run this, going to just move around and then click.

  • And it does indeed work, so that's great.

  • Now, since we have the ability to click anywhere on our screen,

  • from any class--

  • which is awesome-- we can say, basically, in here,

  • if love.mouse.waspressed 1--

  • because the x and the y position are going to be--

  • we can basically figure out which tile we're looking at, given

  • this loop that we've already pre setup.

  • So we basically are iterating over the width and height of the grid,

  • and then we're checking our x and y position

  • and sort of multiplying where the grids x and y are by the tile size,

  • and also the offsets that we calculated.

  • Remember, we have a horizontal and a vertical offset, to center our grid.

  • And so we're doing a little bit of math, there.

  • And the highlighting tile-- so basically what we can do now is say--

  • let me do it in here--

  • if love.mouse.waspressed 1, then--

  • Right?

  • Because we're in here. we know that we're

  • highlighting a given tile on our grid.

  • We have the x and the y that we care about.

  • I can say--

  • Um, how did we do it?

  • What's the member, here, that I care about?

  • It's grid, right?

  • Self.grid.

  • So I need to go to self.grid, at index y, x, and I need to set this tile to--

  • the ishidden needs to be set to False.

  • Right?

  • Because, by default, it's set to True, which allows all of the tiles

  • to be hidden at first glance.

  • But I want to test revealing a given tile, one at a time.

  • So I can instead say .ishidden is equal to False.

  • And what this should do, if everything is correct,

  • is reveal tiles, one by one.

  • Which it does indeed do.

  • So this is pretty cool, right?

  • Now, it doesn't work quite the way we want it

  • to when we play the actual game.

  • When we play the actual game, if we click on a tile, like this-- a 1,

  • or a 2--

  • any number-- it should reveal the number.

  • Right?

  • That's fine.

  • That's totally expected behavior.

  • But if we click on an empty square, like this, what we want to have happen

  • is to actually recursively reveal every other tile that's

  • adjacent to that tile that's either empty or a number.

  • If it's one or the other, then two different things might happen.

  • If it's a number, then we stop the recursion at that point.

  • That tile doesn't spawn out and reveal any of its neighbors.

  • If it's another empty tile, then that empty tile

  • spawns out to all four directions around it, to check those tiles.

  • And then those will spawn out, and so on, and so forth.

  • And it's like a fill sort of algorithm, in that sense.

  • That's the behavior that we're going to.

  • "Or an edge," says Mojodojo101.

  • Yes.

  • And that's an important part of the recursive algorithm,

  • is it needs to check to determine whether or not any of its neighbors

  • are at the top, bottom, left, or right edges of the screen.

  • Because, if they are, then it shouldn't spawn off--

  • it shouldn't fork off that recursion.

  • [INAUDIBLE]?

  • Mojodojo101 also with the clap-- appreciate it.

  • A little bit of Coke Zero.

  • Anyway.

  • So, because this is the point at which we're actually clicking and revealing

  • titles one at a time, this is a great place for us

  • to actually perform that recursion.

  • Right?

  • We have access to all the other tiles, so we can sort of, kind of,

  • inject into here that recursive formula.

  • Now, what we need to do is write a function

  • that can be called, on a given tile, and then pretty much just

  • have that function call itself for all of its neighbors.

  • Right?

  • So we can do that--

  • we can do that here.

  • So I'm going to make it part of the gamegrid class.

  • I'm going to say "revealTile" x, y.

  • And-- well, the important thing is that we, first and foremost,

  • do what we just exactly did-- self.grid oix, dot--

  • um--

  • So there's a thing, here.

  • So, if we click on a bomb, we want that to have separate behavior.

  • And that's not going to be part of the recursive formula.

  • The recursive formula doesn't reveal the bomb.

  • It only reveals non bombs, and--

  • it only reveals basically clear tiles and numbered tiles.

  • And so, as a result of that, we don't want this recursive formula

  • dealing with that.

  • That's going to exclusively be in here.

  • Right?

  • So that's OK.

  • And then--

  • But what we do want to do is we want to check to see whether or not

  • it's a bomb or a number, because that's going to determine

  • the behavior of the recursion.

  • So I can say--

  • um-- and we can assume that, in here, we'll

  • actually perform the logic for ensuring that we don't go outside the bounds.

  • So I'm going to say something like--

  • let's see, OK.

  • First thing we're going to do--

  • top tile, bottom tile, left tile, right tile.

  • And it's going to look something similar to this.

  • It's going to be self:revealTile.

  • So the top one would be x, and then y minus 1.

  • The bottom tile would be self:revealTile x and y plus 1.

  • Self:revealTile x minus 1 and y.

  • And then self:revealTile x plus 1 and y.

  • And these will only reveal to their direct neighbors,

  • so it won't reveal diagonally.

  • It will just reveal horizontally and vertically.

  • And, in order for this to work appropriately,

  • we kind of have to do a couple of things.

  • So the first thing we're going to do--

  • if this tile is a number, we don't want to do this at all.

  • We're just going to basically reveal it and be done with it.

  • So we can essentially say if self.grid y dot x dot-- and I forget what the--

  • OK, so it's numBombNeighbors.

  • So numBombNeighbors is greater than 0--

  • So, if that's the case, that the number of neighbors

  • that are bombs greater than 0, another thing we have to check is to make sure

  • that's not a bomb.

  • Because if it's a bomb, we don't want to reveal it.

  • Right?

  • That would kind of defeat the purpose of the game.

  • So what we also should do--

  • just to make this a little bit cleaner, I'm

  • going to say local tile is equal to self.grid at y, x.

  • Just like that.

  • And, this way, I can do this.

  • Tile.ishidden, and tile.numBombNeighbors is greater than 0, and tile--

  • and not tile, dot, is--

  • what is it? --isBomb?

  • Yeah.

  • IsBomb.

  • So don't recurse--

  • I don't know if that's a word.

  • Don't recurse if this is a number tile or bomb.

  • Right?

  • And actually--

  • Actually, no, even more importantly, if--

  • uh-- we don't need this.

  • We don't need this at all.

  • Because what we're going to do is we're going to say, if, um--

  • if tile.isBomb, then return end.

  • Right?

  • So return end.

  • You have to say "end," because that's the end of an IF block.

  • But basically this is going to immediately exit if bomb no recursion.

  • Right?

  • No recursion or reveal.

  • And then, after that-- so basically, if it's a bomb,

  • it'll skip doing everything completely in its function.

  • The next step is going to be reveal yourself.

  • Right?

  • Should reveal yourself.

  • Because this means that it's either an empty tile or a number tile.

  • In both of those cases, we want that tile to reveal itself.

  • But we only want to do this for every other tile when it's greater than 0--

  • number-- sorry, if it's, uh--

  • if not tile, uh--

  • no, if it's not the case that the number of tiles is greater than 0,

  • then we want to recurse.

  • Right?

  • So, if we have no neighbors, if it is 0, then we

  • recurse, because, if it has anything greater than 0,

  • then it's a number tile.

  • Right?

  • And so we don't want that to have happen.

  • OK, so that should be pretty straightforward.

  • There'd be one check, in here, before we actually

  • do this, which is going to be--

  • um-- self.revealTile, x, y.

  • There's maybe one more check, in here--

  • Oh, actually, a couple more things we want to do.

  • There's a couple more things we want to do.

  • So, to someone else's point, the walls--

  • first, that's a big problem.

  • "Greater than or equal to 0, or less than or equal to 0," says [INAUDIBLE]..

  • Less than or equal to 0.

  • It's never going to be less than 0.

  • In this case, we want to make sure that it's greater than 0, that we skip it.

  • So, if it's not--

  • we could do, if tile numNeighbors is equal to 0, that would work.

  • It's never going to be less than 0, so we

  • don't really need to check to see less than or equal to 0.

  • We're not going to ever have negative-1 number of neighbors.

  • NACLEric says "This is a vague question, but how do you decide

  • when to use recursion over iteration?"

  • it kind of depends.

  • Like, something like this, where you have

  • sort of like the same behavior that spawns off and doing multiple things,

  • it's kind of a natural place to do recursion.

  • We also did recursion when we did the stream with Rodrigo,

  • for the Game of 15 solver, and we used a breadth-first search.

  • That was also recursive.

  • It's kind of dependent on your data and your algorithm

  • and how you think of the problem.

  • In this case, we are sort of like basically

  • doing the same thing for a bunch of-- you

  • know, for every neighbor tile, and then those all branch off

  • to their own neighbor tiles, and then so on and so forth.

  • So the problem kind of grows, exponentially, in that sort of way.

  • And so that's kind of the perfect place to do a recursive algorithm.

  • It makes a little bit less sense when you're

  • doing something kind of sequentially and it's a little bit more straightforward.

  • I would say that's probably not good for recursion.

  • Recursion's also dangerous in some programming languages,

  • when you're dealing with massive data, because you'll get stack overflows.

  • Depends on whether your language supports tail call optimization.

  • Which just means that it doesn't return the entire stack frame.

  • It, like, merges the prior stack frame and the current stack frame

  • in this interesting way.

  • So it depends, ultimately.

  • But this is a great use case for recursion.

  • Anything dealing with trees tends to be really good with recursion.

  • Yeah.

  • It's not something I jump to very often, but when

  • it fits the problem it makes for a very simple, elegant solution.

  • "Don't you want to show the bomb?

  • Or do you reveal the entire board if you've found the bomb?"

  • says Mojodojo101.

  • Couple of things.

  • So if you click on a bomb, yeah, you will show the bomb and the board.

  • If you clear the entire board, as well, without getting any of the bombs,

  • you will also show all the bombs.

  • So both of those we kind of need to take into consideration.

  • And we will do that, I think, ultimately.

  • "Recursive function is needed when we can

  • reduce a problem to a smaller problem of the same kind," says NACLEric.

  • Yeah, that's a nice way of putting it [INAUDIBLE]..

  • But there's a couple of things wrong with this recursive algorithm.

  • So the first thing is that we're blindly assuming we can go on forever

  • on the x- and y-axis.

  • And so what we need to do is, essentially,

  • make sure that we're not going less than--

  • Like, basically, for all of these four different paths,

  • we want to make sure that we're not going outside of our bounds.

  • So, four y minus 1, we basically want to say, if y is greater than 1,

  • then we can do that.

  • Right?

  • We can do the same thing, here.

  • So, if y is less than or--

  • is greater than 1, and if it is less than--

  • what was it? --grid--

  • what was the constant called? --gridwidth?

  • So, if it's-- or, height-- gridheight.

  • Right?

  • Same thing for the left and the right.

  • So I can say, if x is greater than 1--

  • because we're going to subtract 1 from it,

  • so we'd want to make sure that it's greater than 1--

  • because our tables are one index, so we can't go any less than 1.

  • We can't go to 0, for example.

  • So we have to make sure that our x is greater than 1 or y is greater than 1,

  • et cetera, et cetera, when we actually perform this function, here.

  • And then, if x is less than grid width, we can go up to our grid width.

  • So we want to make sure x is less than it, when we increment by 1.

  • So this should work, as far as I'm aware.

  • Hopefully, I didn't mess anything up too badly, here.

  • And let's try it out.

  • Whoa, stack overflow!

  • Oof.

  • That's rough.

  • OK.

  • And that's the proverbial [LAUGH] stack overflow that I just referred to.

  • So let's figure out what's causing that.

  • That's interesting.

  • "If tile"-- so we don't have a base case, here, it sounds like.

  • Well, I mean we do have the--

  • Hmm.

  • We have to return, if--

  • So what we need to do is we need to also return if the tile is already revealed.

  • So that's the thing, too.

  • So, if tile.isBomb or tile, dot, is not tile, dot, is hidden--

  • right?

  • Because if the tile is revealed, then we should just return,

  • because we've already looked at it.

  • Right?

  • Let's try that.

  • Oop!

  • Got a 2.

  • Ooh!

  • Ooh!

  • Ooh!

  • That's some nice stuff, right?

  • Clicked on that-- got all these tiles revealed.

  • Isn't that awesome?

  • So I'm assuming this is a bomb, which means--

  • oh, man, I don't know.

  • One of these is a bomb.

  • It's hard to say.

  • Clearly this one's a bomb.

  • We know that.

  • We know this one is a bomb, for sure.

  • Oh.

  • This is the tricky part.

  • OK, so this is also a bomb, for sure, because these two are only bordering

  • this tile, which is unrevealed.

  • So that's definitely a bomb.

  • So this is going to show you just how terrible I

  • am at Minesweeper, which is a completely different problem

  • to solve than what we're doing.

  • Oh, we've got a few people that followed.

  • I apologize for not reading that out.

  • So let me go ahead and do that, while we just enjoy

  • this amazing feature we just added.

  • So, Dougie Johns, [INAUDIBLE],, Alex1304, and JabsWithDangerNoodles.

  • Wow, that's a great name.

  • Thank you all for following-- much appreciated.

  • OK.

  • Hoo.

  • OK, got a 3, there.

  • That's a little bit worrisome, right?

  • Let's try this corner.

  • OK, that's a 1.

  • One of these is a bomb.

  • OK?

  • OK, that's all OK.

  • [HISS] Oh, man, this is-- this is stressful!

  • This is more stressful than actually writing the code.

  • OK?

  • All right, all right.

  • Let's make sure that the bombs are still rendering

  • and I'm not just getting lucky.

  • Oh, it won't actually let me do that, OK.

  • Oh, right, because, if it's a bomb, it won't actually let me click it,

  • right now, because it's not part of the recursive algorithm.

  • So that's cool.

  • Oof, this is a bomb, right here.

  • It won't let me click it.

  • 1, 1, 1-- oh, that's a bomb, and that's--

  • OK.

  • All right, cool!

  • So this is great.

  • We have a recursive reveal function.

  • That was very easy, because of recursion.

  • Made it-- it was a very simple thing to write.

  • We didn't take into consideration the base case,

  • which is very important, because this, without this condition, here,

  • it will recursively go over the entire board, every time,

  • and not basically skip the tiles that are empty.

  • And, because of that, it'll just basically over and over again just

  • loop around the board, infinitely, and never terminate.

  • And that's a stack overflow.

  • That's what causes a stack-overflow issue.

  • So let's go ahead and figure this out.

  • So what I want to do is, if I click the mouse, and before I reveal,

  • I basically want to check to see whether or not

  • it's a bomb that was on that tile.

  • And, if it was a bomb, we should probably switch to Game Over.

  • And this will actually be pretty easy to do with game states.

  • We can do this with game states.

  • So we can integrate a state machine, like we did in prior games.

  • So I think we'll do that.

  • I'll kind of copy over the code from a prior stream,

  • and we'll go over it kind of fast, and then

  • we'll implement a game state to allow us to transition.

  • And then we'll start implementing flags and doing all that sort of thing.

  • But what I want to do is at least implement being able to open--

  • being able to detect and click open a--

  • reveal a bomb.

  • So what I can say is, if self.grid y x dot isBomb, then--

  • let's see.

  • What we want to do, here?

  • Basically, self.grid y x dot ishidden is going to be equal to False.

  • I guess that will be good enough.

  • Like, that will at least reveal it.

  • Ooh!

  • There we go.

  • It looks the exact same.

  • Are we not seeding our random-number generator, which

  • should be a very important thing to do?

  • Yeah, it looks like we are.

  • Why is it the exact same grid?

  • That was weird.

  • It's the exact same grid, every time.

  • Why is it--

  • Oh.

  • Oh, that's why.

  • So we're seeding the random-number generator

  • after we're generating our grid-- which is kind of unproductive.

  • So I'm just going to do that right there--

  • make sure it's different every time.

  • Oh!

  • And I revealed a bomb, very first tile.

  • So, congrats to me.

  • I'm an expert Minesweeper player.

  • And I realized we didn't actually set the title of our game.

  • That's kind of an ugly feature.

  • Why don't we do that.

  • So we'll say, love.window.setTitle Minesweeper--

  • Minesweeper 50.

  • There we go.

  • That's better.

  • Right?

  • Very important step-- never forget to set the window title.

  • [LAUGH]

  • Let me make sure that I am keeping up with the chat, here.

  • Oh, sorry [INAUDIBLE].

  • I missed your suggestion.

  • "This is so much fun.

  • We should have watched Colton play Minesweeper ages ago."

  • I don't know about that!

  • "This is how you play the game.

  • I thought it was just clicking on all the 1s," says LJ97.

  • No, you indeed are trying to avoid the mines, the bombs,

  • and the 1s are just a clue that tell you how many neighboring bombs there

  • are to that tile.

  • Right?

  • So, in this case, all of these 1s are bordering a bomb of some kind.

  • So these three, right here, are bordering this tile,

  • so that means that that is definitely a bomb.

  • So, knowing that that is a bomb, I can click on this one,

  • because I know that it can't be a bomb.

  • Right?

  • And so that one is also a 1.

  • And, because I know that this is a 1, from this, because this is also a 1,

  • it means that these two can't be bombs.

  • Right?

  • They can't be bombs, because this one was already bordering this,

  • and because this one's bordering the same one as this one,

  • and we know this one only has one bomb neighboring it.

  • Therefore, this one only has one bomb neighboring it.

  • It's the same bomb.

  • Therefore, these two below it that were also neighboring this one

  • are not bombs.

  • Right?

  • And, because these two are bordering this one, and we know this is a bomb,

  • then we know that these three aren't, and so on.

  • And it kind of goes on and on, and you keep doing that over and over again,

  • right?

  • Same thing for this one and this one and this one and this one.

  • And so you rinse and repeat this sort of line of thinking.

  • So, in the next feature that we would add, which would be the flag,

  • we would mark this one as being a flag, and we

  • would mark this one as being a flag.

  • Because we know that those are for sure bombs.

  • We shouldn't click on them.

  • And, until you get all the flags or the bombs,

  • you just keep trying to deduce what the bombs are.

  • "This is the first time I understood what

  • the empty tile does in Minesweeper."

  • Yeah it's a weird game.

  • If you're not familiar with the rules, it looks very arcane.

  • "Yellow, definitely bomb.

  • Green, definitely not a bomb."

  • "Well, I've just arrived.

  • Your Minesweeper grew so much.

  • Last part I saw was drawing the grid and centering it."

  • Yeah?

  • I know!

  • Isn't it cool?

  • We have this whole recursive functionality in our game, now,

  • so we can click on a tile-- reveal all the ones that are bombs.

  • In this particular instance, this is tricky.

  • Right?

  • Because-- well, I guess it's not terribly tricky.

  • We know that this is a bomb, for sure.

  • So we know that this one can't be a bomb.

  • But now we know that this is a 2.

  • So how do we determine--

  • well, I guess this--

  • this, for sure, we know is a bomb, to be fair.

  • And we know this is a bomb, so this can't be a bomb.

  • So, there, we deduced it.

  • We figured it out.

  • And so both these 2s being here, since they

  • both have these two tiles as neighbors, [INAUDIBLE] on only those two tiles,

  • we know those are definitely bombs.

  • We know that this is definitely a bomb, right here.

  • So this isn't a bomb.

  • This, I'm not sure, actually.

  • So I'm not particularly great at Minesweeper,

  • and so me playing this is going to be more

  • painful than actually implementing it.

  • But why don't we go ahead, now, and talk about transitioning states

  • when we lose.

  • That's an important feature of the game.

  • So I can pull our state machine from the last game we did that used it, which--

  • I think it was Space Invaders, if I'm not 100% mistaken.

  • It was indeed Space Invaders.

  • Awesome.

  • So what I'm going to do is, I have a state-machine class

  • from a prior example.

  • I'm going to copy and paste it into Minesweeper, right in here.

  • I'm going to open it up, so we can take a look at it.

  • So this is a state machine.

  • And the purpose of a state machine-- if you're unfamiliar,

  • check out the Space Invaders stream, where we talked about it.

  • But a state machine's goal is to point at something that's active,

  • an active state, at one time, and only one thing can be pointed to at a time.

  • And so, when we want to change state, we essentially

  • change our pointer to something else.

  • We perform a transition.

  • During that transition, we might do something.

  • Right?

  • And then we have conditions that satisfy those transitions

  • across all of our states.

  • So, if I'm in the title screen of my game,

  • and I want to go to the actually playing the game screen,

  • I need to satisfy the condition for that transition.

  • So that condition might be, user presses the space bar.

  • So user presses the space bar, while I'm in the title screen,

  • transitions over to the play state.

  • So the play state might have several transitions

  • that let me go to other states.

  • Maybe there are two branching states from the play state.

  • Maybe one is victory and one is game over.

  • To get to the victory state, the transition criteria

  • must be, for example, clearing the entire board

  • without clicking on a bomb.

  • Right?

  • Once that's fulfilled, I transition to victory.

  • If maybe I fulfill the criterion of transition

  • or, uh, this click on a bomb, and that's the criterion

  • for transitioning to the game-over state, if that's the case,

  • then I go to that state instead.

  • And those two states have their own sort of spin-off states.

  • Maybe the Game Over and the Victory both have a restart state.

  • And both of their criteria might be press Space bar, press Enter, right?

  • And so, when you fulfill those criteria, you transition to those states,

  • as well.

  • So that's the gist of the state machine.

  • Let me read the chat, and then I'll explain the code, here.

  • "That one's a bomb, for sure."

  • "What is the algorithm"--

  • "Why does the algorithm stop at a number," says Remote.

  • Because when we get to a number--

  • if we look at the game grid--

  • bum-bum-bum-bum-- where was it at?

  • Where are you at?

  • It never executes the actual recursive part of the formula,

  • if it's the case that the number of neighbors is 0.

  • If the number of neighbors is 0, then it skips these four recursive statements,

  • right?

  • Because these four statements are just this same exact function,

  • which itself just calls tile.hidden--

  • ishidden is equal to False.

  • Right?

  • But if it's a number, it's not going to spin those off.

  • And that's why we get that behavior of stopping at the numbers.

  • That's one of the base cases for our algorithm.

  • Right?

  • Hopefully that makes sense.

  • [INAUDIBLE] "Add cheat codes-- the best way to test a game without being good."

  • Yeah, good point.

  • Good point.

  • If one bomb is confirmed, all 1s surrounding the bomb

  • can't have any bomb in their neighbors."

  • "All 1's"-- [INAUDIBLE] yes, correct.

  • BartleD says "Hey."

  • Hey, BartleD, good to have you with us.

  • All right, [INAUDIBLE] excited about state machines,

  • so why don't we talk about what a state machine is.

  • Oh, we talked about what a state machine is.

  • Why don't we talk about the code.

  • So the state-machine class that I typically use, that I teach with,

  • takes in a series of states--

  • and I'm going to go ahead and include that, right now, in my main.lua.

  • So if I go over here, and I say gStateMachine is equal to StateMachine,

  • and it takes in--

  • it gets constructed with a table.

  • Right?

  • And so this table is going to take in a few different states.

  • I want a title state.

  • And so this is kind of interesting syntax, here.

  • What we're doing is we're creating a table with string keys,

  • and each of the string keys points to an anonymous function.

  • That anonymous function, every time we call, basically in our state machine,

  • the change function, which calls basically self.currentstate

  • is equal to self.states at state, and then these parentheses,

  • that will call that function and return it and then add it and make it

  • our current state.

  • So, in this case, if we call change to the title state,

  • it will return a new title state, a brand-new title state,

  • completely initialized.

  • And it'll set our state machine's current state-- remember the pointer--

  • it'll set that to this title-state object that we're returning.

  • Right?

  • We haven't implemented the title-state class, the play-state class,

  • the game-over, victory-state classes yet, but we are going to very soon.

  • We're going to do the same thing for play.

  • Play state's going to return an anonymous play state.

  • Our victory is going to return an anonymous victory state.

  • And the game-over is going to return a game-over state.

  • And these are really the only states that we need, I think, for a game

  • like this.

  • You can envision a high-score table, a high-score screen state.

  • You could do something very similar to that.

  • We're not going to go into that, today.

  • But these are the anonymous sort of objects that this is going to return.

  • So we call-- anytime we want to change our state machine--

  • so, for example, right here, gStateMachine

  • change to the title state--

  • right?

  • The new title state--

  • it's going to-- if we have a current state and currently active,

  • it's going to call the Exit function on that.

  • So that's part of the transition that I told you about, before.

  • So every state has an Enter and an Exit.

  • So, when we want to get rid of a state, we'll call Exit on it.

  • And I usually don't use it for a lot of things.

  • As your games get more and more complicated,

  • you might want to do stuff with Enter and Exit, but in today's example

  • we're not really going to use Exit a whole lot.

  • We will use Enter, and I'll show you why we're going to use Enter.

  • Enter takes in some parameters and lets us pass data between states.

  • And this is great, for example, when we go

  • from the play state to the victory state or the play state

  • to the game-over state.

  • We can tell the user in those states what our score was,

  • from the play state.

  • We sort of pass that data along, down the line, sort of, um--

  • what's the word-- propagates, I guess you could think of it like that.

  • The important line is line 13, where we say the self.current state is equal

  • to--

  • remember that table that we initialized--

  • self.states at index state--

  • which, in this case, state would be this string title,

  • which maps to this string, here, for these keys.

  • And then we actually execute it.

  • We call it like a function-- which, it is

  • a function of storing a function object, with these parentheses, right here.

  • So, again, these are actually storing these functions as data.

  • These are anonymous functions.

  • And so we can call them, just like we can call any function we've predefined.

  • And, as a result of that--

  • Well, first thing we want to do-- we want to do a couple of things.

  • So we've added a new class.

  • So I'm going to, in here, say require source, slash, state machine.

  • And then another thing we're going to need

  • to do, in advance, I'm going to say, require source states BaseState require

  • source states TitleState, require source states PlayState.

  • We want a game-over state, source states GameOver state,

  • and then, lastly, a victory state.

  • And we haven't actually implemented these yet, ourselves.

  • We're going to do that very shortly, here.

  • So, in source, I'm going to create a new folder, called "states."

  • This is how I typically like to organize my states,

  • when I'm dealing with state machines.

  • It's just a little bit cleaner.

  • I'm going to create a base state, first.

  • The base state is important, because the base state is actually

  • what all the other states are going to inherit from.

  • So the thing about the state-machine class

  • is that it assumes that all of these states have certain functions on them.

  • It assumes that every state has this Exit function.

  • It assumes that every state has this Enter function,

  • has this Update function, has this Render function.

  • But if we don't want to explicitly define

  • an Exit or an Enter state for a state, or an Enter or Exit

  • function for a state, then we don't want to go

  • through the tedium of hand-writing empty functions

  • all the time for all of our states.

  • Right?

  • So what we can do is we can instead say, I'm

  • going to create a base state which just has all the functions of every state--

  • all the functions that a state would want.

  • Right?

  • So, an Init function, which is just the constructor,

  • which just basically creates the object--

  • function base state, Enter, which takes in params--

  • now, notice, these are all empty functions.

  • I'm just saying "end," right after them.

  • So they're completely empty.

  • The purpose of this is just to provide a foundation

  • upon which we can create new, more specific states that

  • copy all of these functions into them, so we

  • don't have to write them ourselves if they're empty.

  • It'll just keep those empty versions of the functions for itself.

  • Call Exit.

  • I'm going to call--

  • or, rather, I'm going to define, not call.

  • I'm not calling these functions, yet, I'm just defining them,

  • and they're just empty.

  • And, lastly, "render."

  • And we did this in Space Invaders, so this should all

  • be pretty familiar, for most folks, I think,

  • if you watched the Space Invaders stream.

  • But, if not, that's why I'm kind of going a little bit--

  • kind of elaborating over it, just a little bit.

  • Now, once we have the base state, we can go ahead and define the title state.

  • So titleState.lua.

  • Now, here's where it's kind of interesting.

  • I'm going to say, titleState is equal to a new class,

  • but I'm going to specify something inside the curly brackets.

  • I'm going to actually say, underscore underscore includes

  • equals base state, which just means copy all the functions and all the data

  • that base state has and make title-state versions of those.

  • So, now, for free, I get empty versions of all those functions

  • that belong to titleState.

  • For example, that's the equivalent of me basically saying--

  • basically rewriting all of those empty functions-- like, you know,

  • all five of them, with their specific names--

  • and copying them over here, so they exist without me needing to do so.

  • However, I want to overwrite the ones that I care about--

  • the functional ones that I worry about.

  • For example, the Update and the Render and all those ones.

  • And so I have to do those manually.

  • I have to actually specify how I want that to operate for the title state.

  • Which isn't going to be empty; otherwise, it would be a useless state.

  • But the Enter and the Exit, those I can probably

  • leave empty, because I don't care about them, but they need to exist.

  • The title state, Init--

  • I don't know if I'm going to need anything done in the title state,

  • just yet.

  • I know that I will need an Update function.

  • And I know that I will need a Render function.

  • And so what I can do is I can say, in the Update function,

  • if love.keyboard was pressed--

  • and I'm going to say, space.

  • Let's just say space is our-- well, we'll say Enter.

  • Enter or love.keyboard.waspressed, return.

  • This is to make it usable on Mac and Windows.

  • On Mac, by default, Return is basically the Enter, for Mac.

  • So I can say, if love.keyboard.waspressed, Enter,

  • [INAUDIBLE] love.keyboard.waspressed, Return, gStateMachine change

  • to the play state.

  • I want to go to the play state from the title state.

  • Right?

  • And so then I'll have my separate play state, which has all that stuff.

  • We're going to show how to actually do all of that, in just a second.

  • Let me go ahead and make sure that I've kept up, here,

  • with the chat, which I don't think I have.

  • I think there's a little bit of the chat that I've missed, here.

  • OK.

  • UnitedStatesofLove2D, "all right, I'll see [LAUGH] myself out."

  • Interesting.

  • OK.

  • I'm not sure I get the joke.

  • UnitedStatesofLove2D.

  • What is the joke, there?

  • I apologize.

  • I'm missing-- I'm blanking on that one.

  • "I've been trying to beat Minesweeper for the past 15 minutes.

  • No luck," says NACLEric.

  • [LAUGH] That's part of the fun, you know?

  • The educational stream, replete with, you know,

  • teaching you how to play a complicated game--

  • Minesweeper.

  • It's a bundle deal.

  • "15 minutes?

  • Whoa.

  • My best time is 13 seconds for 10-bomb mode."

  • Interesting.

  • Are you trying to brag, [INAUDIBLE]?

  • Is that a brag?

  • Or are you saying that you're horrible at the game?

  • I'm not sure how to read that.

  • I'm not too familiar.

  • JL97, "Inheritance."

  • Exactly.

  • This is a concept called "inheritance," in object-oriented programming.

  • And we're talking about doing an object-oriented stream,

  • in the future, that goes over a lot of these details.

  • So stay tuned for that.

  • Hopefully we can do that to give folks more of a foundation.

  • "How is this include different?

  • Is it inheritance?"

  • Correct.

  • Oh, NACLEric [LAUGH] "Humble brag."

  • Andre, "Two for the price of one."

  • Anyway, OK, great.

  • So this is how we can sort of isolate behavior in our game.

  • We can take out bits and pieces, different parts of the game,

  • different screens, different states-- however you want to think about it.

  • Certain engines will say they're screens.

  • Certain engines will say that they are states.

  • We typically use "states."

  • We use that nomenclature.

  • So, yeah.

  • If love.keyboard.waspressed, Return.

  • We're going to transition.

  • Let's test that this works.

  • I'm going to say "love.graphics.print, Minesweeper."

  • And I'm just going to print that right in the middle of the screen--

  • or right at the top of the screen, rather.

  • Oh, let's center it.

  • So, 0 virtual height divided by 2 minus 8 minus 8.

  • And then virtual width, center.

  • So this is just the printf function, which takes in a string,

  • takes in a starting point, a height, or a y--

  • a starting x, a starting y, a amount of space to format,

  • and then the format style.

  • So, in this case, we're centering it across the width of the screen,

  • starting at the far-left side, about halfway down.

  • And then I'm going to do "press Enter to play."

  • Same thing we just did, but I'm going to add 16.

  • I'm going to say "virtual width" and "center."

  • And I'm just going to create empty states, here.

  • So, play state, game-over state, victory state.

  • Is that all of them?

  • I think that's all of them.

  • And I'm just going to make them classes.

  • So I'm going to say, um--

  • yeah, because these only do exist for main.

  • Because it instantiates a brand-new copy of each class,

  • each object, and those anonymous functions that we just looked at.

  • So I need to define that these are at least classes

  • that inherit from base state.

  • So I'm going to say VictoryState Class.

  • I'm going to say VictoryState is equal to class,

  • underscore, underscore, includes is equal to BaseState.

  • GameOver state.

  • The less-exciting part of programming, but this is the cost involved

  • in actually engineering your game, your code base-- whatever you're doing--

  • making it look better, making it more extensible, more robust.

  • This is the up-front cost you pay, but you reap rewards down the line,

  • especially as you get larger code bases.

  • And so this is important that we look at this.

  • We don't do this every stream, because it's a little bit weighty,

  • but for something like Minesweeper, for things

  • like the big games that we make--

  • Minesweeper and Space Invaders-- those types of games--

  • it kind of makes sense to illustrate how it works.

  • Right?

  • So I'm going to say, includes is equal to BaseState.

  • Oh!

  • That's not what I wanted to do.

  • I quit VS Code, completely.

  • But thankfully it remembers which directory

  • you were in before you did so.

  • PlayState, class, PlayState is equal to class, includes equals BaseState.

  • And I think we're OK to at least start.

  • Uh--

  • Oh, you know what I'm doing?

  • In main.lua, everything is in main.

  • So it's just drawing everything.

  • So we have to do a few things.

  • So the first thing is, I'm actually going to take-- before we test this,

  • I'm actually going to take everything out of our main.

  • I'm going to put it into the play state.

  • Right?

  • Just-- because we can't really test this whole thing

  • until we've finished kind of building it out and refactoring it a little bit.

  • So I'm going to say PlayState update and PlayState render.

  • And then, in main.lua, I'm going to to open up main, which is down here--

  • and, let's see, I'm going to copy these.

  • So, first step, in the play state--

  • thankfully, we isolated a lot of the behavior of our game to the game grid.

  • And so we don't actually have to copy and paste

  • that code into our play state.

  • We just have to copy and paste where we instantiated the game

  • grid, which was at the top of main.

  • Right?

  • But the game grid itself, the class, contains all the logic it needs,

  • so that can go in any state we want it to.

  • We don't actually have to copy and paste any of that.

  • So, in the Init function of the play state,

  • I'm going to copy these two things.

  • And I'm actually going to get rid of this and put this in main,

  • because I realize, every time we create a play state it's going

  • to reseed the random-number generator.

  • So save this.

  • Bring this back over to main.

  • Put this at the very top of load, just like that.

  • And now, in our play state--

  • Now, there's another couple of things we need to be aware of.

  • So now we're actually using classes to represent our states.

  • We want to make sure that all of the important variables that we were using

  • in main, sort of at the top level of our application,

  • are now member fields of these classes.

  • So, in this case, the play state has its own game grid

  • that it has a reference to.

  • There isn't a global game grid or some game grid

  • outside of play state's definitions.

  • It should be a self.gamegrid.

  • The play state should own every variable that's

  • important to the play state's operation-- in this case,

  • self.gamegrid.

  • Right?

  • So we do that.

  • Let's go back over to main.

  • We need to get rid of grid title-- game grid.

  • I need to get rid of some of these modules,

  • just to keep things a little bit simpler.

  • So these all can stay right in here.

  • This is totally fine.

  • Grid is, I think, no longer used at all, if I'm not mistaken.

  • But is it-- um--

  • yeah, it's not used at all.

  • So we get rid of that.

  • Yeah, same thing with this.

  • Yeah, we're not using grid at all, anymore.

  • So that's old code.

  • That's actually been completely unused, this whole time.

  • StateMachine stays in here.

  • All of this stuff stays.

  • Now, the important thing is--

  • now, instead of gameGrid update, we take this, copy that,

  • put that in the play state, just like that, get rid of this,

  • and say, gStateMachine update dt.

  • And in here, this becomes self.gamegrid update.

  • Remember, because the game grid is now a member of this play-state class.

  • It belongs to the play state itself.

  • If we didn't have self, this gameGrid variable

  • would be defined everywhere-- everywhere in our entire application.

  • Which we don't want that to be the case.

  • And if we made it local, it would only be

  • accessible within this Init function.

  • So, not behavior that we want in either of those situations.

  • And then, lastly, the important thing that we want to do

  • is we want to render our game grid.

  • And that should be in here, right, just like this--

  • self.GameGrid.

  • Save that.

  • And then gStateMachine render--

  • just like that.

  • And we can actually get rid of the--

  • no, we can't get rid of these.

  • These are actually going to be part of our play state, now.

  • So, right in here, just like that.

  • And now, all of this code should work fairly well.

  • So I'm going to run this.

  • Uh-- OK, waspressed--

  • OK, that's on title-state line 12.

  • Let's see what that is.

  • Oh, right, I haven't defined the waspressed function.

  • So we did that for the mouse, but we haven't done that for the keyboard.

  • Very important.

  • So, love.keyboard.keysPressed is equal to an empty table.

  • We're doing the same thing that we did for the mouse.

  • We're adding a new table that gets updated

  • with every key press on each frame, that main manages for us.

  • And this lets us use it in any state, any class,

  • wherever we want to in our application, because it's global.

  • We'll have this global object.

  • Right?

  • So I'm going to say, love.keyboard-- in the love.keypressed function, that's

  • where we get a reference to every single key we pressed,

  • the entire life cycle of the game.

  • I'm going to say love.keyboard.keysPressed key is

  • equal to True, just like we did for the mouse.

  • And then I'm going to define love.keyboard.waspressed key.

  • And I'm going to return love.keyboard.keysPressed key.

  • And then I'm going to say love.keyboard.keysPressed is

  • equal to an empty table.

  • Now, that should work.

  • Works great.

  • If I press Enter, we do indeed go from our title state over to our play state.

  • We performed a transition.

  • So that's all working quite well.

  • We now have the ability to very easily take our game,

  • point it to any number of states however we want to.

  • For example, if I'm in my play state--

  • and this is how we do game over, now--

  • rather, I should go to the game grid.

  • That's the important place for it.

  • If I'm in my game grid, and I go over to--

  • let's see, where is it?

  • It is here.

  • So, if it's the case that, where a bomb-- if that current tile that we

  • clicked on is a bomb, I'm going to say self,

  • dot-- uh, what was I going to say?

  • No, sorry, g, rather--

  • um-- well, we want to be able to show that we're in a-- that we

  • got the bomb all cleared up.

  • Right?

  • So we have to actually do a couple of things.

  • So, for ease of demonstration, the first thing I'm going to do

  • is I'm going to say gStateMachine change to game-over.

  • I'm not going to implement, just yet, the revealing all the tiles.

  • We'll do that after we have the grid sort of revealing itself.

  • But that is something that we do want to do in the near future.

  • And then we also, of course, want to be able to set flags down.

  • That's another feature that was requested.

  • And we have plenty of time left to do both of those things.

  • But now, notice that, anywhere we want to transition

  • from one state to another state, I can just very easily

  • say gStateMachine change to game-over, and that

  • will perform that logic for us.

  • So, in the game-over state--

  • right?

  • Let's go over back to game over.

  • And I apologize.

  • I'll keep up with the chat, here, in just a second.

  • Once this is going to the game over, I'll read over all the comments

  • that I missed--

  • which it looks like I missed a few.

  • So, function GameOver State--

  • let's see-- update and render.

  • So, often, it'll be the case that you don't really need the Exit

  • and Enter but you do need the Update and the Render, almost always.

  • And so that's what we're doing, here.

  • And in the Render I'm going to basically copy what I had

  • in the title state, which is here.

  • Instead of saying "Minesweeper," it's going to say "Game Over."

  • Whoops.

  • "Game Over."

  • And then, here, I'm going to say, if love.keyboard.wasPressed--

  • well, rather, I can just do the exact same thing

  • that I did over here, also here.

  • Notice that there's quite a lot of duplication of code,

  • here, so you could very easily make this,

  • like, a message state that lets you go to the game.

  • Because both of these are the exact same.

  • The only thing that's different is that this string is different,

  • so you could just pass in this string as a Enter parameter, to that state.

  • But, because we're only doing it for two different ones,

  • and because often it would be the case that your game-over, your title,

  • and your victory would all kind of have pretty different messages,

  • we're not going to do that.

  • We're just going to keep it like this.

  • I was just thinking about game over.

  • We could essentially copy the game grid over to game over but just

  • get rid of the input.

  • And then output a game-over message.

  • We might be able to do that.

  • That might actually save us a bit of work.

  • So we'll take a look at doing that.

  • Or we'll talk about passing.

  • That'll give us a nice demonstration on passing data

  • back and forth between states, as well-- large data, even,

  • which would be our grid, in this case.

  • This is not large, per se, but large enough.

  • Let's go ahead and run this.

  • So hopefully I click on a bomb.

  • So this is definitely a bomb.

  • And then I clicked on it, game over, press Enter to play.

  • We press Enter-- brand-new board.

  • It works great.

  • Right?

  • So now we have our game loop.

  • We have the ability to lose.

  • We have the ability to click.

  • We get a new board when we do lose.

  • Everything flows really nicely.

  • Everything's very modular.

  • This is what the play state looks like-- very clean, only 20 lines of code

  • for our play state.

  • Because, remember, we deferred all of the logic to our grid.

  • Our grid actually has all of the logic for doing the recursion,

  • for highlighting-- all that stuff.

  • Right?

  • That's all taken care of by the grid, in its Update function.

  • And so the actual state logic, the actual transitions between whether

  • we're at the title, whether we're playing, whether it's a game over,

  • and whether it's a victory-- which we'll come to--

  • and we need to calculate whether we've gotten to victory, as well--

  • all that part of our game is very clean, very isolated from,

  • for example, the logic of how do we detect whether or not

  • I've clicked on a tile and whether I'm highlighting a tile

  • and doing the recursion-- all that stuff.

  • So, everything's great.

  • OK.

  • Let's take a look.

  • What did I miss, here?

  • "So what's the benefit of using a state machine over an architecture

  • such as MVC," says [INAUDIBLE].

  • MVC is a bit different.

  • So you could think of a state machine as kind

  • of being a potential layer on top of MVC.

  • MVC-- games are already kind of like MVC.

  • I mean, you have entities that get rendered, every frame.

  • And they get updated, every frame, which would be your controller, right?

  • But, with games, you have to go a step beyond that

  • and you transition between different layouts, different scenes,

  • different modes of presentation.

  • And so games are almost like, if you took MVC and added

  • an extra, extra layer on top of that.

  • So I kind of don't equate them to be the exact same.

  • I don't equate them to be, you know, apples to apples.

  • It's a pretty different use case.

  • [INAUDIBLE]

  • [INAUDIBLE]---- CS50 [INAUDIBLE] [LAUGH] doesn't even know what it is.

  • It's our Intro to CS, here at Harvard University.

  • So we're actually streaming live from Harvard, right now.

  • And David Malan teaches CS50.

  • It's an awesome course.

  • I've been working for David for a long time.

  • But somebody kind of linked [INAUDIBLE] linked in the chat--

  • CS50.edx.org.

  • Definitely check that out.

  • Check out our YouTube channel.

  • We do a lot of videos here.

  • David helps teach a lot of topics with me.

  • So, yeah, CS50 is great course, in my opinion.

  • Really like the culture and the style of presentation.

  • A lot of the folks, I think in here are familiar with it,

  • but we surely probably will attract people that haven't seen CS50 yet.

  • So definitely check out our resources, our assets.

  • We have a lot of content up.

  • Yeah, and to Martin's point, we also have other classes.

  • I teach a games course, which actually just went live today.

  • Our spring version, here at the Extension School, went live.

  • Brian Yu is a teaching fellow here.

  • He teaches the web course, a CS50 follow-on web course.

  • And then Jordan Hayashi taught a React Native course,

  • which went into JavaScript and React, React Native.

  • It was a good course.

  • [INAUDIBLE]---- "This setup of reminds me of the YouTuber code parade.

  • Feels good, man."

  • I actually haven't seen that channel.

  • I should take a look at it.

  • I've seen a couple of coding channels, here and there,

  • mostly VODs that were on YouTube, but I haven't actually

  • paid attention to too many streams.

  • But that's awesome.

  • Based on what you said, there, it seems like a good channel,

  • so I'll take that as a compliment.

  • HippoDefense says "Love" [INAUDIBLE].

  • I like that name.

  • "Nothing like that.

  • This game happened to be a game that I was better at than any other game,"

  • says [LAUGH] [INAUDIBLE].

  • "I can't help to ignore the evil villain face starring in a comedy movie brought

  • by the haircut."

  • [LAUGH] Uh-- FeelsAmazingMan--

  • "You should be an actor, man.

  • [INAUDIBLE] watch you anytime, bro."

  • [LAUGH] Oh, man.

  • I, uh-- if that is--

  • FeelsAmazingMan's not a name of a--

  • oh, is that-- is that, like, a private emote-- the FeelsAmazingMan?

  • But, yeah, I appreciated it.

  • It did sound-- overall, that did sound like a compliment.

  • So thank you very much.

  • All right.

  • Um, what do we want to do, here?

  • "Emoticons by add-ons."

  • Yeah, OK.

  • That's what it looked like.

  • I wasn't 100% sure I wasn't 100% sure.

  • Thank you.

  • Thank you for the compliment.

  • Even though I've been described as an evil villain--

  • hey, you know, it's a role that we need in movies, you know?

  • We have to have good versus evil, light versus darkness.

  • You know, someone's got to do it.

  • Someone's got to take one for the team.

  • All right-- next step.

  • What's the next step?

  • We have the game loseable.

  • So I guess the next step would be, figure out whether we've won

  • and go to a victory.

  • And that's going to be kind of tricky, just

  • because I'm terrible at Minesweeper.

  • So I guess cheat codes [LAUGH] would be important, here.

  • [INAUDIBLE] says "That's exactly what a villain would say."

  • I guess it kind of is, isn't it?

  • The victory state.

  • It's an important part of the game.

  • We should implement that, so let's do that.

  • So-- victory state--

  • [LAUGH] I think it's going to be identical to the other ones,

  • to be honest, the game-over, victory--

  • what was it?

  • Victory-- where are you at?

  • Nope, [INAUDIBLE] title state.

  • I'm just going to copy both of these functions--

  • function, VictoryState, update, and VictoryState render.

  • All right.

  • And then let's go ahead and copy these and do that.

  • So it's the exact same state as we've used for the game over.

  • We used it for the title.

  • We're going to change the text-- again, only thing different amongst these

  • is the string.

  • So if you see something like this in real code,

  • this is a great opportunity to kind of make it more abstract,

  • make, like, a kind of a base class that just takes in a string that does this

  • and you can change the message.

  • That would be a lot more effective.

  • But, in this case, we're going to take the easy way out.

  • We're not going to do that.

  • But you would want to do that for a more complicated game.

  • And actually the victory state is going to be a little bit

  • different, because it's going to include our score.

  • And, I think, so will the game-over state.

  • So we'll do that.

  • Oh, another thing we have to do is we have

  • to actually add a timer to the game.

  • We haven't done that yet.

  • So that's something to definitely take into consideration.

  • So, "You win!"-- we're going to do that.

  • And then we need to actually test to see whether we won, which

  • is going to be a little bit trickier.

  • So I can just write a function that says "function"--

  • um--

  • So what I'm going to do, first, is I'm going to say "reveal all."

  • So, GameGrid, reveal all.

  • What this is going to do is just going to go over the entire grid

  • and just reveal every single tile.

  • So I can say, for y is equal to 1, until self.height do--

  • um-- for x is equal to 1 self.width do--

  • and then self, dot--

  • is it grid, or is it tile? --self.grid, right?

  • self.grid at y x, dot, is hidden, is equal to False.

  • And then, to test that this is working, as soon as we enter--

  • as soon as we--

  • um-- how do I do this?

  • I guess I can do this as soon as we instantiate it.

  • That's fine.

  • So I'm going to say calculate numbers, and then I'm

  • going to say self revealAll.

  • So we're going to start off the game--

  • completely reveal.

  • That's totally fine.

  • So now we can see the board.

  • We can see that it's working.

  • Everything's great.

  • This is, like, kind of the debugging or the cheat codes,

  • I guess you could envision.

  • And if I click on the 2, that's fine.

  • If I click on the bomb, it does let me at least test the game over,

  • which is cool.

  • It's not going to test whether or not we have a victory.

  • And so what I think we should do is, we need a separate function that basically

  • says, have we won?

  • So-- isVictory.

  • Right?

  • And, in order to do this, we basically need to iterate over the entire grid

  • and check to see if we have any hidden tiles that are not bombs.

  • If we have any hidden tiles that are not bombs, then we have not won yet.

  • But if all of our hidden tiles are bombs, then we have won.

  • So it's kind of the same thing.

  • So what I can do is I can assume, at the very beginning,

  • I can assume that we've won.

  • I'll say, yep, we won.

  • That's true.

  • And then I'm going to do what I did before--

  • y is 1, self.height, do, x is 1, self.width, do.

  • And then if self.grid at y x, dot, isHidden--

  • right?

  • If it's hidden, and not self.grid why, dot, x, dot, isBomb, then won is False.

  • We haven't won, because we've found a hidden tile that's not a bomb.

  • Right?

  • That was the one thing we needed to check for.

  • And so, at the very end of all this, I can just return won.

  • I can return whether we won or not.

  • And, on click, which we have down here, uh, I've lost track of where I am.

  • Where is the click?

  • It's in update, here, right?

  • Cool.

  • We'll do an else, here, for the isBomb.

  • So else if self, dot-- or-- else self--

  • what do I name the function?

  • isVictory?

  • Yep, isVictory.

  • Right?

  • So, if self isVictory, then gStateMachine change to Victory.

  • Right?

  • So this will only trigger when we click.

  • So actually we can still use cheat codes and test that this is working.

  • Right?

  • So everything is revealed, completely revealed.

  • So, if I do this, we won!

  • We revealed everything.

  • Right?

  • That was easy.

  • Press Enter to play, brand-new board, et cetera, et cetera.

  • And now, if we actually have the game not cheating from the very beginning--

  • so I'll comment this revealAll out, and I run it, and then I click on--

  • Uh, I clicked [LAUGH] on a bomb, on the first one!

  • That's not good.

  • There we go.

  • So now we can clearly see that the game functions.

  • It doesn't take us to the victory right away,

  • but, if we click on the board, when everything is already

  • revealed that's not a bomb, it was glitched, before,

  • just because those were--

  • it reveals every tile.

  • But normally those would have been unrevealed.

  • Then we win, essentially.

  • Right?

  • That's the criterion for winning.

  • So that was easy.

  • Let me, just to make sure that I'm not missing any chat, here.

  • "How can we use Unity on Ubuntu or some Linux distro?"

  • Good question!

  • So, "unity ubuntu."

  • "How to install Unity on Ubuntu 18.4 and 17.10."

  • So I'm not super-familiar with unity, but it

  • says that they switched to Gnome, which I'm guessing is a windowing program.

  • And according to this article, it's been around

  • for at least six years on Ubuntu.

  • So, yeah, I would--

  • it looks like there's videos and resources.

  • I would definitely check those out and see.

  • I haven't installed it manually, myself, so I'm not 100% sure.

  • If you're using a desktop version of Ubuntu with a windowing system,

  • it should be a window manager.

  • It looks like it's probably pretty straightforward.

  • "I've just finished learning C. The next step is learning a graphics library,"

  • says [INAUDIBLE].

  • "Apparently SDL is a good choice."

  • Yeah.

  • SDL's a good choice.

  • If you're looking to get into game programming in C,

  • I would say probably the best choice, in terms of, like,

  • market share in the C gaming industry--

  • I mean, there's a lot of engines.

  • There's a lot of frameworks.

  • SFML, which sort of sounds like an inappropriate acronym,

  • but SFML is a really awesome C++ graphical game-development,

  • multimedia--

  • I mean, it's Simple and Fast Multimedia Library, I believe, is the acronym.

  • It literally says it right there.

  • [LAUGH]

  • But it's designed to be used for pretty much anything

  • that you can think of that would use graphics, sound, input--

  • any of those sorts of things.

  • It wraps OpenGL.

  • You can do a lot of great stuff with it.

  • I would probably recommend using it.

  • And it has bindings for C, as well.

  • But SDL couldn't hurt you to use.

  • SDL is just a little bit weirder.

  • It's a little more arcane, a little more low-level

  • in certain ways that aren't appealing.

  • SFML kind of abstracts a little bit of that out,

  • to make it more manageable to do C and C++ game development.

  • Still quite a bit trickier than getting into something like Love,

  • which handles a lot of tedium for you.

  • Memory management and data structures in C and C++ isn't fun at all.

  • So I would probably recommend sticking to Love until you really need to use C

  • or C++.

  • But it's a valuable skill to have, I suppose.

  • But, yeah, that's my two cents.

  • It's a very opinionated landscape.

  • So just kind of try different things out and see what you enjoy.

  • Oh, that's my dad.

  • Everybody remembers.

  • "Hello, everyone.

  • Just checking in for a short visit."

  • Good to see you, Dad.

  • Thanks for popping in.

  • Why isn't it unity3d.com," says [INAUDIBLE]..

  • Are you referring to the Linux version?

  • Oh, that's interesting.

  • I'm not sure why they don't have unity--

  • Linux, kind of super-easily--

  • [INAUDIBLE] asking me for my location.

  • Dunno why they have it at the very--

  • Oh, where is this?

  • Get Started.

  • I also should probably look at the newest version.

  • Personal, Try Personal, OK, Install it for--

  • OK.

  • Oh, yeah, [LAUGH] you're right!

  • They just have Mac OS and Windows, on their front page.

  • Yeah, I'm not sure, but it seems like they do make it usable for Linux.

  • So I would look at the video and the article we just looked at together

  • and see what's going on.

  • Everyone's chatting on my dad.

  • Everyone's super, super-awesome.

  • All right, cool.

  • So we have--

  • And then, just to demonstrate, again, we have Minesweeper.

  • Press Enter to play.

  • You click on a thing.

  • I clicked on a [LAUGH] bomb.

  • Click on that.

  • You reveal the entire grid.

  • Everything looks great.

  • You can see that this is probably a bomb.

  • Well, it's most certainly a bomb, because all of these are 1s.

  • They say that, for sure--

  • So there's only one bomb neighboring all these tiles,

  • and there's only one unrevealed tile.

  • It's probably that.

  • Same with this one.

  • Same with this one.

  • These [LAUGH] are a little bit trickier, I would say.

  • Oh, I got lucky, there.

  • Ooh, I got really lucky, there.

  • Um--

  • I'm not sure.

  • It could be this one or this one, for both of these.

  • That's a good question.

  • Hoo!

  • Oh, man.

  • OK, well, OK, so we know that these two belong to this one.

  • Right?

  • And, because of that, I can click on this one, safely.

  • Oh, but it sucks when you reveal a 2.

  • OK.

  • Yeah, that would be the end of this particular iteration.

  • Judy1984-- "Whilst we all love Colton, we also wish to hail in the Malan."

  • I'm not sure where he is!

  • Hopefully he pops by, at some point.

  • You know what?

  • On stream, I'm going to text him to pop by.

  • This is on stream, for the record.

  • Peer pressure.

  • All right, I just texted him.

  • We'll see if he pops in-- see if we get a shout-out from--

  • we'll see if [LAUGH] we can hail in the Malan.

  • Anyway.

  • Let's go ahead and do the next feature, which was, I think, the victory--

  • not victory-- game over, or showing-- oh, actually, no, we do a timer.

  • Right?

  • So we have the game, but we're not locked into any time limit.

  • We just have 120 at the very top left.

  • And also I can probably get rid of all that debug output.

  • It's not really that compelling to look at.

  • So I'm going to do that, actually, too.

  • So let's get rid of all this, all this debug output.

  • Don't need it.

  • Run the game.

  • Looks nice and clean-- super-simple.

  • Let's go ahead, now, and talk about what we need to do to look at the game over.

  • What I want to do is, per what [INAUDIBLE] said earlier,

  • I want to transition us to a view of the game board revealed completely.

  • And that's kind of why I wrote the reveal function, over in game grid,

  • because I wanted to be able to just call that right away, once we transition

  • to the game-over state, and not have to do any kind of iteration and copy

  • and paste anything.

  • So what we can do--

  • and this will illustrate the state machine a little bit--

  • is, if we get a game over, what I'm going to do

  • is over in the-- it's in the game-grid class, down here.

  • So this is where it happens.

  • Now, the nice thing about the state machine that we have

  • is that I can actually pass in a table, to change--

  • the Change function.

  • And this table is going to let me pass in any data I want,

  • in order to be accessible by the class that is--

  • sorry, the state that we're transitioning to.

  • So I can pass her in something that I want game over to have a reference to,

  • the game-over screen, the game-over state.

  • And what I wanted to do is have a reference

  • to-- well, a couple of things, ultimately, but, first and foremost,

  • the game grid.

  • So what I'll do is I'll say, gameGrid equals gameGrid.

  • And so what this is going to do is, I'm creating a table which has the key game

  • grid and the value of our actual game grid-- in this case,

  • it needs to be self.gameGrid.

  • Right?

  • So the game grid that belongs to this game grid-- um--

  • [INAUDIBLE] not self.gameGrid, uh, self--

  • no, just self.

  • Right?

  • Yeah.

  • Sorry.

  • Just self.

  • We're just passing, in this game grid, this actual game

  • grid that's calling this function.

  • Because self, in this case, is the game grid itself.

  • Right?

  • Because that's where we're calling it.

  • We're calling this from within the game grid's own update function,

  • so self is this game grid.

  • So gameGrid is going to be equal to self.

  • So we're going to have a reference to the whole grid,

  • when we're in the game-over state.

  • And, before I do that, I'm actually going to call self.revealAll.

  • Right?

  • And then, after that, I'm going to go over to game over.

  • And this is where we want to use that Enter

  • function that I talked about before.

  • So, function GameOverState enter params.

  • And these params are what we just looked at, in this case.

  • So, game grid--

  • So params is this table, right here.

  • In this case, it's going to have just a single key in a single value--

  • gameGrid, which maps to the game-grid object that we're looking at.

  • So I can say, self.gameGrid is going to be equal to params.gameGrid.

  • Right?

  • And then, once that's finished, we don't want to update anything,

  • but what I do want to do is call self.gameGrid render.

  • And now, what that is going to do is call the same rendering code

  • that we used in our play state, but now, when we go to the game-over state,

  • it's going to actually show our completely revealed game

  • grid that we just lost on, so that the user can see where they clicked

  • and where all the bombs were, where all the numbers were, just

  • as some feedback.

  • Right?

  • So, if I-- and again, Enter itself, if you're confused on where that gets

  • called--

  • If we go back to our state-machine class, here,

  • when we call Change notice that we do self.currentState

  • enter on this current state object.

  • Remember, the new-- in this case, the new game--

  • in this case, it would be the game-over state-- right?

  • The new game-over state object?

  • If we go back to main, right here.

  • All of these are-- remember, these are the state objects

  • that get returned from self.states.

  • So, if you go back to--

  • where were we?

  • We were just in the state-machine class.

  • So currentState is going to be equal to a brand-new, anonymous function's

  • return value of that either a game-over state

  • or a victory state or a play state, depending

  • on where we're calling state machine.

  • In this case, we're calling change on the game-over state.

  • So we're going to get a new game-over state.

  • We're going to pass in-- because, remember, we took in the params

  • as that table, that second parameter.

  • We're going to call enter, pass in those params.

  • And then, in game-over state, we therefore have access to those params,

  • here, which we can then--

  • that affords us the ability to take the game grid essentially

  • from our game, our play state and include

  • that in a completely separate state.

  • So we can transfer whatever data we want,

  • while keeping everything nicely decoupled and organized.

  • So let's test, to make sure this is actually working.

  • And I'm actually going to do this before the output,

  • just so that our text doesn't get obscured.

  • See if I can lose.

  • Oh!

  • I messed up, there.

  • OK, gameGrid on line 38.

  • Um, where is 38?

  • Oh, do we not have self.height and self, dot-- well,

  • we do have self.height and self.width.

  • Hold on a second.

  • Self.height, self.width?

  • Huh!

  • What is going on?

  • OK.

  • So it looks like it's when we lose.

  • OK, it's when we lose.

  • And let's look at our stack trace, here.

  • So, play state going 149.

  • Oh, in the Reveal All function.

  • Game grid, line 38, in Reveal All.

  • Why is that-- interesting.

  • OK.

  • And where were we calling revealAll?

  • Did I mess something up there?

  • In revealAll-- so, on line 149--

  • we're calling revealAll.

  • OK.

  • Let's go to 149.

  • Oh, I called it [LAUGH] self.revealAll.

  • That is a big nuance problem, in Lua, is the difference between dot and colon.

  • Dot essentially doesn't pass anything into revealAll

  • it's an object-oriented, weird thing with Lua.

  • Essentially, if you type in colon, it's effectively

  • the same thing as calling revealAll and passing self in as a first parameter,

  • so that the function has access, implicit access, to self.

  • It does that sort of behind the scenes with colon,

  • but if you don't do it with the period then you

  • have to explicitly type-- you have to basically

  • pass in self as an explicit parameter, to have access

  • to that object it's a little bit weird.

  • It's a prototype inheritance model that has a lot of weird quirks

  • to it, very similar to JavaScript.

  • But now if we click on a bomb, somewhere,

  • notice that we do indeed have that functionality,

  • where we're in the game-over state that we just looked at--

  • right?

  • That's all the same.

  • But we have our full grid in the background.

  • Now, I would say that the text going over the grid is pretty ugly.

  • So we're going to fix that, by changing the text offsets, here.

  • So I'm just going to--

  • where do I have--

  • in the game-over state is where I'm going to change that.

  • So this will be at 0, and then we'll say 16.

  • And then this will be at virtual height minus 32.

  • And let's try it one more time.

  • That looks better.

  • It's not ideal.

  • The offset, the grid offset, could be a little lower, but that's the gist of--

  • now we can actually see where all the tiles were.

  • And it looks nice.

  • It looks-- it's not cluttered with text and our visuals.

  • Ideally, you would style that a little bit better and probably use

  • a better font, to begin with, and more interesting background.

  • And there is an argument to be made about whether we should

  • do love.graphics.clear to maybe some other color, like maybe a really

  • pale shade of red, for example.

  • Let's try something like that, maybe.

  • Yeah.

  • So, just a little bit more of a visual feedback, to tell you, oh, you know,

  • you lost.

  • Like, red's a bad color-- like, not good that you lost, right?

  • And then, in our victory state, we could do the opposite of that.

  • We could actually call love.graphics.clear,

  • to maybe something like green.

  • We could say RGB--

  • like that.

  • And then I'm going to start off, right away, as everything revealed,

  • so we can test victory again.

  • So I'm going to go into gameGrid, just come

  • in and start with everything revealed, just to make it nice and easy.

  • We'll test that.

  • Press Enter to play.

  • Click on anything.

  • "You win!

  • Press Enter to Play."

  • It's kind of an iffy background, so I'm probably

  • going to change that a little bit-- to 0.2, maybe, 0.3.

  • And then we have to style this just like we did the other things.

  • So I'm going to say 16 and then virtual height minus 32, just like that.

  • Click a tile.

  • Oh!

  • And an important thing-- we're not actually passing in the board

  • to our victory state--

  • which is-- that's the only way we can actually

  • get it to render on the screen.

  • Right?

  • So we should probably do that, too, just like we did with the game-over state.

  • We should call--

  • We should pass in, here, our game grid, as self.

  • Right?

  • And then, remember, we have to actually get access to this.

  • We have to define Enter, so that we can extract that data from change

  • and then store it and use it for our victory-state functions.

  • So I can say VictoryState, enter params.

  • And then this will be self.gameGrid is equal to params.gameGrid.

  • And then, did I--

  • I did that correctly in the other one, right?

  • Yep, I did that correctly in the other one.

  • Now I can say self.gameGrid render, just like that.

  • Boom.

  • Enter, click, [LAUGH] and fail.

  • OK.

  • Not what I was expecting.

  • Oh, because I called clear before--

  • or, after-- I called clear after I called the game grid to render.

  • Which, it'll just wipe the whole screen with that color.

  • Not ideal.

  • Cool!

  • So that looks great!

  • [LAUGH] My dad says "Blood on the screen?"

  • Yeah, that's kind of what it looks like.

  • JL97-- "hey, by the way, did you get a haircut?"

  • [SIGH] Man, I did not.

  • Still haven't gotten a haircut.

  • I'm a little ashamed, to be quite honest with you.

  • HippoDefense says "Are states in Lua like classes in Java?"

  • So, no, a state isn't a Lua-specific construct.

  • A state is just an abstraction, over separating our game

  • into multiple sort of domains.

  • Right?

  • We have the title of the game, we have the play state of the game,

  • we have the game-over part of the game, the victory part of the game.

  • These are all different things.

  • They all, in most games, typically look different, act differently.

  • If you wanted to do this all in, like, one monolithic area,

  • with IF statements that say, if we're at the title, if we're

  • playing the game, if we won, if we-- you know,

  • whatever-- it would get to be monstrously

  • long and convoluted and hard to work on as a team, especially.

  • And so a state is just something that we invented

  • that's kind of like a higher level than anything within Lua itself.

  • The actual thing that's most similar to Java classes

  • would be the class that we are using as an actual library.

  • So lib, and then class.lua.

  • And so that is something that we covered in part 1.

  • If you go back to part 1 of the stream, we actually

  • covered how we got that, how we implemented that.

  • Lua doesn't actually have, like, Java-style classes.

  • It has JavaScript-style prototypes, and you can do much of the same stuff

  • that you can in JavaScript, but in Lua it's a little bit weird and weighty.

  • And people made a library that lets you just

  • use it kind of like you would use classes in something like Java.

  • The syntax is very similar.

  • And you just have to basically use only colons

  • for all of your manipulation, all of your function calls for everything.

  • And not using dot is preferable, because it gets a little bit bulky.

  • Passing in self as an explicit parameter isn't particularly fun or interesting.

  • So, yeah, the ability to just basically say, this game grid is a class,

  • and then define its constructor and then all of its functions.

  • They're all colon functions.

  • They all expect an implicit self.

  • And so, if you don't know much about what this is,

  • go on the Lua programming manual and look up

  • the difference between dot and colon.

  • You can look up self, you can look up tables,

  • how tables let you do this, and meta tables, and all that stuff.

  • It's not very interesting.

  • You don't need to really remember it.

  • You really only, really, to do good development in Lua and Love2D,

  • you kind of just need this, and then you can use it like you would use Java

  • or Python or any other language with what's called "classical-style"

  • object-oriented programming.

  • [INAUDIBLE] says "That's a nice explanation.

  • I was getting a bit confused."

  • Oh, I'm glad-- if anybody wants clarification, definitely let me know.

  • [INAUDIBLE] says "Hey, guys."

  • Hello, [INAUDIBLE].

  • Good to have you with us.

  • Cool!

  • So we have visual feedback, now, on our states.

  • It's not pretty.

  • It's not beautiful.

  • That's not our goal, though.

  • Our goal's not to be pretty.

  • I mean, our game grid itself is actually pretty nice.

  • Right?

  • Like, this looks pretty good.

  • And, last stream, if you want a nice crash course

  • in making excellent, excellent pixel art,

  • you can definitely check out what we did.

  • I hand-drew all of these tiles, the 1, all the way up through 8, and then

  • the bomb.

  • And that was a great time.

  • Now, another big feature that was requested

  • is the ability to actually have flags that you can mark individual tiles for,

  • so that I could say, I don't want to click on this tile.

  • I don't want to reveal it.

  • I want to set this to be a flag.

  • Right?

  • And we can do this fairly easily.

  • Let's go ahead and see how I might do that.

  • So a couple of things are going to need to happen.

  • So we need to go probably add some stuff to GridTile.

  • So I'm going to open up GridTile.

  • And then I can say self.isflagged is equal to False.

  • And let me just make sure this is good.

  • Cool.

  • Now, this actually-- we could do this actually

  • in a fairly straightforward way.

  • I can say, down where we click, where we actually do the clicking on our tile,

  • to reveal it, I can say--

  • you know, because this is checking to see if the mouse pressed was 1.

  • But I can say, if--

  • or, rather, I should do this in an elseif, so we don't do them both.

  • Whoops!

  • I can say, elseif love.mouse.waspressed--

  • was that how I wrote it?

  • Yep.

  • --waspressed 2, we'll assume that right-click is to place the flag.

  • And I'm [LAUGH] actually going to have to draw the flag, which

  • is going to be fantastic.

  • Can't wait for that.

  • But what we can do is we can say, self.grid at y x,

  • dot, isflagged is true.

  • And, now, what I basically want to do is say if love.mouse.waspressed 1 and not

  • self.grid y x, dot, isflagged--

  • right?

  • Because, if it's flagged, I basically want to just ignore it.

  • I want to ignore the left-click.

  • Because that's the whole point of the flagging,

  • is to make sure that I don't accidentally click a bomb.

  • So, by right-clicking it, by flagging it,

  • I'm not going to even be able to do that.

  • I'm going to completely ignore all of the logic in here

  • that checks to see if it's a bomb, reveals it,

  • calls the recursive reveal algorithm.

  • All that's going to be gone.

  • All that's going be bypassed.

  • And, in here, what I need to do is say--

  • actually, no, even better-- even better, we'll toggle.

  • We'll say, self.grid at y x, dot, isflagged equals not self.grid at y

  • x, dot, isflagged.

  • Right?

  • So now I'm just calling a NOT operation on that variable.

  • So it's going to toggle on or off.

  • So this will allow me to just use right-click to go back and forth, if I

  • want to unflag something or whatever.

  • Completely have that option.

  • Now, in the actual GridTile--

  • oh, and another thing.

  • Another thing we do have to keep in mind is

  • we have to make sure that it's not revealed.

  • So, if love.mouse.waspressed 2 and not self.grid y x, dot, ishidden--

  • or, rather, and self.grid y x ishidden--

  • We want to only be able to flag hidden tiles.

  • It doesn't make sense for us to flag anything else [LAUGH],,

  • because bombs can't be revealed, ever.

  • So we're not going to let that happen.

  • We're just going to say, make sure that, if we're checking for the right-click,

  • that we're also checking to make sure that that tile is hidden.

  • OK?

  • So, super-easy.

  • The next thing is that we're going to actually have to make it

  • so that the flags draw onto the screen.

  • Which is very important.

  • We'll do that in GridTile, because GridTile

  • manages all of its own rendering.

  • Right?

  • It draws the bomb, if it is a bomb.

  • If it's hidden, it'll just draw the tile.

  • If self.hidden is that, and then if self.isflagged--

  • so now I need to actually draw the flag texture, which is right here.

  • And I don't have a flag texture yet, so this

  • is a great opportunity for us to go back to our lovely, lovely ASprite program.

  • And let's pull up, I guess, bomb.png.

  • We'll just zoom in nice and big, there, so you can see.

  • So my dad's actually pretty good at Photoshop,

  • so he's going to probably cringe, seeing me working in a 2D drawing application.

  • But this is how we do excellent, excellent sprite work.

  • So I'm [LAUGH] going to just kind of erase all of this.

  • And I don't know what the flag looks like, in regular Minesweeper,

  • so I'm going to kind of draw whatever I want, I guess.

  • Let's go ahead.

  • I'm going to load up a palette.

  • I'm going to load up--

  • I really am a huge fan of the DB32 palette, DawnBringer 32,

  • so I'm going to load that.

  • And then get rid of that.

  • And then I'm just going to choose maybe this red color.

  • So we'll say something like this.

  • Uh-- I guess that's OK?

  • Maybe we'll do, like, a dash of pink, there, just to be cute.

  • And then we'll do some thing like that.

  • And then maybe a little bit of green, just right there, like that.

  • What do we think?

  • Think that's acceptable?

  • That's a flag?

  • It's not amazing, but it'll work.

  • Right?

  • So we'll call this "flag.png."

  • Save it.

  • Go back here.

  • And, when we load it, we should be able to just right-click on things.

  • And I think I can right-click with my trackpad, here.

  • Another thing that we want to do, and a thing to take into consideration,

  • is we want to get rid of the flagging of a tile if we click on it

  • and it's not a bomb.

  • So I can say, um--

  • And now, here's an interesting thing.

  • If we flag a tile that's not a bomb--

  • [LAUGH] Dad says "Perfect."

  • If we flag a tile that's not a bomb, and we reveal that through recursion,

  • I guess we want to just get rid of it.

  • Yeah, I guess that makes sense.

  • We don't want false flags anywhere.

  • So, yeah, I think that's what we're going to do.

  • We're just going to say tile.hidden is equal to False,

  • and tile.isflagged is equal to False.

  • So now, at least if it ever was flagged, it won't be flagged eventually.

  • Right?

  • Let's try and run this and see if I got it right.

  • [LAUGH] Everything's set to reveal.

  • OK, that's not right.

  • Let's go ahead and unreveal everything, so we actually have a game to play.

  • I'm going to try right-clicking this.

  • Ooh!

  • Oh, I didn't load it.

  • I didn't load it.

  • That's an important step.

  • So, dependencies.lua-- come down here, into our textures table.

  • I'm going to say, flag is equal to love.graphics.newimage,

  • graphics/flag.png.

  • Do that.

  • Rerun it.

  • Center.

  • Nice!

  • Oh, but you know what I did?

  • Foolishly, I drew over the depressed tile.

  • So [LAUGH] that's the wrong tile.

  • It's not what I wanted to do.

  • So let's try that again.

  • Open Recent.

  • Go all the way up to--

  • where was it?

  • was it o.png?

  • Oh, no, that's the, um--

  • wait, where was--

  • Where was the empty tile?

  • I thought I had an empty-tile one.

  • Really?

  • Well, I can just open it up in, uh--

  • what did I call it?

  • Tile, dot-- it's not--

  • OK, interesting.

  • Let's open it with ASprite.

  • Let's zoom in.

  • We get more practice drawing flags.

  • Here we go.

  • So I'm going to--

  • I mean, I guess I could just copy it, but [INAUDIBLE] ah, it's fine.

  • Uh--

  • Cool.

  • Let's see if that works--

  • see if I did a better job, this time.

  • Yes.

  • Nice.

  • OK.

  • That's the correct style.

  • Dad says "That flag looks great."

  • Thank you very much for your kindness.

  • I appreciate it.

  • And now, if I try to click on it, no matter

  • how many times I try to click on it, it won't let me click on it.

  • Now, if I unflag it and I click, it works.

  • So I think it was [INAUDIBLE] who suggested

  • it there is your feature, [INAUDIBLE].

  • We have flags, indeed, in our game.

  • Pretty exciting!

  • Now, I know that's definitely a bomb, 100%.

  • So is that one.

  • So is-- uh, is it?

  • Uh, this might not be a bomb.

  • Actually, it's definitely not a bomb.

  • Yeah.

  • It can't be.

  • This is a bomb, because this 2 is right here,

  • and these are the only two adjacent tiles.

  • So these two are bombs, for sure.

  • And, because this one is touching this one, this is not a bomb.

  • This is also not a bomb.

  • Pretty sweet!

  • It's pretty sweet.

  • "So all bombs flagged is equal to victory?

  • Did we do this functionality?"

  • Yeah, we already implemented the loop that goes over--

  • it's a little bit hard to test organically,

  • but we implemented it such that, when you click and there are no tiles that

  • are hidden that aren't bombs--

  • so, any number tiles, any clear tiles--

  • you'll go to the victory state.

  • So we already tested that.

  • Cool, cool.

  • "Do you plan on doing a lecture on procedural development,

  • sometime in the future?"

  • Procedural development-- um, [INAUDIBLE],,

  • can you elaborate on what you mean by that?

  • If you mean, like, procedural programming in C?

  • Or is there another methodology that you're alluding to?

  • "Please play to the end once," says [INAUDIBLE]..

  • We'll be here all day, if I try to do that.

  • [INAUDIBLE]---- we should get rid of the hover sprite on buttons that can't be

  • pressed anymore."

  • Um-- that wouldn't be too hard, actually.

  • And I think you might be right on that.

  • I think we might be better off doing that.

  • Let's try that.

  • So we do that over here, right?

  • Um-- hmm-hmm-hmm-hmm-- if self.grid at y x, dot, is--

  • um-- ishidden-- rather, if not self.grid y x, dot, ishidden, then--

  • um-- mmm--

  • Is this correct?

  • If not self.grid y x, dot, ishidden, then it's not hidden.

  • We want to highlight it.

  • No, we want to highlight only if it's hidden.

  • So, will this work?

  • No, it's still doing that.

  • Self.ishighlighting is equal to True.

  • Oh, and we've got to do else self.ishighlighting is equal to False.

  • Cool.

  • There you go.

  • Easy feature, implemented.

  • Procedural generation-- oh, procedural generation!

  • Not procedural development.

  • I love procedural generation.

  • In my Mario pset in my games course, we actually

  • cover procedural generation for game levels.

  • It's pretty cool.

  • For Unity, I'd have to think about what would be fun to illustrate it with.

  • There's a lot of really good tutorials, actually,

  • online that go over procedural generation.

  • Catlike Coding has a few, so check out Catlike Coding

  • for some procedural generation in Unity.

  • Super-excellent website.

  • I'll actually recommend that right now.

  • So, catlikecoding.com Check that out for-- they

  • have a series on tile maps, procedure-generated tile maps,

  • like Civilization style, but 3D, in Unity.

  • So that's a great way to get a handle on it.

  • I haven't gone through all of them.

  • It's a massive amount of material, and it's very intricate, very deep.

  • But if you're looking to get good--

  • If you go through Catlike Coding and you can do all of it,

  • you'll be really good by the end of that.

  • Promise you.

  • [LAUGH] "Maybe a super-secret stream of" me failing at Minesweeper?

  • I don't know if people want to be subjected to that kind of torture

  • for that long.

  • We're actually almost done.

  • So this is great.

  • We have a pretty robust game, here.

  • Now, the only thing that's different, the only thing

  • that we need to worry about, is that the timer needs to get implemented.

  • And so, in order to do that, what we should do is probably go over here.

  • Hmm.

  • Well, this is functionality that we can sort of use at the state level.

  • So I can say, so this will be in our update.

  • So this is important.

  • So, first of all, the most important thing we need to do

  • is actually include our timer class.

  • So I'm going to do that.

  • I'm going to say timer equals require lib/knife.timer.

  • And I don't think I have timer or knife in here at all,

  • but Space Invaders has it, I believe.

  • No?

  • Space Invaders does not have it!

  • I think Typing Game might have it.

  • Yeah, there we go.

  • Typing Game.

  • I have the entire knife library.

  • If you're unfamiliar with the knife library, super-great library for Love.

  • Just type "knife library love2d."

  • And it is Airstruck who created it.

  • So Airstruck knife on GitHub.

  • Load this page, here.

  • Yeah.

  • So, github.com/airstruck/knife.

  • And it's got a lot of great micro modules, as they describe it.

  • There's a lot of great ones.

  • But what I really enjoy are the timer and the event classes.

  • And also chain's pretty cool, too.

  • But we're going to use just timer.

  • So, actually, I didn't copy it yet.

  • So I'm going to go back into Minesweeper, into lib-- copy that.

  • So I have the whole knife library in there.

  • And that's why I'm saying "knife," dot, "timer,"

  • because "dot" is sort of a way, when you're acquiring a library,

  • so you can go into a directory and get a module.

  • So, lib/knife.timer.

  • And then I'm going to-- in my main.lua, in update, I'm going to say,

  • timer.update delta time--

  • Takes in the dot, because it's a global object.

  • It's not an instantiated object.

  • It's not a part of a class.

  • It's just an actual, one entity, one timer variable.

  • Dot, update, DT.

  • And then, if we go into, for example, play state, the play state--

  • we currently are just drawing this 120 as a constant string, which

  • isn't helpful.

  • And so what I want to do--

  • Also, one thing that we didn't do is include a score, actually,

  • which is interesting.

  • So we should probably do that, as well.

  • But what I'm going to do is I'm going to say "tostring self.time."

  • And then that's actually sufficient.

  • Then what I can do from there is I can say self.time is equal to 0.

  • And here's the cool thing about knife.

  • I can say "timer"--

  • or, sorry, time, not knife--

  • timer, the submodule of knife.

  • I can say, timer, dot, every 1 second, call this anonymous block of code.

  • I can say self.time equals self.time minus 1, if self.time is equal to 0,

  • I want to gStateMachine change to the game-over state,

  • passing in our game grid.

  • Remember?

  • Like so.

  • So we're going to not only have the ability

  • to go to game over from clicking on a bomb

  • but also by letting the timer run out.

  • And so, if I test this, if we run, we'll see that we

  • start at [LAUGH] 0, which is not great.

  • We want to start at 120.

  • So let's try that again.

  • So now we're starting at 120 to 119, 118.

  • And I notice it actually didn't even transition to, uh, to the, um--

  • oh, because it called this function after it had already been 0.

  • So it wouldn't work.

  • Now let's try setting it to 5, just so we

  • can test to make sure that it does indeed work when it gets to 0.

  • So 3, 2, 1, and 0-- game over.

  • Cool!

  • It didn't reveal the board, though.

  • That's important.

  • So let's make sure we do that, too.

  • Um-- we'll do self.gameGrid revealAll.

  • Which, actually, that should be probably part

  • of the game-over transition, itself, [INAUDIBLE]

  • when it goes into game over, it will reveal the board.

  • But there you have it.

  • That's the game-over state, using a timer, now.

  • So now you have the element of the clock also kind of keeping you,

  • I guess, nervous, uncomfortable, in a rush.

  • Now, this is another thing that we need-- tostring score.

  • And we're going to actually need to make this part of the game grid,

  • because we're doing all of the score--

  • we're going to be doing all of the actual score modification

  • inside of the gameGrid class itself.

  • So it makes sense to keep the score within the grid.

  • Which is a little bit weird, but it kind of makes sense.

  • And then we can reference the string version of this

  • from anywhere in here, you know, once we've used it.

  • Now, we have to make sure that we actually defined it.

  • Whoops-- didn't mean to do that.

  • So it'll be just part of our initialization.

  • So self.score is equal to 0.

  • And I don't know if there is a special way that you score Minesweeper,

  • but we'll just say that every tile that you reveal

  • is going to be worth five points.

  • Right?

  • So we'll say, over here, in our revealTile function--

  • So, right here, where we reveal the tile,

  • we'll just say self.score equals self.score plus 5.

  • So, anytime we reveal a tile, now, we get five points.

  • Right?

  • 10-- and [LAUGH] game over.

  • That's unfortunate.

  • And I actually really like the fact that, when we click the bomb,

  • the revealed tile stays highlighted like that.

  • And that's just an accident, but that's an amazing accident.

  • That, like, works perfectly.

  • And that's because we're using, if I'm not mistaken--

  • no, we're just highlighting it.

  • Yeah, I was going to say, it looked almost

  • like we were using the subtile, the flat subtile, but no, we're just using--

  • it's just the highlighted rect.

  • It stays active.

  • Which is cool.

  • I actually didn't realize that was going to happen.

  • Anyway, let's try and get a better--

  • oh, [LAUGH] this one has four bombs around it.

  • That's crazy.

  • Ooh, yeah, there we go--

  • 355.

  • So it seems to be working.

  • Uh, well, is that true?

  • Is that really 355?

  • 1, 2, 3, 4, 5, 6 7, 8, 9, 10-- oh, I guess, yeah, it'd be about 355.

  • It would add up, quite a bit.

  • Cool!

  • That is the scoring and the timer.

  • I don't know if I'm missing anything.

  • I think that's all of the features of Minesweeper, right?

  • Correct me if I'm wrong.

  • All the ones that we at least thought we were going to implement.

  • Let me commit everything, so that it's on GitHub,

  • so you can all download it and try it out.

  • Let me make sure I'm in the right place, which it looks like I am.

  • Get status.

  • Get add-- blah-blah-blah.

  • Cool.

  • So that's on GitHub now.

  • So all of you can clone it for yourself and try it out.

  • My DS Store little meta file is in there, unfortunately

  • [LAUGH] [INAUDIBLE] says "It's a feature, not an accident."

  • Yeah.

  • I would say it's kind of both, in that situation.

  • "I'm fine with you getting a bomb.

  • I just want a genuine try."

  • Yeah, I'll give it a genuine try, with our last bit of time.

  • "Timer's not required unless you want do a best time or score?"

  • Oh, I didn't-- OK, I thought it was a feature of Minesweeper.

  • I am mistaken.

  • Well, now you have the restriction of 120 seconds.

  • So hopefully you can finish it in that length of time.

  • "Minesweeper I looked at only calculated your time.

  • You didn't have a limit."

  • Oh, OK, that's interesting.

  • Some variations score according to time taken-- less time, high score,

  • et cetera.

  • Others just do best time, with no score.

  • Easy mode is 9-by-9 grid with 10 bombs and a fixed

  • score for victory, bonus for time, et cetera, but they are just variations."

  • Yeah.

  • I would say--

  • [LAUGH] I would say, give that a shot--

  • implement that on your own.

  • A nine-by-nine grid with 50 bombs sounds insane.

  • That sounds really hard.

  • But that'd be cool.

  • That'd be a cool, sort of, I guess, branching-off feature you could,

  • add like different difficulty levels.

  • Certainly you could take this codebase and run with it

  • and do whatever you want, add all kinds of stuff, but the core of the game

  • is certainly there.

  • And it doesn't look half bad for what it is--

  • you know, a Minesweeper clone that we built in a couple hours, a few hours.

  • You know, it looks pretty much like the real game,

  • and it functions identically.

  • Unfortunately, it seems like David might have been in a meeting or something,

  • so he was unable to join us.

  • But that's the game.

  • Since I'm doing so well already, on this run, I'll just take this as my run,

  • here.

  • So I know this one only is neighbor to this one, so this is a bomb, for sure.

  • Which means this can't be a bomb and this can't be a bomb.

  • This is a bomb.

  • Which means this can't be a bomb.

  • Oof.

  • So these, all three of these, are bombs.

  • OK.

  • Which means this isn't a bomb.

  • So this is a bomb.

  • Which means this is not a bomb.

  • This is a bomb.

  • I'm so bad at Minesweeper.

  • OK, how do we definitively--

  • OK, so this is a bomb, for sure, so that means this is not.

  • OK.

  • This is a bomb.

  • Which means this is not.

  • Hmm.

  • I only have 42 seconds!

  • OK, uh-- hmm.

  • This is a bomb.

  • Oh, this is terrible.

  • Oh, wait, OK, so this is a bomb, which means this is not a bomb.

  • [INAUDIBLE] this is not a bomb.

  • So this is not a bomb.

  • Oh, so these two aren't bombs?

  • OK.

  • Shoot.

  • Shoot, shoot, shoot.

  • Uh, OK.

  • How do we-- [SIGH] Crap!

  • Oh, did we-- oh, so it is not actually working, when we--

  • so I got them.

  • I did win, at the last second, but it looks like there's a bug.

  • So, for the record, I did beat it.

  • [LAUGH] Um, interesting.

  • OK.

  • At least, I think I beat it, unless I made a mistake.

  • [LAUGH] But that means that there's a bug with the actual win detection,

  • so we need to fix that.

  • So--

  • So the isVictory function is not correct.

  • OK.

  • I could have sworn that this was--

  • this is correct.

  • So I'm just going to leave, like, the first--

  • So, in the revealAll function, I'm just going to keep the first three--

  • but like, the first row, I'm just going to reveal the first row.

  • Right?

  • So--

  • But OK.

  • So that's a bomb.

  • So these are bombs.

  • So these are bombs.

  • Oh, wait, no, no, no.

  • That's not true.

  • These three are definitely bombs.

  • Which means this is not a bomb.

  • These are bombs.

  • Those are bombs.

  • OK.

  • All right, well, that should have definitely taken me to a--

  • OK, interesting.

  • So where is the logic bug, here?

  • So, OK, it's probably something very obvious that I'm just missing, here.

  • OK, so we're going through the entire grid,

  • basically saying, assuming that we've won--

  • so, for every single tile in the grid, if that tile is--

  • hmm.

  • Interesting.

  • Oh, did I have it--

  • did I screw that one up, the logic?

  • Or my play?

  • Because, the first time I played, I thought I had gotten every single bomb.

  • Right?

  • Am I tripping?

  • That's a bomb-- oh, no, no, no.

  • That's not a bomb.

  • That's not a bomb.

  • That's not a bomb.

  • [LAUGH] That's not a bomb.

  • That's not a bomb.

  • That's not a bomb.

  • See, I-- yeah.

  • Um-- there's something very simple, here, and I'm having a brain fart.

  • [LAUGH] [INAUDIBLE] says "Tried my latest, and it works there."

  • That's weird!

  • That doesn't make sense.

  • "If the victory is called, then certainly something stays hidden,

  • I think."

  • Hmm.

  • Oh, wait.

  • We're not-- we're not checking it after the click.

  • That's the problem.

  • We're checking it before, or--

  • sorry, we're not checking the logic before the tile gets revealed.

  • We're checking it-- sorry, we're not checking it

  • after the title gets revealed.

  • We're checking it before it gets revealed.

  • And so that tile that we're clicking that should help us win

  • is actually not--

  • it's actually still hidden.

  • Uh-- yeah.

  • So we have to check isVictory after it gets revealed.

  • That's what it is.

  • Yeah, [LAUGH] see, that's what it is.

  • That's what it is, right here.

  • OK.

  • OK!

  • All right.

  • OK.

  • OK.

  • So--

  • We got this.

  • We got this.

  • That's a bomb.

  • Uh-- hey!

  • [LAUGH] That was easy.

  • There we go.

  • MojoDojo101-- "Yo, goodnight, dude.

  • Thank you for streaming.

  • Really enjoy these.

  • See you, chat."

  • Thanks, Mojo, for popping in--

  • really appreciate it.

  • You're leaving at a good time, because we just fixed the last bug, I think,

  • of the repo.

  • Cool-- so we did it, chat.

  • Couldn't have done it without you.

  • "It was a journey that we all embarked on together."

  • We started with nothing and ended with Minesweeper.

  • It was fun.

  • We had good times.

  • Hopefully I didn't just mute myself.

  • No, I didn't, I don't think.

  • Yeah, I think I'm all right.

  • I'm going to stick around for just a couple questions, I think.

  • And then I will--

  • well, one thing I'll do is I'll push that change.

  • Right?

  • So, fixed victory bug.

  • So I'll take any questions.

  • [LAUGH] StayPeaceful89, oh you're about to--

  • [LAUGH] sorry, yeah, we're about to peace out.

  • But we have the VODs up, and they'll be on YouTube.

  • So, if you want to check it out.

  • The next few streams are--

  • so, no stream tomorrow.

  • On Wednesday, Kareem is streaming, where he's going to be talking about Flask.

  • So, if you're into back-end web development,

  • there is going to be a super-cool stream to tune into.

  • It's back-end Python library.

  • If you are into web development on the front end,

  • I'm going to be doing a CSS stream on Thursday.

  • And then, on Friday--

  • I'm super-excited for this-- we're going to actually be looking at people's code

  • submissions.

  • So there's a form, and I'm going to plug it,

  • in case anybody here is unfamiliar with it.

  • bit.ly/cs50twitchcodereview.

  • So David and I are going to do that one together.

  • We're going to take a look at some people's repos,

  • some GitHub repos, and gists.

  • And we're going to critique them for design and style.

  • We're not going to do any bug testing or performance testing

  • or anything like that.

  • But we're just going to look at the code itself, just the code,

  • and determine if there's any stylistic changes that we would make

  • or any stylistic things that we think are really good about your code.

  • So, if you're a beginner, an intermediate, or even

  • advanced, but probably more towards the first two,

  • definitely submit that link, that form.

  • Submit your code-- anything that you're comfortable with having

  • us show on stream.

  • Preferably not-- as we're thinking more about it--

  • preferably not CS50x problem sets and things like that,

  • just so that we don't release solutions and answers to them live, online.

  • But anything that you've done yourself or has

  • been inspired by CS50, or any code that you'd like some words of wisdom,

  • primarily from David, on--

  • who, again, unfortunately couldn't make it today--

  • definitely toss us some links.

  • We'll be happy to take a look at them.

  • NACLEric says "Sent mine out the other day."

  • Awesome.

  • Really shortly before I leave, "Do you have a Twitter I could follow?"

  • I don't use my Twitter at all, but I have

  • to change it to a different user name.

  • It's an old Twitter I had when I wanted to make music production.

  • So it has a music-production name.

  • But I think I might just rechange it to be a more personal Twitter account,

  • for myself.

  • When I do, remind me in the future and I'll plug it.

  • But I'm going to wait until it changes.

  • My handle is going to change.

  • I might even just make a new one.

  • But my handle's probably just going to change.

  • I appreciate your interest in doing so.

  • Thank you very much.

  • Can we tweet git not to add some file folders, not to all commit, like,

  • py cache, if you know at the top of the head,

  • like, that file you don't need to push?"

  • Yeah, so gitignore will do that.

  • So you can create a gitignore.

  • And what that will do, if we're back in here,

  • is, you can just specify whatever files you want here.

  • So dot, ds, store.

  • So, if I do a git rm, dot, ds store, and then I do git status, OK, git-- oh,

  • git add dot, git commit, git ignore--

  • now, this is how you do it.

  • You can you can use patterns.

  • You can use asterisk patterns, glob patterns,

  • to specify file types and naming types--

  • things of that nature.

  • If you refresh, then you can see that the ds store is indeed gone,

  • and now we have a dot gitignore file that replaced it.

  • So, yes, you can absolutely do that.

  • Oh, and-- [INAUDIBLE] kindly plugged the Minesweeper link, there, in the chat.

  • "If you could choose any of them, which game

  • would you wish to have created yourself?"

  • Probably [LAUGH] Minecraft.

  • Fortnite is great, it's been very successful,

  • but I have more of a personal attachment to Minecraft.

  • So I think if I had created that I'd be pretty happy with myself.

  • But, I mean, [LAUGH] I'd take Fortnite, too.

  • That'd be fine.

  • Fortnite's fun.

  • [INAUDIBLE] says "Hey, everybody, I'm late."

  • Oof-- we are just now winding down, Adam.

  • I apologize.

  • But I'm glad you tuned in, nonetheless.

  • Definitely check the vod out-- watch the Minesweeper part 1, part 2.

  • It was actually-- it was pretty good.

  • I'm happy with how this turned out.

  • It ended up looking pretty nice.

  • "This really was a great game we did."

  • I think so!

  • I think it was pretty cool.

  • Right?

  • Like, it's kind of a real-world--

  • I mean, it's very simple, all things considered, but it looks pretty good.

  • Just one more time, for the folks that just showed up, the game

  • we implemented was Minesweeper.

  • And it's a little bit skewed, because we have it kind of in debug mode.

  • So I can do is, I can go to my game grid, go all the way up here,

  • and we'll just comment this out.

  • So, folks like Adam who just tuned in, we have Minesweeper.

  • So your goal is to avoid the mines.

  • So notice we have highlighted tiles.

  • You can click on the grid.

  • This 1 means that one of these neighboring tiles has a bomb in it,

  • so don't click--

  • Basically, we have to be careful.

  • We can't really click any of these and know for certain whether it's a bomb

  • or not.

  • And you kind of just have to take some guesses.

  • Oh, [LAUGH] it looks like the revealAll function is still messed up,

  • so I need to set this to 1.

  • So I'll commit those two changes, here, very shortly.

  • But, if we do this, and, for example, I click on an empty tile,

  • like there is over here, it will recursively go out amongst all tiles

  • and reveal it if it's either empty or a number.

  • And if it's not an empty or a number, which means it's a bomb,

  • it will just leave it like this.

  • And if it's a number, it'll stop recursing at that number.

  • So it'll only reveal up to a number or an empty tile.

  • And the empty tiles will just keep on expanding

  • until they hit numbers or bombs.

  • And so we know for sure, this right here is a bomb, because all of these tiles

  • say they have one neighboring bomb and it's the only unrevealed tile.

  • So we have the functionality where you can right-click that tile,

  • to basically tell yourself and the game that that's a bomb--

  • mark it.

  • Don't let me click it.

  • And so, now, no matter how many times I try to click this tile,

  • it won't let me accidentally click on it.

  • Which is a nice failsafe.

  • Now, it gets a little bit trickier when we're out here,

  • because, a lot of these, we're not entirely sure which of the tiles

  • is the bomb.

  • The only one we definitively know--

  • that we can definitively ascertain is a bomb for sure is this tile, right here.

  • Because there's just this one, right here, and it's bordering this tile.

  • That's the only tile that it's bordering that's unrevealed.

  • So I can mark that as a bomb.

  • And, as result, any of the tiles that this is next to that have a 1,

  • we can get rid of, because we've already determined that this is the one.

  • This is the bomb.

  • So I'll click this here and click this here.

  • And we're sort of expanding out--

  • This is kind of the game loop, the center, core aspect of the game.

  • You kind of go out and use deduction to figure out, oh, OK, this

  • has this neighbor.

  • What can I do?

  • How can I figure out which tiles there are?

  • Yeah, it's pretty sweet.

  • It's pretty sweet.

  • But, yeah, that's Minesweeper.

  • If we click on a bomb--

  • I can try and detonate a bomb.

  • It looks like that's a bomb, so I'll unclick it and then do that.

  • If we click on a bomb, and we blow it up--

  • uh, we win.

  • OK, that's a bug!

  • That's not-- oh--

  • [LAUGH] and we lost.

  • Uh, I'm not--

  • OK, there's still a bug, here.

  • That was kind of hilarious--

  • Oh, and I spoke too soon!

  • And we have a special guest, today.

  • You want to play some Minesweeper?

  • DAVID MALAN: Is the game ready to play?

  • Hello, everyone!

  • COLTON OGDEN: In a sense.

  • DAVID MALAN: Uh-oh.

  • COLTON OGDEN: [LAUGH] I just discovered a bug, actually,

  • right as I was wrapping it up.

  • But if you want to press Enter, you can click with the trackpad.

  • Left-click is to-- do you know how to play Minesweeper?

  • DAVID MALAN: It's been a while.

  • COLTON OGDEN: OK.

  • DAVID MALAN: Yes.

  • OK, so let's go here.

  • So now there's two bombs around me, I think?

  • COLTON OGDEN: Yep.

  • DAVID MALAN: So maybe, hopefully, not there.

  • COLTON OGDEN: [LAUGH]

  • DAVID MALAN: OK, now--

  • uh-oh.

  • There's one right around me?

  • COLTON OGDEN: There's one, yes.

  • [INTERPOSING VOICES]

  • DAVID MALAN: Wait, I won?

  • [LAUGH]

  • COLTON OGDEN: That's the bug.

  • DAVID MALAN: That is the opposite of correct!

  • COLTON OGDEN: Watch-- wait.

  • Oh, wait, never mind, actually.

  • There's another bug where it turns into You Lose, from a timer type thing.

  • So that was the thing we just discovered.

  • It was working, before, and I did something, and now it's inverted.

  • DAVID MALAN: Well, I hope you all learned a little something, today,

  • about-- after the past three hours-- of how to implement Minesweeper.

  • COLTON OGDEN: Yeah.

  • DAVID MALAN: Nice.

  • COLTON OGDEN: It's been good, though.

  • It looks pretty nice, though.

  • I'm pretty happy with how it looked.

  • It was functionally a little bit better, before,

  • when it actually said "You Lose," here, instead of "You Win."

  • DAVID MALAN: And then it would be red, instead of green?

  • COLTON OGDEN: Yeah.

  • [LAUGH] But, all things considered, the core aspect of the game does work.

  • So that's pretty cool.

  • DAVID MALAN: Uh-huh?

  • Is that true?

  • Chime in, if you agree, if you could.

  • COLTON OGDEN: We have video proof.

  • DAVID MALAN: All right.

  • COLTON OGDEN: But, yeah, we have a couple of things we need to fix.

  • DAVID MALAN: Good to see everyone.

  • I'll say goodbye in the chat, as well, but thanks for having me.

  • COLTON OGDEN: Cool!

  • Thanks for popping by-- appreciate it.

  • DAVID MALAN: See you soon.

  • COLTON OGDEN: I was getting my--my hopes were down!

  • I thought you weren't going to make an appearance.

  • DAVID MALAN: Oh, well, I came to see the big finish, but, you know--

  • COLTON OGDEN: [LAUGH] not such a big finish.

  • All right.

  • Cool, cool.

  • Well, I'm very happy that we got an appearance in.

  • Let's figure out why [LAUGH] that's happening.

  • So our logic is clearly messed up, here.

  • So, in the game grid--

  • I believe it's in the game grid--

  • and, you know, just when we thought we were done, right, now

  • we have another thing to pluck off.

  • But this is kind of the perpetual cycle of game development.

  • We didn't test thoroughly enough.

  • But there's a lot of corner cases and a lot of things,

  • and you change one small thing, and it can propagate

  • and really sort of evolve and, um--

  • it can get kind of nasty.

  • Now, this victory thing, here, this whole thing needs to be in an Else.

  • So this is the problem.

  • We're going through this code, right here,

  • and we are doing this whole is-a-bomb thing, et cetera, et cetera.

  • And then, at whether or not it was a bomb, and we triggered a game over,

  • we actually go straight down into here, where we reveal the tile,

  • and then we check victory.

  • And because, when it's a bomb, the whole thing gets revealed,

  • it actually turns out that that's a victory condition, as well.

  • Because the game, the whole board gets cleared when we get a victory.

  • Right?

  • So this needs to be, indeed, in Else.

  • And let's go ahead and do this, like that.

  • And let's make sure everything still works.

  • There we go.

  • So-- Game Over.

  • Now it's working.

  • Everything's in the right order.

  • And it was a pretty easy fix, all things considered.

  • Now, with those last three changes that I made, which were just a few,

  • I'm just going to commit them--

  • bug fixes, bug and debugging fixes, et cetera, et cetera, et cetera.

  • But-- nice, easy fix.

  • Glad that didn't take another 45 minutes, which would be unfortunate.

  • Everybody had some nice things to say for David.

  • Ooh, man, we've got a lot of comments.

  • OK.

  • "To update you, Colton, I'm on week 2 of the Gave Dev course,

  • and it's definitely gotten more difficult.

  • I'm going to be working on it today," says Adam.

  • Awesome.

  • Well, thank you, for definitely sticking with it.

  • Let me know if you have any difficulties with it.

  • If you work hard, it'll be pretty doable.

  • It's a robust course.

  • It's a-- games are-- you know, it's meant

  • to get you introduced to larger code bases.

  • And it's also meant to kind of keep you interested.

  • Like, there's games plucked from all sorts of corners classic gaming

  • history, from the old to the new.

  • And I think that's an important thing to do,

  • to keep people sort of engaged in a course like games,

  • is to actually focus on the games.

  • Because, at the end of the day, that's what you're going for.

  • You're going into this with the goal of trying

  • to be able to make something yourself, so it only

  • makes sense to get some real hands-on experience doing that.

  • So, good on you for sticking through it.

  • Keep us up to date, as we go on through the streams.

  • "We did modularized code, state-machine classes, timing, scoring, everything

  • a game needs.

  • This and sound and levels will be in the to-do [INAUDIBLE] different language,"

  • says [INAUDIBLE].

  • Yeah, I know, it's a it's a it's pretty robust.

  • A game like Minesweeper, I mean, we could have done, like,

  • an explosion-type thing or whatnot.

  • But, at this point, probably not the most crucial feature.

  • And it's fairly easy to do.

  • You could easily do something like that on your own--

  • and maybe even an animation, which would be pretty cool,

  • like, an explosion when you lose, and then it reveals the map.

  • That'd be pretty cool, actually.

  • That would be a little bit more work than just, you know, a sound effect,

  • but pretty awesome.

  • Oh, [INAUDIBLE],, that's the bug I told you about.

  • OK, I missed that.

  • I missed that [INAUDIBLE].

  • And, yeah, the timer function keeps going.

  • Yeah.

  • "The timer needs to be reset."

  • Does it actually get reset when we, um--

  • when we reset the game?

  • It does.

  • Oh, right, because we're resetting 120 every time

  • we reinitialize the play state.

  • So, yeah, that works great.

  • Everyone's got some nice words for David.

  • Nice, nice.

  • "It works.

  • [INAUDIBLE]"" [LAUGH] Yeah, it was [INAUDIBLE]..

  • I appreciate everybody for supporting me in the chat.

  • It's very nice of you.

  • "The bomb is not hidden, so it doesn't check if it is a bomb.

  • In the condition, maybe it would be OK if we changed the check sequence?"

  • Yeah, no, I think the problem was the order in which we were calling things,

  • and we were changing from the--

  • we were changing to the game over but then, immediately after that--

  • It wasn't a binary operation.

  • We were kind of doing both of them.

  • And so we would go to the game over, but then we'd

  • go to victory, after the board was cleared,

  • because the victory condition was checking the cleared board,

  • after we went to game over.

  • Because game over clears the map.

  • It was kind of a chain of events that turned out to be unideal.

  • But everything's working, everything's great.

  • Twin Tower Power [LAUGH] Did I just thank myself?

  • Yeah, that's what it looks like.

  • "I think play state needs an update, because that one keeps running

  • even though there is a victory?"

  • Um-- no, it doesn't keep running, I don't think.

  • Right?

  • Am I--

  • I don't think the play state keeps running, does it?

  • Because, if there's a victory, then it'll

  • get transferred over to the victory state

  • and the play state's gone, at that point.

  • So it's actually impossible for that to happen.

  • But the timer will keep running, because the timer is a global variable that

  • always maintains a counter.

  • But the timer itself, the time that we have in the game, the play state, gets

  • reset upon the instantiation of the play state, which

  • takes place in the state-machine class.

  • So, altogether, everything works pretty well.

  • But, yeah!

  • That was Minesweeper.

  • That was the second, I think, big, awesome game that we implemented.

  • We've done a few, so far, but I think Space Invaders and Minesweeper

  • are two sort of big, chunky, meaty sort of representative games

  • that give you a sense of what goes into, like, to [INAUDIBLE] point,

  • making a full game, something that's actually feature-rich,

  • not just a toy example or an illustration.

  • So it was good.

  • It was good fun.

  • I enjoyed it.

  • And I think it was [INAUDIBLE] who initially suggested Minesweeper,

  • so shout-outs to [INAUDIBLE].

  • If you're not the one who suggested it, [LAUGH] I apologize.

  • And a shout-out to whoever did.

  • But I'm pretty sure [INAUDIBLE] suggested Minesweeper.

  • So.

  • Credit where credit's due.

  • "Sorry I missed it, but does this game use a state-machine class?"

  • It does.

  • It does, yeah.

  • In main, over here, you can see that we are indeed

  • initializing a state machine.

  • And we used the state-machine class that we used from Space Invaders-- sorry,

  • was it Space Invaders?

  • I don't remember.

  • Oh, yeah, it was Space Invaders.

  • Oop, I just almost fell.

  • Yeah, we used the same state-machine class

  • that we just used in Space Invaders.

  • JL97-- "I would love to see a stream on deployment methods."

  • We did talk about Travis.

  • Kareem and I did a Travis stream.

  • But if you have more specific ideas, definitely let us know.

  • Also, [INAUDIBLE] did it.

  • Oh, OK, shout-out to [INAUDIBLE].

  • Haven't seen her in a little bit.

  • [INAUDIBLE] says "Good job."

  • Thank, you, [INAUDIBLE].

  • Yeah.

  • And, as always, if you have any suggestions

  • for this stream, any exciting topics or videos,

  • games that you want me to program, or anything any of our other staff

  • can program--

  • and we will be having some more staff soon, and Nick is coming back,

  • and that's exciting.

  • And then we'll have other folks and some new folks, as well-- like,

  • Emily Hong will be joining us for web scraping.

  • We're going to have some good stuff.

  • Yeah, let me know, either here in Twitch, on Facebook, YouTube comments.

  • [INAUDIBLE] says "A machine-learning talk?"

  • Yeah, Nick actually did a binary classifier in TensorFlow.

  • So we have done a little bit of machine learning,

  • but we could do some more, more machine learning, I'm sure.

  • Machine learning is a hot topic.

  • One of these days, I should learn some machine learning.

  • But, yeah, if you're on Twitch, then let me know via Twitch.

  • If you're on YouTube, and you're watching this, which many people will

  • be, leave a comment with some ideas.

  • And subscribe to our YouTube channel.

  • And, if you're on YouTube and you're watching this video, there,

  • but you haven't yet checked out our Twitch channel,

  • definitely go to twitch.tv/cs50tv, which is right down here-- whoops--

  • I'm getting cut off-- right over that way, on the bottom left of the screen.

  • Go there, so that we can have a lovely conversation

  • while we do all of these awesome programming streams.

  • Because that's the awesome thing about Twitch, is the sort of the--

  • bi-- the, uh--

  • what's the word I'm looking for?

  • Sort of the bilateral communication layer?

  • I don't know.

  • It's a little bit too fancy.

  • "It was a complete project.

  • Thanks for making these streams."

  • Ah!

  • My pleasure.

  • [INAUDIBLE] "the game does turn to game over, after a victory, with the latest.

  • Nice challenge, to figure out why that is."

  • "It turns to game over, after a victory."

  • Oh, you know why that is?

  • I think because it's still in play state.

  • Doing it here.

  • See this?

  • This block of code is still going to exit every--

  • or it's going to execute outside of--

  • because timer's global.

  • So this anonymous function is still going to exist, even

  • when you're within the, um--

  • even when you're within the-- buh-buh-buh-buh-buh-buh-buh--

  • some other state.

  • Right?

  • And so, I think what the appropriate thing to do would be,

  • in the victory state, it would probably be timer.clear.

  • And what that should do--

  • oh, I'm not on the update function.

  • Actually, you could do this in the game grid, I think.

  • So, in the update--

  • oh, I guess in the victory state, here, you would do--

  • and I'll probably leave this as an exercise,

  • because debugging is very important.

  • So you would do something like timer.clear.

  • And I don't remember exactly offhand if that's the function name

  • or if it's something else, but this just clears

  • all of the time-based functions that are associated with the timer.

  • So you can do this in the victory state.

  • You could do this in the play state.

  • We don't need to do it in basically anything but the game-over

  • and the victory state, I think.

  • But we can do that in all of them.

  • Timer.clear.

  • So try that out, test that out, Martin, and let me know.

  • And also, thank you for testing so thoroughly.

  • In any case, what I was saying was, if you're on YouTube,

  • check out our Twitch.

  • If you're on Facebook--

  • sorry, I should say, if you're not on Facebook, [LAUGH] follow us on Fa--

  • or, you know, uh, yeah, I guess, follow us on Facebook--

  • Facebook.com/cs50.

  • We have a Facebook group, as well.

  • I think it's Facebook.com/group/cs50, if I'm not 100% mistaken.

  • Facebook.com/group/cs50.

  • Is that right?

  • It is.

  • OK.

  • So, definitely check that out.

  • So, Facebook.com/cs50, Facebook.com/group/cs50,

  • and then youtube.com/cs50.

  • Go to all those links.

  • Most of the people that are here in the chat probably know already,

  • but this is more for folks that are watching

  • on YouTube and through other means.

  • We do Facebook, where-- we're figuring out our Facebook Live shenanigans.

  • But we stream to YouTube, and we stream to Twitch.

  • And we do this every week.

  • So hit us up with your ideas.

  • We're always interested in providing a useful content.

  • We'll do a little transition, here.

  • So thanks, everybody, who's here, who attended, who watched.

  • Special thanks if you watched both parts all the way, which is very good.

  • And especially if you coded along-- that's a great way to learn--

  • through, [LAUGH] for better or worse, given all the bugs that we did.

  • But, yeah, [INAUDIBLE],, yeah, that was the end.

  • So-- my pleasure.

  • "[INAUDIBLE] for the stream."

  • Thank you.

  • Yeah!

  • I'll take any last questions, before we adjourn for today.

  • Again, to repeat, Thursday--

  • rather, Wednesday-- Kareem and Flask.

  • So Kareem and I will talk about Flask.

  • On Thursday, I will show you the beautiful ways of basic CSS.

  • I'm not a CSS wizard.

  • Again, this is the year for me to grow as a web developer,

  • but I do know the basics of CSS, and I can teach you how to use them.

  • And then, Friday, David and I are going to review your code.

  • So submit your source code to bit.ly/cs50twitchcodereview.

  • Again, so that it's visible on the final video, cs50twitchcodereview.

  • Many of you probably already did this in the chat.

  • But, if any of you have not done so, and if any of you watching on YouTube

  • haven't done so, and you want to get this into us before Friday,

  • for our first episode of that show, of that-- well, it's

  • going to be CS50 on Twitch, but of that particular version of the show,

  • definitely do so.

  • Cool!

  • All right, everybody.

  • Enjoy the rest of your day.

  • I will see all of you on Wednesday.

  • Ta-ta.

COLTON OGDEN: All right.

字幕與單字

單字即點即查 點擊單字可以查詢單字解釋

B1 中級

MINESWEEPER FROM SCRATCH (Part 2) - CS50 on Twitch, EP.30 (MINESWEEPER FROM SCRATCH (PART 2) - CS50 on Twitch, EP. 30)

  • 1 0
    林宜悉 發佈於 2021 年 01 月 14 日
影片單字