Placeholder Image

字幕列表 影片播放

  • COLTON OGDEN: Good morning, good afternoon good evening,

  • depending on where you are.

  • We are currently based in Massachusetts in the United States.

  • But I know we have many viewers all over the world.

  • This is CS50 on Twitch.

  • My name is Colton Ogden.

  • And today we're going to be taking a look back

  • at game programming, which is what I usually program in.

  • Last week on Friday we did HTML.

  • Today we're doing games in LOVE and Lua.

  • And if you don't know what those are, we'll go over them.

  • But they've been used in many streams before.

  • Today we're going to look at a pretty famous game.

  • If you grew up in the 90s or used computers in the 90s

  • you probably had or at least used a Windows machine.

  • And on many Windows machines, there was a game called Minesweeper

  • and it looked something like this.

  • Let me transition to my computer.

  • And Minesweeper has had many incarnations over the years

  • and certainly not just limited to Windows machines.

  • But this is what Windows '95 Minesweeper looked like.

  • So you have this grid of tiles.

  • And we've talked about 2D tile grids before on stream, particularly

  • with snake as a good example, that snake was

  • based around the sort of 2D grid idea.

  • But you have a grid of these blank tiles that all start off unrevealed.

  • And you click tiles individually.

  • And when you click on a tile, it basically

  • tells you how many bombs are adjacent to that tile.

  • And it considers also diagonal offsets relative to that square.

  • So not just up, down, left, or right but also whether there's

  • a tile in the diagonal direction.

  • And so through inferring based on what you see,

  • what numbers you see when you reveal tiles,

  • you can get a sense of where the bombs are.

  • And if you can happen to reveal every tile on the map except for the bombs

  • you win the game.

  • And you get a certain amount of points, as you

  • can see up at the top of the screen.

  • I believe on the left here, this may be the points and on the right

  • would be the timer.

  • I'm not 100% sure.

  • But in any case, that is the game that we will be talking about today.

  • So let me just read off some of the chat here.

  • And everybody let me know if you can hear me, if everything sounds good.

  • We had a network change, although that shouldn't affect this stream.

  • But I did have to rewire everything up to make sure that it worked.

  • So shout outs to everybody who joined the stream early.

  • So, sashka32, bellocures, lewie0416sdo, that's

  • a name that I haven't memorized yet.

  • I'm not sure if you are a first timer or if you've actually been here before.

  • Mkloppenburg, thanks for joining.

  • You and I chatted briefly in the chat beforehand,

  • saying that you really like the code review idea.

  • So we are going to be doing a code review episode or potentially

  • more episodes in the future.

  • Moididie, thank you very much for the follow.

  • So what we're going to be doing is, we'll be soliciting

  • and we did this on Facebook, where we essentially

  • are soliciting folks who are interested in us reviewing their code.

  • And so I'm trying to think offhand.

  • I don't have a Bitly URL.

  • It'd be cool to have a Bitly URL or something.

  • Let me see if I can really quickly do that.

  • So if we have a--

  • just go to Bitly.

  • So a Bitly is a URL that allows you to shorten the longer URLs.

  • So we have a form.

  • Essentially here's the gist of the idea for the new program.

  • So you have a GitHub repo or a gist, those two.

  • We're formally saying gist or GitHub repo

  • is public URLs for your source code.

  • Sashaka32, you didn't get a haircut.

  • You're right.

  • I promised I was going to get a haircut on this last Sunday.

  • Couple of things happened.

  • So firstly, we had a blizzard here.

  • And it was kind of a pain to go out in the blizzard in super cold weather,

  • super freezing.

  • And especially because I would be getting a buzz in super freezing

  • weather, I was like, no.

  • I'm not going to do that.

  • And then I tried to get it yesterday and the person that cuts my hair

  • wasn't working yesterday.

  • So I kind of shafted myself a little bit.

  • But I'm going to say, I will do my best to get it done

  • by Friday if not over next weekend.

  • But yeah, I screwed that one up.

  • Anyway, the code review idea.

  • So the code review thing is something where you, if you're a Twitch viewer

  • or otherwise if you're on YouTube or whatnot,

  • you can submit to us your source code for some repo or a gist.

  • And we'll take a look at it and on stream, David and I

  • will grade your code sort of in the same way

  • that we do with CS50 students at Harvard,

  • where we just take a look at your style and your design.

  • So basically, how well is your code formatted and then how well

  • have you engineered your application?

  • Do you have duplication of code?

  • The dry principle, those sorts of basic things.

  • Like is your code well engineered, well designed?

  • Does it look nice?

  • To sort of give you some feedback if you're

  • a beginner or an intermediate programmer and you

  • want to make sure that you're on the right track, we can do that for you.

  • And it would be a cool bit of new content for the Twitch channel.

  • So what I was going to do was, I'm going to the CS50 Facebook group.

  • I'm going to try and get that URL so I can post it in the chat

  • here for everybody who's actively watching right now.

  • Let me just-- I don't want to take too much time on this.

  • But this is a cool idea.

  • I think it could make some great content.

  • Let me get the URL.

  • Going as fast as I can here.

  • OK.

  • Bitly, here we go.

  • OK, create.

  • Long URL.

  • OK.

  • This is going to be--

  • copy.

  • Actually, let's make it--

  • how do I edit this?

  • Customize.

  • So This is going to be CS50 Twitch code.

  • What's the best way to do this?

  • CS50 Twitch code review.

  • It's not really that long of a URL.

  • But I'm going to put in the chat now.

  • So bitly/CS50twitchcodereview.

  • Let me just make sure that that works.

  • Bitly, CS50 Twitch code review.

  • Yes.

  • OK.

  • Cool.

  • So that's in the chat now.

  • So if you go to that URL you can submit your code, your source code.

  • So the form is going to expect a GitHub repo or a gist, a GitHub gist, which

  • is another GitHub feature that lets you basically put your source code into one

  • file that you can share publicly.

  • Ideally the code basis should be somewhat small and manageable so

  • that we don't have to spend too much time grecking through a massive code

  • base.

  • We're not going to bug test, so this isn't like, fix my bug.

  • Although maybe that's something that we can

  • look at in the future for a different bit of content.

  • But this is going to be more like, grade my style.

  • Like, how good does my code look?

  • How well engineered is it?

  • Definitely use it for that intended use case.

  • So I'm going to transition back to my screen here.

  • That's all done.

  • Let me make sure that I'm caught up with the chat.

  • So that was mkloppenburg, Martin posted in the chat about that.

  • So if you're curious what he's referring to, that's what the URL is.

  • Submit that link if you want a chance to have your code reviewed on stream.

  • And we may not be able to review everybody's source code,

  • but we'll take a look.

  • We'll definitely try our best.

  • So thanks so much.

  • [INAUDIBLE], thank you very much.

  • Doing a great job.

  • Appreciate it.

  • Bellacura says hi.

  • Tuxman, let's Minesweep today.

  • And tuxman I think also posted about the source code thing.

  • So tuxman's already in.

  • So follow tuxman's example, join the CS50 review initiative.

  • LightOfHell1 says hello.

  • What technologies will you be using for this, says zakillapotato.

  • And how in-depth will you explain what you are doing and why?

  • Fairly in-depth.

  • I would say probably not as in-depth as prior streams that covered the basics.

  • But we're going to be using two technologies today.

  • So the first technology is called Love 2D, or Love.

  • And it's a framework, a game framework that you

  • can go to love2d.org to download.

  • And the language that it uses for you to actually write your programs

  • is called Lua.

  • And it's a scripting language similar to JavaScript and Python

  • in some ways and kind of its own thing.

  • But download that if you haven't downloaded it already

  • and you're curious and you want to follow along.

  • We've used it in a bunch of streams so far.

  • It's a great 2D game development framework.

  • And we will be using that and covering--

  • I'll be explaining some of the basics as we go along, but not tremendously.

  • Especially because today's game is a little meatier than some of the games

  • that we've done recently.

  • So great question.

  • Never used Lua.

  • Is it worth watching if I'm primarily focused on web development?

  • Again by zakillapotato.

  • If you're interested primarily in web development,

  • you may not get a ton of value out of this particular video.

  • In the future we'll be doing more web development

  • content such as CSS and JavaScript.

  • So it's CSS next week, JavaScript probably the week after that

  • or at least the weeks after that.

  • Bootstrap coming after that.

  • Love and Love 2D are in the context of game development

  • and pretty separated from web development.

  • So it kind of depends on whether or not you have a casual interest in games.

  • But yeah, that is a great question, good consideration.

  • Ultimately all of learning how to program

  • kind of comes together and is useful.

  • But I wouldn't blame you for ducking out of today

  • and maybe watching a future stream on maybe CSS or Bootstrap or JavaScript.

  • But to sashakan's point, always helpful to explore different areas, exactly.

  • Lua has multiple web frameworks like Sailor and Lapis, says tuxman.

  • Oh yeah, interesting.

  • So I guess if you're doing a transpiled approach, so taking a language like Lua

  • and transpiling it to JavaScript, which is common to transpile languages

  • back and forth on the web.

  • For example, CoffeeScript is a transpiled language.

  • It's very similar to Python.

  • And people program in CoffeeScript and then transpile that to JavaScript.

  • So you could do something like that with Lua, it sounds like.

  • I actually haven't dug into Lua web development too much at all.

  • So I'm not familiar with either Sailor or Lapis.

  • But I believe tuxman's much more familiar with that area than I am.

  • Babbox says, what is the deadline to submit code for the code review stream?

  • I'm working on tic-tac-toe using Python, the one discussed on CS50 Beyond.

  • We don't have a specific deadline.

  • So what we're going to do is have a stream next week,

  • next Friday on the code review.

  • And then if people like it and it's a good stream

  • we'll probably do multiple of them.

  • So I would say deadline, next Wednesday try to get it

  • if you want us to look at it and get it onto the first code review stream.

  • I would say try to get it to us by next Wednesday.

  • But like I said, we may end up doing multiple videos, multiple code review

  • videos.

  • Zakillapotato, your hairstyle rocks.

  • Thank you very much.

  • I appreciate it.

  • It's getting a bit over overwhelming now.

  • Adamantium, what's up?

  • Good to see you.

  • Meowcat, hello.

  • Zodiac, what's up?

  • Zodiac, good to see you as well.

  • Pudavodatel, hi from the Ukraine.

  • I'm here for the first time.

  • Yeah, I don't recognize the username.

  • So thank you very much for joining us.

  • I hope I didn't butcher your username too much.

  • [INAUDIBLE] got the XD.

  • LightOfHell1, will David appear today?

  • Physically no.

  • I don't believe he's in Cambridge so he probably

  • won't be making a physical appearance.

  • But I would pay attention to the chat.

  • He will probably make a chat-based appearance.

  • TheTronBot, hello.

  • Good to have you with us.

  • Bilty URL is there indeed.

  • Coldmind, finished week one of the game dev course.

  • Loving the course so far.

  • Awesome, thanks so much.

  • I really appreciate that.

  • Week one, week one, week one.

  • So week zero was pong, week one was--

  • what was week one?

  • I'm blanking.

  • Brain fart at the moment.

  • I apologize.

  • But yeah, I remember it starts to ramp up a considerable amount.

  • My favorite lecture, again, the Mario one which I think

  • is week five or week four.

  • Let me know how-- oh, Flappy Bird.

  • That's right.

  • Of course, of course, of course.

  • Yeah, that one's good.

  • So it's nice because it introduces you to sprites and other cool things that

  • get some graphics involved as opposed to just line graphics

  • and shapes and fonts like we've done for some of our streams.

  • Anyone have any idea why running Love 2D on a VM

  • would actually have a higher FPS than my host machine?

  • Graphics drivers, I'm going to assume.

  • If it's not the software, yeah.

  • It's weird because it depends.

  • Like a Mac drawing API like Metal and stuff,

  • or Vulcan, which is a Windows one, if it's done enough in software

  • and then if you--

  • it's kind of complicated.

  • It's a little bit weird to predict because even that,

  • I would imagine that VMR would try to translate that

  • to the most efficient graphics API for your host operating system.

  • But I don't know.

  • It's hard to say.

  • My instinct would be that it's compiling--

  • that it's using a drawing API that's more efficient in the simulated VM

  • than is used natively on your host operating system.

  • But I have no data to say whether that's true or not.

  • Packopandas, how do you style your hair?

  • Asking for a friend.

  • It's mostly volumizing hair gel, a little bit of pomade,

  • and then hairspray.

  • And then drying the hair with all of those in it at the same time as well.

  • Yeah.

  • Zero indexing the weeks in a Lua course?

  • Yes.

  • I should have one indexed them.

  • That's a good point, actually.

  • Although we do use Unity, to be fair.

  • So there is a little bit of zero indexing involved.

  • Getting 60 frames per second on my Windows machine

  • and it's saying about 200 or 300 FBS in my Ubuntu VM.

  • Yeah.

  • I don't know.

  • That's a good question.

  • And especially using Ubuntu, I have almost zero experience.

  • I mean, not zero strictly speaking.

  • But I have very limited experience with Ubuntu and Emulation.

  • So I'm not sure.

  • That's a good Stack Exchange question, though.

  • And yeah, to mkloppenburg's point, you could

  • be having your Windows machine frame locking

  • and then your Ubuntu is unlocked.

  • And maybe your Windows machine is actually getting 600 frames per second

  • but your Ubuntu machine is getting 200 and looking as if it's getting more.

  • [INAUDIBLE] says, like the hair, too.

  • Sludgebeard, which is a great name, where are you taking the course?

  • EdX, Vsync, or refresh rate?

  • Yeah, those are related.

  • OK.

  • So awesome.

  • So we caught up with the chat.

  • And so Minesweeper, again, just to restate what we said earlier,

  • we're going to be making a version of Minesweeper.

  • We may or may not have the ability to make the entire game in one stream.

  • We'll try.

  • And if we don't get it finished by today's stream,

  • we'll get it finished next Monday or Tuesday.

  • But we'll take about three hours today, three, maybe 3 and 1/2

  • depending on how far we get, depending on how long we anticipate having

  • left by the time the stream is over.

  • I have never coded Minesweeper before, so this is sort of a blind run for me.

  • I have thought of the algorithms and done a little bit of research

  • into how people have done it, but I have not actually implemented this myself.

  • So this is from scratch for all of us together,

  • which is going to be exciting.

  • So it's a grid-based game.

  • So we already have that fundamental foundation

  • built upon from prior streams.

  • That will be easy.

  • We'll be doing things fairly well-engineered today just because it's

  • kind of a larger code base.

  • I say, let's just actually start diving in and get some code up and running.

  • And I'll be paying attention to the stream here.

  • I can actually type back into the chat today, unlike last week,

  • where I didn't have that functionality.

  • So I'm going to--

  • and let me know if this is a good size, if everybody can see everything

  • appropriately.

  • Everything is running at 720.

  • Should look great.

  • I'm going to create in my Streams folder, which is just

  • an arbitrary folder on my computer, I'm going

  • to create a sub folder called Minesweeper.

  • And this will be my folder in which I hold my source code for this Lua

  • project.

  • Love 2D expects that you have everything in a folder

  • or you have a main.lua somewhere in a folder.

  • That's your starting point.

  • I'm going to use VS code, so that's my text editor of choice.

  • It's a highly recommended text editor.

  • It's a really cool text editor.

  • It has a lot of features, a lot of great plugins, lot of great out

  • of the box functionality.

  • But you could use something like Adam or Sublime Text as well if those appeal

  • to you.

  • And I'm sure there are other text editors that are competitors

  • that I don't even know about.

  • I'm going to create a new file called main.lua.

  • For the regulars in the stream or the folks that have watched prior videos,

  • this is all kind of old hat.

  • But this is more just to help bootstrap the folks

  • that are brand new to the stream.

  • I'm going to create a comment block.

  • This is how you create a comment block in Lua.

  • I'm going to say Minesweeper, author, and email.

  • And so there's a few functions.

  • And we've looked at all of these before.

  • This is just part of programming a game in Lua in Love 2D.

  • I'm going to say, love.load.

  • i'm going to say love.update and love.draw.

  • And for good measure, I'm also going to say love.keypressed

  • just so we have that basic foundation.

  • These are all functions that Love 2D expects to exist in your main.lua.

  • It calls love.load once the beginning of the game.

  • It calls update every frame.

  • Get past this DT parameter, which controls basically your frame rate.

  • It controls your interpolation of movement and transformations

  • across different frame rates, rather, so that you

  • can do consistent movement between two machines that

  • are running at different speeds.

  • And then draw, it will basically do everything that's in the draw function

  • after update every frame.

  • So update and draw both happen every single frame.

  • Part of the game loop.

  • This thing called a game loop that Love 2D manages for you, something

  • that often you'll have to manage yourself.

  • NACLEric, caught the beginning of a 650 stream.

  • Nice, nice.

  • And Putevoditell, thank you very much for the host.

  • I appreciate that very much.

  • So yeah.

  • The game loop is sort of this infinite loop

  • that manages drawing and updating and taking an input.

  • And it does this usually once every sixtieth of a second.

  • So that's the frame rate.

  • Bevignite says, there's no mouse click only key press.

  • We will be doing mouse as well.

  • We'll basically be looking to see where their mouse is.

  • So we have to check to see where it is on the grid, which we can do

  • with love.mouse.getcursor, I believe.

  • It might be getposition.

  • And then we will also be doing mouse click as well.

  • This is to bootstrap us into getting a basic application up and running.

  • So I can do something like if key is equal to escape, then love.event.quit.

  • We can bootstrap our application up into something a little bit

  • more sophisticated with defining our own function for checking for input,

  • which we looked at in a prior stream.

  • Essentially Love 2D doesn't let you check

  • for single key presses outside of main.lua,

  • which is a problem if we want to check for single key presses

  • in a separate game state, for example.

  • Which we will probably end up taking a look at by the end of the stream.

  • Now I can run this application, which is cool.

  • And then it gives me a window, a black window.

  • And if I press escape, it'll actually let

  • me quit the application because they defined this bit of code

  • here that says in my love.keypress function, which is again,

  • a function that Love 2D provides you, and it

  • gets this key value for every single key you press, it gets that key value.

  • So we're basically just checking, if key is equal to escape,

  • the string escape, which is how Love 2D organizers

  • its symbolization of the different keystrokes on your keyboard.

  • If the key is equal to escape, then we'll just call love.event.quit.

  • That's Love 2D's way of quitting the application in code.

  • Let's actually figure out how we want to lay this out.

  • So in Minesweeper, and ours may not look exactly

  • like the Minesweeper that's here but it'll look similar,

  • we have all these squares.

  • And depending on where the squares are--

  • or sorry, depending on how the square is interacted with,

  • so whether it's unpressed, whether it's pressed,

  • and whether it's blank, a numerical square, or a bomb,

  • it has a different look.

  • So you can see that the squares that are unpressed look more three dimensional

  • and that's just because they have a little bit of lighting applied to them.

  • And we can probably do that ourself using something fairly simple.

  • What I want to do is, I want to use Push today, which is a virtual resolution

  • library.

  • And so if I go to something that we did before, like for example, not Snake.

  • I guess we didn't do that with Snake.

  • Did we do the Space Invaders?

  • We did.

  • So I'm going to copy the Push library.

  • So if you're curious, the Push library if you're doing this completely

  • from scratch--

  • I can't type for some reason.

  • Ulydev Push.

  • So Ulydev is the name of the user on GitHub that created this library.

  • So it's a library for Love 2D that basically

  • lets you simulate using a virtual resolution

  • no matter what your window size is.

  • So you can see here, this is kind of what that effect looks like.

  • You have this large--

  • this on my actual computer might be, I don't know,

  • 800 pixels tall and 400 pixels wide.

  • But it's looking as if it's maybe only 30 pixels wide and 30 pixels tall.

  • Because using a virtual resolution, it's basically

  • drawing to some tiny image the Mario information,

  • the little simple sprite graphics.

  • And then it's stretching that image to fit a native resolution raster, so

  • something on my actual screen.

  • So it's basically the equivalent of taking something that's 30 pixels

  • and turning it into 800 pixels but it maintains the look of it,

  • the crisp look of it.

  • It doesn't blur it or anything like that, which you can often

  • have happen if you're stretching a texture with filtering applied to it,

  • but you could even simulate in Love 2D if you wanted to.

  • But we're going to do this using the Push library.

  • We're going to get the same effect so we can draw a small 2D-looking grid

  • of stuff, of sprites.

  • And have it fill our window no matter how big our actual window is.

  • And have it look nice and crisp and retro.

  • And I already have-- you can download this library.

  • It's just a push.lua file from the GitHub repo if you type Ulydev Push.

  • I already have it on my machine from a prior stream.

  • So I'm just going to copy this push.lua and I'm

  • going to put it into my Minesweeper folder here inside of a lib folder.

  • And this is kind of conventional if you're doing any sort of development

  • and you have a library, someone else's code that you use.

  • You'll typically put it into a lib folder like this.

  • So that's all I have in my Minesweeper repo here.

  • I'm going to just test that this is working.

  • So we're going to be a little bit more well-engineered today

  • than we are in some other streams just because this is more of a larger code

  • base that we're going to be working with, or at least

  • a more rich set of code and logic.

  • So I'm going to create a source directory.

  • And in that source directory I'm going to create

  • a new file called dependencies.lua.

  • What this does is it basically allows us to store

  • all of our libraries, references to libraries, references to assets,

  • to sounds.

  • If we were to do sprite sheet splitting up,

  • this would be a good place to do it.

  • Just basically any resources that you're using in your game,

  • if those are libraries or artwork or sound, anything like that.

  • So I can say push equals require 'lib/push'.

  • This is relative to main.lua.

  • And what this is doing, is it's importing the push library

  • and assigning it to this push variable.

  • And this push variable is global to our entire game.

  • So regardless of whether it's in one file or another file,

  • we can use push anywhere, assuming that this

  • file itself has been imported in our game, which we're going to do

  • by just saying require dependencies.

  • So now this module, our main.lua has access

  • to push, that push symbol that we created earlier.

  • So in here, now that I have reference to push,

  • I'm going to say push setup screen.

  • Now what it expects is, it expects some references

  • to some resolution variables.

  • So a virtual width and a virtual height and then our actual window

  • within window height.

  • It's going to map the two together.

  • So I'm going to do a another file in my source directory.

  • I'm going to create constants.lua.

  • Another convention which is nice to adhere to,

  • especially if your game ends up being very large and have a lot of constants.

  • Something like Minecraft, for example, which

  • might have a ton of different constants for the different block types

  • in the game.

  • Or some other game, you can extrapolate that to pretty much anything.

  • I'm going to define a few constants in here that

  • are important to my game functioning.

  • So I'm going to say arbitrarily that I want my virtual width

  • to be 384 virtual height 216.

  • I don't know remember if this is the exact resolution I'm going for,

  • but it's something very similar to this.

  • It's like a 1920 by 1080 ratio that looks very similar to the SuperNintendo

  • era.

  • That sort of nice resolution.

  • So virtual width, virtual height.

  • But window width, I want this actual window on my machine to be 1280 by 720.

  • I want this to actually be a 720p window that looks

  • like it's from the SuperNintendo era.

  • So this is how you get that effect.

  • In my main.lua I can now, inside of push setup screen--

  • sorry, one more thing we need to do, we need to require source/constants.

  • And remember, we're importing this inside of main, this dependency.

  • So we get access to all of these things within main.

  • So constants, in this case.

  • So now I can say virtual width, virtual height, window width, window height.

  • And if we do that, we have to do a couple of other things.

  • So I have to say, push begin.

  • And then push finish.

  • Actually, offhand I don't recall whether it's push start or push finish.

  • We can easily change that.

  • Just to make sure that we are actually rendering everything

  • at the correct virtual resolution, I'm just

  • going to print something to the world.

  • I want to say, hello, Twitch in a love.graphics.print statement.

  • Run the game, and dependencies is not found

  • because what I did is I said require just dependencies.

  • This needs to be require source/dependencies.

  • Again, this is relative to where main.lua is.

  • Dependencies itself is within this source directory.

  • So I'm going to run this again.

  • And the method is not begin.

  • It is start.

  • So push start.

  • I always get those two confused.

  • And then now that we have everything running,

  • I'm drawing a 12 pixel font to the screen.

  • So by default, Love renders a 12 pixel font.

  • And it looks like it's actually pretty large on the screen.

  • So that's the result of the virtual resolution at work.

  • And it says, hello, Twitch.

  • It looks a little bit blurry.

  • So I'm going to make a small adjustment in my dependencies.lua.

  • At the very top I'm going to say, love.graphics.setdefaultfilter.

  • And what this does is, it just says, for every resource that you import

  • or you generate whether that's a font, whether that's a text,

  • whether that's a sprite, anything that's art or font related,

  • make sure that you do no filtering on it.

  • You just use what's called nearest neighbor filtering.

  • So it just basically ends up scaling it up or scaling it down

  • by sampling specific pixels and not interpolating pixels, which

  • is how you get bilinear, trilinear filtering, that sort of thing

  • and therefore that blurriness.

  • This will just make it look completely crisp and completely pixilated

  • no matter how big or how small we make it.

  • So if I re-run this, you'll notice that--

  • and it may be hard to see on camera, but we do indeed

  • have a much more crisp font.

  • There's no interpolation of pixel data.

  • Now, this font isn't ideal because it's an alias font, right?

  • Whoever designed this font assumed that it would be used for smaller use cases

  • and it wouldn't be blown up, it wouldn't be enlarged,

  • and it wouldn't be set to nearest neighbor filtering, right?

  • So what we need to do is, we need to choose

  • a more appropriate pixel-based font that doesn't get alias.

  • Aliasing is basically like somebody blurring the edges

  • with making the edges a lighter color or transparent.

  • That's essentially what aliasing is, getting rid of the jagged edges.

  • And that's why our text looks a little bit blurry, even though it's not.

  • It's because somebody who designed the font made the edges blurred.

  • Or if the font is generated, if that particular font,

  • that glyph size is generated, it's being aliased in the process.

  • On dafont.com, which is an excellent resource for getting fonts -

  • I'm going to zoom in just a little bit here - we can go to--

  • looks like they got rid of the menu.

  • If you go too zoomed in, the pixel bitmap section

  • here is excellent for that sort of thing,

  • for the retro pixilated font look.

  • I'm just going to choose an arbitrary one, just to get us up and running.

  • Let me see here.

  • The actual Minesweeper uses this LCD sort of font.

  • It might be hard to see, actually.

  • If I zoom in just a little bit, you can see it just a little bit better

  • there, this font here.

  • So I'm not sure if I'll spend time actually looking for a font like that.

  • But that would be cool if you were designing

  • your own version of this, that would be a design consideration to look at.

  • And the developers of the game made that design consideration

  • because to them, it looked like a bomb defusal type thing.

  • So this is an interesting way to look at or to at least contemplate

  • user experience and making your users feel like they're in the game.

  • And actually, this is a cute image.

  • I didn't see this earlier.

  • There's a soldier using an actual metal detector

  • on top of a texturization of the Minesweeper game.

  • So just basically taking it, putting into the real world.

  • Anyway, somebody's doing it here, too.

  • I'm not sure if that's real or not.

  • But yeah, that's the font that you could consider using.

  • We're not going to do that.

  • We're just going to use a simple bitmap font.

  • So let's pick something.

  • This one's kind of nice and simple.

  • This one's public domain.

  • So we'll use this one.

  • It's called Poco.

  • So it downloads a zip and then it'll download a ttf file usually.

  • I'm going to copy that ttf file into the folder in which I have the stream

  • source code.

  • I'm going to create a fonts directory.

  • Just paste that right into there, poco.ttf.

  • I'm going to go back here.

  • I'm going to go into my dependencies, which I'm already in here actually.

  • Now what I like to do when I'm writing a game like this,

  • I consider fonts and graphics and sounds all to be assets.

  • And I put them into global tables like this.

  • So g fonts with a lowercase g is short for global.

  • This is a good convention and it's often used in game programming.

  • You'll see it a lot in lower level languages, for example,

  • C++ or Java than Lua just because in that particular instance,

  • you need the readability.

  • And there are a lot more uses and easy uses of global variables.

  • But in this case, I'm using a global table called g fonts.

  • And I'm going to set an index to, it was called Poco, I believe.

  • And I'm going to say love.graphics.newfont.

  • I think that's what it was.

  • And then fonts/poco.ttf.

  • And we have a guest today with us here.

  • So he's getting his stool set up there.

  • So this is Rodrigo.

  • What's up, Rodrigo?

  • How are you.

  • RODRIGO SANCHEZ: Hi, everybody.

  • Let me just grab my coffee.

  • COLTON OGDEN: So Rodrigo has been on stream before.

  • So he played at least Mario.

  • Did you play Zelda with us as well?

  • RODRIGO SANCHEZ: Yeah, I did.

  • COLTON OGDEN: I didn't remember 100%.

  • And what are you going to be doing tomorrow?

  • You want to share with the stream?

  • RODRIGO SANCHEZ: Sure, yeah.

  • Tomorrow we're going to be doing a game of 15,

  • which is kind of weird to explain.

  • But essentially you have, say, 16 titles.

  • COLTON OGDEN: We can probably pull up an image, actually.

  • RODRIGO SANCHEZ: Yeah, it might be easier to show a picture.

  • COLTON OGDEN: So, game of 15.

  • And it has the name also, I think 15 puzzle is what a lot of people

  • also call it.

  • But it looks something similar to this.

  • RODRIGO SANCHEZ: So this is what it looks like when it's solved.

  • The numbers are usually scrambled in some way that's solvable.

  • And then you're just trying to shift tiles down to the right,

  • up, or to the left in order to make them in this configuration

  • where everything's from 1 to 15.

  • COLTON OGDEN: And you'll be doing this in Python?

  • RODRIGO SANCHEZ: Yeah.

  • We'll be doing it in Python.

  • COLTON OGDEN: So it'll look something probably similar to this, then.

  • RODRIGO SANCHEZ: Yeah.

  • It'll just be text on the terminal.

  • Nothing too fancy.

  • COLTON OGDEN: This kind of looks like a ridiculous--

  • Oh, is this actually a CS50?

  • This is a CS50 image here.

  • So this is from--

  • which year would this have been from?

  • It's in sig one with netbeans, hacker315,

  • it was probably several years ago.

  • But this is game of 80, which is insane.

  • RODRIGO SANCHEZ: Game of 80, oh my gosh.

  • COLTON OGDEN: That's crazy.

  • And this is a solver, too, I think.

  • Maybe not.

  • I'm not 100% sure.

  • Let me get you mic'ed up though, just so that you're audible on camera there.

  • RODRIGO SANCHEZ: I don't know how long I'll be in, but--

  • COLTON OGDEN: OK.

  • RODRIGO SANCHEZ: --be at least for more than a few minutes.

  • COLTON OGDEN: OK.

  • Well, if you're only going to be on for a few minutes,

  • we maybe don't necessarily need to mic you up.

  • RODRIGO SANCHEZ: I'll be in until Greg needs me or something.

  • COLTON OGDEN: OK.

  • So let me go ahead and I'll just mic you up just so it sounds OK.

  • This receiver's almost out of battery.

  • OK.

  • Where are the other batteries at?

  • For the sake of time, I'm not sure.

  • We don't have batteries around here.

  • RODRIGO SANCHEZ: That's all right.

  • COLTON OGDEN: Well, you should be able to hear Rodrigo pretty well on camera.

  • RODRIGO SANCHEZ: Let me see.

  • People are saying they like your hat.

  • Oh yeah, and Bhavik Knight says, game 15 is not

  • solvable from any random position.

  • That's exactly right.

  • And so we'll talk about tomorrow how to randomly generate

  • the board so that it is solvable.

  • COLTON OGDEN: Nice.

  • Well, I'm excited for that.

  • That'll be fun.

  • In the meantime, let's continue here with Minesweeper.

  • So I explained, I think I talked to you about it in advance

  • because we chatted earlier outside of stream.

  • RODRIGO SANCHEZ: I can be your rubber duck.

  • COLTON OGDEN: I appreciate that.

  • Sometimes I need one of those.

  • But this is Minesweeper.

  • You're probably familiar with it.

  • You've probably seen it.

  • So we're going to implement a version similar to this.

  • I don't know if it's going to be this big of a grid.

  • But we can probably start off with maybe a 10 by 10 or a 15 by 15.

  • And we could expand.

  • Because our code will probably be fairly flexible.

  • We can make it even maybe a random size, which would be pretty cool.

  • RODRIGO SANCHEZ: That'd be fun.

  • COLTON OGDEN: Rigo seems chill, says sludgebeard, Rigo is indeed chill.

  • Spidey27, limit test sounds fine.

  • [INAUDIBLE] 2017 was the last time it was live, says tuxman29.

  • Cool, cool.

  • Sick toque.

  • Not sure what that means.

  • Do you know what that means?

  • RODRIGO SANCHEZ: Not sure.

  • COLTON OGDEN: Sick toque.

  • Maybe that's a type of hat.

  • Is that what your hat?

  • RODRIGO SANCHEZ: It's supposed to be a pseudo winter

  • hat where it's actually R2D2.

  • I'm a huge Star Wars nerd.

  • COLTON OGDEN: Oh, nice.

  • I didn't even notice before.

  • RODRIGO SANCHEZ: If you look at me from a distance

  • it's like, oh, nice winter hat.

  • And then you get up close and it's like, whoa.

  • You're a droid.

  • COLTON OGDEN: So I guess that is the name of a hat, a toque.

  • RODRIGO SANCHEZ: Yeah.

  • It was a gift, so I'm not super familiar.

  • COLTON OGDEN: So the last thing we were doing

  • before you came in was, we were getting a font up and running on the game.

  • So we have just a window right now.

  • Just in, we got the nearest neighbor filtering set up.

  • We have text rendering, we have virtual resolution with the push library.

  • All cool, fun stuff.

  • Now we're going to actually change because if you notice,

  • the font is aliased.

  • It's the default Arial font, which isn't designed for this use case.

  • So it has sort of semi-transparent pixels used on it, or grey pixels,

  • rather.

  • And so what we're going to do is, I went on to dafont.com,

  • got a font that's meant for retro sort of bitmap, pixelated look.

  • It's not aliased, essentially.

  • And I created a new font object here in my dependencies.lua.

  • So within main.lua I can now, because I made g fonts a global object,

  • go into main.lua and say, love.graphics.

  • What I'm actually going to do, so I'm going to do it after push.setupscreen.

  • Love.graphics.setfontgfontspoco.

  • And I capitalized that, right?

  • I did.

  • So now what I should be able to do if I run this is, this will look different.

  • It will be a different font when I run the game.

  • And it is indeed.

  • And now the font itself, if you specify a size that's

  • non-standard for that font, so if we go back to dafont.com,

  • we can see that that font is designed to be used at 10 pixel size.

  • So I believe the default size for the font is 12 or 16 pixels.

  • But what we need to do is, we need to make it a multiple of 10.

  • So it can be either a single multiple, in which case it's 10 pixels,

  • or it can be 20 pixels, 30 pixels, 40 pixels.

  • If you don't specify the proper multiple,

  • it will alias because it's going to try and stretch it out or stretch it

  • down to a size that it's not intended to be drawn at

  • and cause some aliasing as a result of that.

  • So we're going to go over to dependencies.lua.

  • I can pass in a second parameter to the function love.graphics.newfont.

  • And then if I save that and re-run it, it is indeed, if you look at it,

  • not aliased anymore.

  • It's a bit small so we may end up deciding

  • that we want to use a larger version of it,

  • let's say 20 pixels, and then re-run it.

  • And so it looks something like that.

  • That's arguably a little too large.

  • So instead, I'm going to render it like that.

  • Now if we're using this just for a score,

  • which essentially is probably going to be the only use for it,

  • I can simulate this by saying, maybe 000.

  • And if I re-run this, that looks a little bit tiny.

  • So for the score, I might actually decide

  • that I want this to be at 20 pixels, re-run it, and that looks OK.

  • Now what I didn't realize is going into this

  • was that the font is actually vertically margined.

  • So there's some extra padding at the top of that font glyph.

  • So it's rendering at 00, the top left.

  • But it looks like it's getting pushed down a little bit.

  • So I kind of don't like that.

  • I might want to choose a different font for that purpose, for that reason,

  • to circumvent that.

  • This pixeled font has a pretty cool little number glyph set.

  • So I might actually do that.

  • So I'm going to download pixeled.

  • We'll do the same thing that I did before.

  • Open that up, copy that over to my stream.

  • So dev, streams, Minesweeper, fonts, pixeled.

  • And then I can go into here, create a new font object called pixeled.

  • Love.graphics.newfont, fontspixeled.ttf at size and what's the font size?

  • It was also 10 pixels.

  • So in this case I can say 10.

  • Might need to make this one 20 as well because it'll

  • be the same size as the poco font.

  • Render that.

  • We need to actually set that to be our default font.

  • So I'm going to say pixeled here instead of poco.

  • And again, that has margin.

  • So you know what it is?

  • No, there's no margin.

  • I screwed up the virtual resolution.

  • So what I need to do, I'm going to say list of 16:9 resolutions.

  • So earlier I decided on 384 by 216, I believe it was.

  • Right up here.

  • But apparently that's not a 1920 by 1080 perfect resolution.

  • So going to zoom that back out just a little bit.

  • So if you render at a non-16 by 9 resolution,

  • you're going to get some either vertical or horizontal padding,

  • the black padding depending on.

  • And we can't tell that it's black padding because we are rendering

  • our game with a black background.

  • We could do, for example, love.graphics.clear at 0.4.

  • Oh, OK.

  • In that case it also did it.

  • Am I just going crazy?

  • I may be going crazy.

  • No, I'm not.

  • So 384 by 216 is a 1920.

  • Did I just get unlucky and choose two fonts that were both vertically padded

  • by quite a bit?

  • That might be what it is.

  • That's strange.

  • For you, for personal use, pixel mix.

  • Can't use that because I don't think I can technically use that on stream.

  • I might be able to.

  • I like this press Start to play one.

  • I use this quite a bit.

  • So I'm going to do this one again.

  • It looks like I already downloaded it.

  • Let me just make sure that I'm not going crazy.

  • And then JHarvard, dev, streams, Minesweeper, fonts.

  • I'm going to call this one start.ttf.

  • Go back in here.

  • Print 000.

  • Let's create yet another font.

  • We'll call it start.

  • And notice that I have this table that I'm

  • storing just an arbitrary number of these fonts, which is kind of cool.

  • So you can have as many different fonts as you want in your game.

  • As long as you just choose the default font is when you draw,

  • that's the only thing that matters.

  • You will use this a lot if you design UIs that have different fonts.

  • You'll need a table that stores.

  • And this is why we use a table, because you

  • want to make a reference to potentially multiple different fonts.

  • I like Rodrigo's thinking face, says twintowerpower.

  • I like that.

  • OK.

  • So let's go ahead and set the default font to start.

  • See if this does the same thing.

  • OK.

  • So we just got unlucky and I chose two fonts

  • that happened to have some vertical margin, I guess, at the top of it.

  • And it led me to hypothesize that I had chosen an incorrect virtual resolution

  • because off the top of my head, I thought of 384 by 216.

  • But I was correct.

  • That's actually a 16:9 resolution.

  • That's important because, again, it needs

  • to map to the aspect ratio of your actual resolution, 1280 by 720.

  • So essentially what that means is, 384 divided by 216

  • should be the same thing as 1280 by 720 in order

  • to avoid the horizontal and vertical margins.

  • If I were to run, for example-- and I might actually

  • have to have terminal up.

  • But if I were to do Python, we can also do

  • Lua, but if I say 1280 divided by 720, and this is

  • Python 2 so this isn't going to work.

  • So Python 3.

  • So if I say 1280 divided by 720, that gives me 1.77777 infinite repeating.

  • This is the same thing as 16:9 aspect ratio.

  • That's that number, that constant number.

  • And then if I do 384 by 216, you'll see that that is the exact same number.

  • So those numbers being the exact same means

  • that the window's, assuming that you have them at the same resolution,

  • it'll fit perfectly.

  • The virtual resolution raster, the 384 by 216 raster that you're drawing to,

  • when stretched out, will completely fill and preserve

  • the balance of pixels, that 1280 by 720p window.

  • However, if I were to go into my virtual resolution, for example,

  • and change this to something like 340 by 216 and I run this,

  • you'll notice that on the left and the right

  • I do have some vertical bars to offset that difference in ratio.

  • And that is why it's important if you don't want those vertical bars in order

  • to keep your rasters at the same constant, 1.777.

  • That should be the result, the quotient between your width

  • and your height of your dimensions.

  • So yeah.

  • Fun stuff.

  • OK.

  • Let me just make sure that I can read the chat and keep up with it.

  • Out of bands, array exceptions, says tuxman.

  • Yeah, that only takes a few times before that

  • starts to get a little bit stressful, a little bit overbearing.

  • Randy says, these are not droids you're looking for.

  • These are not the droids you're looking for.

  • Star Wars reference.

  • 1223 in Nepal says, [INAUDIBLE],, I love all CS50 live streams.

  • Awesome.

  • Thank you so much for tuning in.

  • Bhavik Knight says score in time.

  • 11Lewie, you can name the table indices.

  • Yeah.

  • Yeah, you can do that in a lot of languages.

  • It's the same thing in Python, for example.

  • If using a dictionary, that'd be the same thing

  • as using a string as your key and then whatever value you want.

  • RODRIGO SANCHEZ: You can even use numbers or whatever you want.

  • COLTON OGDEN: Yep.

  • Exactly.

  • Yeah, you can use-- and there's some languages that let you,

  • if you have hashable objects, you can have

  • objects act as your keys, which is very cool and kind of crazy.

  • I don't think Lua is one of those.

  • I don't remember offhand if it is.

  • It's kind of an esoteric use case, too.

  • [INAUDIBLE] says, funny bits of programming.

  • So we've come a long way.

  • We have we have some text in the background.

  • I'm going to darken this color, just a little tiny bit in the background.

  • But we're not going to use just pure black.

  • We're going to keep it kind of a different color, kind of this dark

  • gray.

  • This'll be our background.

  • What I want to do is, I want to start sketching out the game, the grid,

  • the 2D grid that will act as our Minesweeper grid, I guess.

  • The things that we can actually click and then reveal them on the screen.

  • So let's do a faux score and timer at the top, which

  • is what we've done in the past.

  • So I'm going to print.

  • So at the top left I'm just going to say,

  • we'll assume it starts at 120 seconds.

  • So it'll count down from 120 going down to zero.

  • And I'm also going to love.graphics.printf.

  • And printf is what allows us to format the printing so we can center things

  • or align them.

  • In this case, we're going to right align them.

  • I going to say love.graphics.printf0000.

  • Eh, just zero for now.

  • And then we want to right align this.

  • And so printf if you're using it in your aligning something within some bounds.

  • It'll start at zero.

  • It's going to also start at zero height.

  • It's going to take up the virtual width as its center, its alignment zone,

  • you can sort of think of it.

  • And then I'm going to not center align it but right align it.

  • And then if I run this, you'll see.

  • And it's somewhat difficult to see on the stream there.

  • But I do have a zero on the right side of the screen at the top right.

  • And that's going to be where we have our score, the user's score as they play.

  • Cool.

  • So that's working.

  • Well, we'll wire those together at some point in the near future.

  • Now, what I want to do, let's go back to Minesweeper.

  • The actual graphic and let me zoom in just a little bit here.

  • I want to have a few things happen.

  • So there are a few things we can look at that are fairly simple, especially

  • if you've watched prior streams.

  • One thing is we're going to need tiles.

  • We're going to need two separate tiles it looks like,

  • at least two separate tiles.

  • So we have one tile and this is a pretty poor resolution to be looking at.

  • But we have a tile that is unrevealed.

  • So it's this tile here.

  • So in this case, you can see that it's a little bit three dimensional-looking.

  • And the way they accomplish this was they

  • chose a center color of some sort of pale gray.

  • They used a lighter color for the left edge and a darker color

  • for the right edge.

  • A very simple way to get a three dimensional look for your game.

  • Now what size would this be?

  • This would probably be maybe 10, 12 pixels.

  • If you use an eight-pixel font we could probably achieve this.

  • Let's try 12 pixels.

  • So what I'm going to do is, I use a program called Aseprite, which I like.

  • And Aseprite is a drawing application.

  • So kind of similar to Photoshop, kind of similar to something like GIMP.

  • It's used mainly just for drawing 2D sprite work for games like this.

  • To answer pudavodatel, are you sitting or standing?

  • We are thankfully sitting.

  • I wouldn't want to stand for three hours.

  • Tuxman, can you use emojis?

  • RODRIGO SANCHEZ: We were talking about table indices.

  • COLTON OGDEN: Oh, sure.

  • Probably not.

  • Probably not in Lua.

  • Maybe in Swift, because I think you can do that in Xcode,

  • you can use emojis in the actual programming language.

  • Not in this programming language.

  • Greetings from Germany.

  • Love your video, says sensotouch3d.

  • Thank you very much.

  • Appreciate it, senso.

  • [INAUDIBLE] says, think this channel is cool.

  • Definitely need to fill in some info below the stream with links, schedule.

  • I agree.

  • I'll take a look at doing that as soon as possible.

  • We have to kind of formulate a lot of that stuff.

  • But yeah, you're absolutely right.

  • We want some behind the scenes video.

  • Show us what it takes to stream.

  • I would watch it.

  • It would be cool, says [INAUDIBLE].

  • Yeah.

  • We could potentially do something like that.

  • I'll ask David and our production team if there'd

  • be something they'd want to do and we could maybe do something like that.

  • Have like a stream of the stream going on in the background.

  • Little bit of stream-ception.

  • Back to the thing here, I'm going to create a new sprite.

  • And it's going to be 12 by 12 pixels.

  • So let's do that.

  • So this is going to be very ultra tiny.

  • Can I zoom in like that?

  • I can.

  • Ultra tiny.

  • Even though this looks large right here, it's actually

  • going to be a very small drawing.

  • I'm going to choose this pale gray.

  • Like we said earlier, we need a pale gray, a light gray, and a dark gray.

  • So I'm just going to fill this in and I can actually do this with the fill tool

  • here.

  • I'm going to go to my pencil.

  • I'm going to choose a lighter color.

  • So I could choose white, but that's a little obnoxious.

  • I'm just going to go up to here and choose that.

  • And then again, it was kind of like this on the left going down.

  • And then a darker gray, so something like this on the right.

  • And you can kind of see that, actually, in the preview here.

  • It may be a little bit difficult to see, but when it's shown at smaller scale,

  • it really does look quite three dimensional.

  • Now, I'm going to save this as a PNG.

  • Not a PCC, a PNG.

  • I'm going to go.

  • And it's already set to stream.

  • In our tic-tac-toe stream, we made our Xs and circles from scratch.

  • I'm going to go up to streams, into Minesweeper.

  • And I'm going to create--

  • it looks like I don't have the appropriate folder here.

  • I'm going to save it in here and then I'll move it in the Finder.

  • I'm going to say, this will be tile.png.

  • It'll just save it as a PNG.

  • So if I go back to my Finder, notice that we do indeed now have a tile.png.

  • And now, what you see here illustrates what filtering is.

  • So we saw very clearly in our actual Aseprite program

  • when we were doing the sprite artwork, that it was crisp, you know?

  • We saw the individual pixels of our 12 by 12 image.

  • But Mac OS and Windows OS, I believe, by default

  • apply filtering to all image previews just so they look kind of nice

  • as you're looking through all your stuff.

  • But when you're looking at something very small like this, like a 12

  • by 12 pixel, it looks blurry.

  • It looks like it's actually at a higher resolution than it is.

  • And this is what filtering looks like versus nearest neighbor

  • or point filtering or no filtering.

  • So this is a perfect illustration of what that is.

  • Now what I said before is I'm going to move this.

  • And what we generally want to do is have a separate folder for your graphics.

  • So I'm going to go ahead and I'm going to take tile.png,

  • put that in my graphics folder.

  • I now have a tile that I can draw on the screen.

  • To illustrate that this looks OK when drawn in a grid,

  • I'm going to, in my main.lua, do a simple loop.

  • I'm going to say 4y gets one until 10.

  • Do, just put a space there before the end of that.

  • Actually going to put a space between push start and the rest of the code

  • just for style.

  • This is a nice thing I like to do stylistically, just

  • to illustrate semantic difference between the different things you're

  • doing in a large chunk of code.

  • So splitting out the fact that we're starting

  • with the push rendering and then the font rendering,

  • and then separately our loop that's going to draw all of our tiles.

  • And then separate from that, the actual closing

  • of the push block, that's a nice way to space out your code.

  • Makes it easier to read and understand what's going on

  • and to separate things semantically in chunks.

  • So for y, it's 1 to 10.

  • This is a nested loop.

  • I'm going to say 4x is 1 to 10, do.

  • And what I'm going to do is draw this.

  • So I'm going to say love.graphics.draw.

  • And we have nothing to draw yet.

  • So that's another thing that we want to do.

  • I'm going to go into dependencies.lua and I'm going to say G textures.

  • And so typically you'll hear 2D graphics referred

  • to as textures or rasters, in this case, textures.

  • So I'm going to say, G textures is equal to, and we'll call this tile.

  • And we'll say love.graphics.newimage.

  • And this is going to be graphics tile.png.

  • So just loads that tile.png that we created from our Aseprite.

  • And we put it in a folder.

  • Just going to load it from disk in that graphics

  • folder just like it does with the font file, the ttf file, as well.

  • I'm going to say love.graphics.draw, G textures.

  • And I'm going to say tile.

  • I'm going to say--

  • and we have to draw this at a particular xy location.

  • So I'm going to say x - 1, actually no.

  • I'm just going to draw it at x, actually.

  • I'm going to say x times, and then in constants.lua I'm

  • going to say tile size equals 12, right?

  • Try to take magic numbers out of your source code as much as you can.

  • You could say we're using it here, debatably.

  • But this is just to illustrate something.

  • We will get rid of this.

  • But we will not get rid of, for example, the tile size.

  • We're going to use that a lot.

  • I'm going to say tile size, y times tile size.

  • And that should actually just work right off the gate.

  • And if I run this, you indeed can see that we have sort

  • of a Minesweeper-like grid being drawn.

  • And it's offset a little bit because we're drawing this not at 00,

  • we're not starting at 00 because we're actually starting the xy rendering

  • at one.

  • Notice that y and x start at 1, they don't start at 0.

  • So I'm actually drawing relative to the top left.

  • I could say x - 1 times tile size and y - 1 times tile size.

  • And what this will do is, this will actually

  • drive, based on the top left corner.

  • And this is nice and clean and flush with our window.

  • But it obscures the timer that we put earlier in our actual grid.

  • When we get to the game itself, when we lay everything out

  • the way we properly want to, it's not going to be at the very top left.

  • It's going to be kind of shifted down, kind of centered in the game world.

  • So, yeah.

  • Looks kind of nice already, right?

  • RODRIGO SANCHEZ: I like it.

  • COLTON OGDEN: Very simple, too.

  • Just a very simple idea.

  • Something that you see a lot in sprites and in UI.

  • Just make the far left edge of something a light color,

  • it's kind of in the same palette as your central color.

  • And then make the far right a darker version of that same palette.

  • Let me make sure that I caught up.

  • Is CS50 available somewhere?

  • The CS50 website is all about some event and that is it.

  • And then looks like, yeah.

  • Tuxman kindly put in cs50.edx.org.

  • Got to go.

  • Thanks for the great stream, as always.

  • Learned about better folder hierarchy from my future project.

  • Awesome, thanks tuxman.

  • Appreciate you tuning in.

  • Glad that you were able to pull something away from today's stream.

  • Cool.

  • So we have our grid being drawn.

  • We have a little bit of a tutorial on drawing some basic sprite stuff.

  • That's not the only tile that we want, though.

  • Because if we look at the image here, we can see that when we do click a tile

  • and that tile has been depressed, it's actually

  • very similar to our other tile.

  • But it doesn't have a light edge any longer.

  • Now what it has is just a dark left and top edge and then

  • nothing on the right and the bottom.

  • And so it's very simple.

  • We can do the same exact thing that we just did.

  • I'm going to go back to Aseprite.

  • I'm going to copy this.

  • Going to draw this here.

  • So keeping the left edge intact, and then I'm

  • going to take this dark color that we used before.

  • I'm just going to make that the left edge just like that.

  • And then I'm going to save that.

  • Not what I wanted to do.

  • Whoops.

  • I'm going to save that.

  • I'm going to reapply.

  • I'm going to command shift save that to create a new image.

  • And I'm going to call this tile depressed, like that.

  • And actually, it saved it again here.

  • So it didn't actually overwrite the file in our graphics folder.

  • But even still, you want to watch out for accidentally doing something

  • like that.

  • So tile depress, put that in the graphics.

  • I'm going to go ahead to delete that.

  • Now you could put those onto a separate sprite sheet.

  • Sorry, you could put both of those together onto one sprite sheet.

  • But given that they're so small and we're only

  • going to really have two separate tiles, we're

  • going to see that we're actually going to draw

  • everything else on to these tiles.

  • We're going to basically draw the tile and then layer

  • on top of that some text or an icon.

  • There's no real need or reason for us to go

  • through the effort of creating a sprite sheet.

  • And then additionally, splitting that sprite sheet in code.

  • We can avoid doing that in this case.

  • A 3D-looking unimpressed grid would do.

  • Not sure what you mean, because that was kind

  • of the goal of what we just accomplished,

  • Bhavik, if you want to elaborate on what you mean by that.

  • OK, so cool.

  • What I'm going to do now is I'm going to--

  • let's test to make sure this actually works.

  • So I'm going to create a grid.

  • I'm going to create an empty grid.

  • I'm going to say 4y is equal to 1 till 10.

  • Do 4x is equal to 1 do.

  • And then I'm going to say table.insert into grid in another empty table.

  • I'm basically creating a 2D table here.

  • And then I'm going to say, table.insert into grid y, which

  • will now exist by the time this happens, because we

  • will have just inserted a table.

  • I'm going to insert into that either a one or two.

  • So in this case, math.random, two.

  • And another important thing that we should do,

  • actually, is seed our random number generator.

  • So going to say, math.randomseedos.time.

  • If you're not familiar with what this does,

  • this just seeds our random number generator with our OS time

  • to always be different every single time we start the game.

  • So this means our random number generator will function differently

  • every single time we play the game.

  • So we've created a 2D grid here.

  • Every single index.

  • So this is going to be mapping to our actual tile grid.

  • Every single index in our 2D grid is going to be either a one or a two.

  • And so what I can do down here is, I can say grid at yx is equal to one

  • and this or gtextures tile depressed.

  • RODRIGO SANCHEZ: And underscore instead of the hyphen for tile depressed?

  • COLTON OGDEN: No, no.

  • Because what we're going to do is I'm going to go into dependencies.

  • But I haven't done this yet.

  • I'm going to create another index here called tile depressed.

  • And this is not really a formal convention.

  • I would say I've seen it more than I've seen this for indices, at least

  • in the context of Lua.

  • But my experience might differ from your experience.

  • I'm not sure whether there's a formal standard for this.

  • But I typically like to use hyphens to be my indices for tables in strings.

  • And I'm going to say love.graphics.newimagegraphics tile

  • depressed.

  • And underscores are more of a convention for actual files.

  • So there's kind of a dichotomy here, which

  • you could argue could lead to a source of bugs for people assuming

  • that it will be underscores or for developers even

  • assuming it'll be underscores.

  • But there's semantic value, I guess, in knowing that that's

  • the table key versus a file.

  • So maybe that's OK.

  • But in any case, I have this implemented.

  • And now if I render the game and it's all randomized,

  • you can see that we do have a grid of tiles

  • and we do get this nice faux 3D look, right?

  • You get that tile.

  • We sort of tricked our mind into thinking that that's lower

  • or that tile is higher than that tile on the left.

  • So very simple things that add up to make

  • a pretty nice-looking UI for our game.

  • Now, do we have any more comments that we should read?

  • RODRIGO SANCHEZ: Just some chatting.

  • COLTON OGDEN: OK.

  • Cool, cool.

  • So, Bhavik-- or, sorry, Bellocure has kindly pitched my game dev course.

  • Hevron is saying, that looks amazing.

  • Bhavik's saying, cool.

  • I agree.

  • This is super cool.

  • And I want to say, I have never implemented this before.

  • I just happened to know these puzzle pieces

  • that I've picked up from working on this game, working on this game,

  • working on this game.

  • Making observations about the game that I'm trying to implement.

  • And then actually orchestrating that in code.

  • And you can sort of predict how things are going to look and behave

  • the more experience that you get.

  • And so I went into this knowing what the outcome of this would be.

  • But even I, looking at this, I'm like, wow.

  • This actually looks pretty cool.

  • I think this is pretty awesome.

  • One thing we could do, we could actually darken

  • the background just a little bit, just a shade

  • to give it even more of a 3D look.

  • So I think I might do that.

  • I'm just going to sample this color here.

  • And then I'm going to bring this over just a little bit.

  • And then fill that in.

  • Save it.

  • And what that will have done is it actually

  • created a new tile depress there.

  • I'm going to replace that one there, re-run the game,

  • and now we have an even more exaggerated three dimensional look to our grid.

  • So cool.

  • I'm enjoying this so far.

  • This is moving along.

  • Now it's not centered, which is kind of a problem.

  • So what I probably want to do is take care of that.

  • So why don't we go ahead and do that.

  • So in order to center it, what we basically want to do

  • is effectively start drawing it at the x point that

  • is the half point of our virtual width minus the total size of the grid.

  • So we take this whole virtual width, we subtract from it

  • the total width of this, which will give us this space here.

  • Then we divide this space by two, start drawing at that on the x-axis,

  • and that will have the effect of drawing this grid.

  • Is that the case?

  • No, we actually have to divide this by two as well and shift that to the left.

  • I might be getting myself confused here.

  • No, that should be right.

  • Yeah, that's the margin.

  • Yeah, that's the margin.

  • That should be correct.

  • I could be mentally having a little bit of brain fog at the moment.

  • But we're going to try that.

  • So I'm going to say, local.

  • We can actually pre-calculate this.

  • So I'm going to say local padding is equal to virtual width

  • minus the total grid size, which would be--

  • so another thing we should do, and actually this is important,

  • grid size or rather, grid width.

  • And this will be 10 and then grid height will also be 10.

  • And we can change this.

  • The way that we're actually creating this right now,

  • we can have this be programmatically centering.

  • Because we're going to take this into consideration when we actually

  • figure out the margin of our game or the padding of our game grid.

  • So I can say, virtual width minus grid width times tile size, right?

  • So that'll be the number of tiles and the width of the tiles.

  • Sorry, the size of the tile which is 14 pixels and then multiply that

  • by the number of tiles in the grid.

  • So that'll be the number of pixels wide that our grid is in total.

  • So I'm going to say--

  • and this will actually still--

  • because of the order of operations work the same way as the parentheses,

  • but it lets us kind of keep track of what

  • we're doing a little bit easier here.

  • So I'm going to say virtual width minus the grid width times tile size.

  • And then I'm going to divide that by two.

  • And this will actually be more appropriately named as left padding

  • or left offset, I should say.

  • So this will be, again, our padding between the grid

  • and the virtual width's difference of that.

  • And then divide that by two.

  • And then if I say left offset plus where we want to draw the x and then I render

  • this--

  • oops, I screwed that up.

  • OK, so that's not working.

  • Virtual width minus the grid size.

  • Is it this?

  • No, that's the same thing.

  • OK, so we have the--

  • let me just make sure I'm thinking about this correctly.

  • We have the virtual width.

  • We need to take the difference between the full grid, which will be something

  • like, what, 140 pixels?

  • Wait.

  • We have virtual width minus that.

  • RODRIGO SANCHEZ: Each tile is what?

  • How long?

  • COLTON OGDEN: Wait, does it actually need to be virtual width minus that

  • and then we're minus that?

  • That's what it is, OK, I think.

  • I might be incorrect.

  • It looks roughly correct.

  • Basically, I was taking the--

  • if we were to get rid of that--

  • wait, am I not dividing it by two anymore?

  • RODRIGO SANCHEZ: No.

  • COLTON OGDEN: So the virtual width, these always

  • gets slightly-- depending on what problem you're trying to solve

  • can get a little bit hairy.

  • So we're taking the grid or rather our virtual width

  • minus the grid, which will be that amount, right?

  • So virtual width minus--

  • let's figure this out.

  • That divided by two, right?

  • So will that work?

  • There we go.

  • OK, got it figured out.

  • So the virtual width minus the grid's width in total.

  • And then we divide that whole thing by two.

  • So I was dividing it by two before actually

  • subtracting it, which was causing it to behave inappropriately.

  • That is completely centered.

  • You can tell based on the untitled right here.

  • And then I can draw this, shift it down on the y as well.

  • I'm just going to make this arbitrary.

  • So top offset, I'm going to say that's, let's say 40, 40 pixels.

  • And then I can say, top offset plus that.

  • Cool.

  • So now it's completely centered, everything looks pretty good.

  • Now what I want to do is to be able to choose a tile with the mouse, which

  • is what Bhavik Knight said before.

  • And let me just make sure I'm keeping up with the chat here.

  • Rodrigo nods approvingly.

  • Windows fine for programming?

  • I see many developers use Unix-based systems

  • and it confuses me a little bit.

  • Yeah, Windows is fine for programming.

  • Unix tends to make it a lot easier to program in certain contexts.

  • But I mean, game programming is often done on Windows machines.

  • You can program a lot on just Windows machines

  • without too much of a headache.

  • But I would say, I think it's a little bit easier to get into programming

  • and to be productive and to get bootstrapped in a Linux or Unix

  • environment just because there's a lot of tools to easily get set up,

  • a lot of package managers that make it easy.

  • Mac, for example, with Homebrew you can get set up really fast

  • and it's not really that easy to do on a Windows machine in the same way.

  • RODRIGO SANCHEZ: It's a lot of-- if you're Googling around

  • for help, a lot of the examples that you'll find assume that you're

  • running a Google space system.

  • COLTON OGDEN: Yeah.

  • Agreed, agreed.

  • And it looks like Bhavik Knight-- yeah, Bhavik Knight had the right idea there.

  • Yeah, [INAUDIBLE] had the idea.

  • I was dividing by two at the wrong time, which was causing a problem.

  • Foursunlight says, hello [INAUDIBLE],, Rodrigo, the regulars, and newcomers.

  • Thanks for joining us.

  • JPguy, good to see you.

  • Cool, cool.

  • All right.

  • Let's go ahead and maybe break out some of this into different files.

  • So for example, there is an idea of a grid, right?

  • A game grid.

  • Let's rename this actually to Game Grid because I think grid is just

  • a little bit too generic of a class.

  • I'm going to go ahead and this is going to be a class.

  • So what I want to do is, I'm going to take the class library

  • that we used last time or rather--

  • wait, did we use it last time?

  • Yeah, I think we did.

  • Yeah.

  • No, we didn't use it there.

  • Where did we use it?

  • I forgot offhand.

  • Did we use it in Space Invaders?

  • We did.

  • So it's an unfortunate name, Love 2D class library helper utilities.

  • I'll say use the helper utilities for massive progression.

  • And yeah, it's an unfortunate name and unfortunate acronym.

  • But it's a class library that they use and that is what we'll be using.

  • So here it is.

  • VRLD/ helper utilities for massive progression.

  • Within the Love 2D community, they have some unfortunate

  • naming conventions as kind of a set of jokes and conventions.

  • But that is the library, the specific class library that you want.

  • I have it pre-downloaded because we used it in Space Invaders.

  • I'm going to copy it over to my--

  • where is it?

  • Minesweeper into my lib folder.

  • And so now I have a class.lua in there, so that's

  • going to be my ability to use object-oriented programming classes.

  • If you don't know too much about that, I think

  • the goal is to eventually start doing a object-oriented programming stream.

  • I'm going to find somebody to do it.

  • Maybe me, maybe somebody else.

  • But suffice to say, this will allow us to put together

  • all of the functions and data that make up a game grid

  • and put them into one module.

  • And that's what we're going to do.

  • So in Game Grid I'm going to say-- well first of all,

  • what I need to do in dependencies is say, require rather,

  • class equals require 'lib/Class.'

  • And it's their convention to capitalize class.

  • And it's nice because it kind of almost feels like new syntax in Lua,

  • and the fact that Lua is such an extensible,

  • simple language allows you to do stuff like that.

  • We'll say game grid class.

  • This block comments is not terribly useful,

  • but this might be where you include a description of how

  • the class should behave.

  • I'm going to say game grid equals class.

  • And notice that I made it a global variable by not specifying local.

  • So again, in Lua, you can say that something

  • is local, which means that it won't be accessible

  • anywhere outside of its scope.

  • In this case because we defined it at the top level of the scope

  • in this module, it can be used in any function within main.lua.

  • But if you tried to use it, for example, within Game Grid,

  • even though the two can talk to each other, it's not going to be accessible.

  • Veronny, thank you very much for following and theyashbhutoria,

  • thank you very much for the follow.

  • So I'm going to say function game grid init.

  • So the init is sort of the constructor function,

  • the function that will create this object

  • and set it up for whatever it needs to do.

  • In this case, it will need a self.width.

  • Let's assume that it takes a width and height.

  • It'll take a self.width equals width and a self.height equals height.

  • Self refers to if we have a specific instance of game grid,

  • in particular game grid object, that particular game grid's width should

  • be set to however we pass it with in a constructor for it, height

  • and the constructor for it.

  • And again, we can probably do an object-oriented programming tutorial

  • in the future.

  • So if this kind of goes over your head a little bit, I apologize.

  • But kind of just take it.

  • Think of it as this game grid, the game grid class defining what a game grid is

  • and what functions that game grid can use.

  • And then if we were to go in main.lua, I can specifically

  • create an instance of game grid.

  • I can say, for example, local game grid equals game grid width and grid height.

  • And this right here, this is just one instance of game grid.

  • But we could create as many as we wanted to just like this, right?

  • And they all take the same constructor, the same game grid constructor.

  • This is the same thing as the init function right here.

  • Notice that it takes a width and a height.

  • These take a width and height and it creates a unique game grid

  • for each one of those lines of code.

  • So yeah, that is basic object-oriented programming, I guess.

  • It gets a lot more complicated than that that's

  • in a nutshell it's essentially just what objects and classes are.

  • This is a class, this is an object, and a class

  • is kind of like a blueprint and an object

  • is a specific implementation of that class.

  • So in any case, we're going to be able to define all of the behavior,

  • including the drawing and updating of our class within the game grid

  • class itself.

  • I could say a game grid update.

  • And game grid render.

  • And what this will allow us to do is put all of the code

  • that we essentially put in main.lua to do all of the drawing

  • and updating for our grid, or rather, just the drawing.

  • I can take this and I can instead say something like game grid render

  • right here.

  • Defer the rendering to the class itself right in here.

  • So I can say now, for one until and we don't want to use 10.

  • We want to use self.height and self.width.

  • And all the rest should be pretty good.

  • Now, top offset no longer exists in this current scope.

  • And that is left offset.

  • So what we probably want to do is make those constants.

  • Well, actually, no.

  • Top offsets is not going to be a constant.

  • But what we can do is we can say self.leftoffset.

  • We keep it as a variable within the game grid itself.

  • You can also argue for-- oh no, because it maintains its own rendering logic.

  • So we want to actually keep the offset within this class, probably.

  • So I can say take this bit of code here, delete it, bring it over here,

  • copy that there.

  • We do still have virtual width, grid width,

  • and tile size references within this module

  • because these are global constants.

  • We put them into the constants file.

  • They can be accessed within any module.

  • So our game grid can make references to those variables of all those constants.

  • And then now this works fine.

  • The left offset is no longer a global variable.

  • It's a member variable.

  • This has a specific reference to its left offset.

  • It's calculated it at the beginning of its instantiation.

  • It calculates it as soon as the constructor is called.

  • After it gets the width and then it gets the height,

  • it figures out what its left offset should be.

  • And actually, this should not be width grid width.

  • This should be self.width.

  • But virtual width and tile size are always going to be the same.

  • It's just the actual width is going to be different.

  • And then after that, we're going to make top offset a constant.

  • So I'm going to say, top offset.

  • Put this in here.

  • Top offset is equal to 40.

  • I'm going to get rid of that in main.lua.

  • And then this is fine.

  • This is fine.

  • We're rendering the game grid in here.

  • So if I were to run this, we have an issue because again, it's a new module.

  • We're not actually requiring it anywhere.

  • We have to do that first before we have access to it.

  • I'm going to say require source/gamegrid just like that.

  • And now it indeed works.

  • All of the rendering logic and everything is included within the grid

  • itself, within the class itself.

  • This object has a reference to its own functions, its own data.

  • We don't need to keep this in main anymore.

  • That should be one of your goals when you're implementing a game,

  • keep everything outside of your main.lua.

  • Try to defer as much logic as you can into different modules.

  • Keep those modules as small as you can.

  • What this gives you the ability to do is separate your concerns a little bit.

  • It allows you to think in smaller chunks at a time.

  • You don't have to mentally parse a ton of information.

  • And if Rodrigo and I are working on a game together, I can say,

  • Rodrigo, I want you to make this game grid class

  • and this is how it should behave.

  • It should have a render and update function.

  • I'm going to focus on just the architecture of the game.

  • I'm just going to focus on main.lua right now

  • but I want you to do the grid by yourself.

  • And now he has the ability to do that because the grid is all

  • taken out of our main.lua.

  • So that's probably the most valuable aspect of it,

  • especially if you're working on a team of people.

  • But even if you're working by yourself, the mental assistance

  • that you give yourself by separating things out and making it more modular

  • is helpful in and of itself, and can boost your productivity.

  • Let me make sure that I didn't miss any comments here.

  • Never played Minesweeper, actually.

  • How does it work again?

  • Sure.

  • So we'll end up implementing this ourself.

  • But essentially you have a grid of these tiles that start off completely empty.

  • And when you click on a tile, what it does

  • is it recursively checks all of its neighbor tiles

  • and sees if those are a number.

  • If they're are number, then it stops recursively iterating through the board

  • and opening tiles.

  • If it's just an empty tile like this, it checks all of its adjacent neighbors

  • and does a recursive, are those a number or are they empty?

  • And opens all of those up and those all open up their neighbors

  • until, again, they reach a terminal point.

  • It's the base case for your recursive algorithm, right?

  • The base case is that it's a number.

  • And that's pretty much it.

  • And then if you click on a bomb, which is what these numbers refer

  • to is, how many bombs are near me?

  • So it takes into consideration all your tiles, whether they're diagonal or not.

  • So you can you can surmise that because this tile, for example,

  • right here is a one, that means that there's one bomb neighboring that tile.

  • The only tile unrevealed is this diagonal tile right here.

  • So we can say with 100% certainty this tile is a bomb.

  • We know that's a bomb.

  • There's no way that cannot be a bomb, right?

  • And same thing for this two here.

  • This two, because there are two tiles neighboring

  • it that are unrevealed, only two tiles that are unrevealed

  • and because this number is a two, meaning

  • that there are two bombs neighboring this tile,

  • we can say with 100% certainty that both of these tiles are bombs.

  • This three, same idea.

  • This three, it only has three neighboring unrevealed tiles.

  • Three bombs, therefore, all of those are bombs, right?

  • And so that's the gist of the game.

  • That's the game loop.

  • Your goal is to--

  • using, is it induction?

  • Deduction?

  • The Difference

  • RODRIGO SANCHEZ: Deduction?

  • COLTON OGDEN: Deduction.

  • I always get these two confused.

  • You're supposed to surmise whether tiles that you haven't clicked on are bombs

  • or not.

  • And you're essentially trying to find bombs, you're sweeping for bombs.

  • It's just a number game.

  • It's similar to Pick Cross, which is another number game.

  • Using number hints you figure out which tiles you can pencil in or not.

  • RODRIGO SANCHEZ: I like twintowerpower's rule about playing the game.

  • COLTON OGDEN: You press the grid and hope the bomb goes off, I think.

  • That's a good heuristic for playing, yeah.

  • Lot of greetings going around.

  • Others are saying hello.

  • JPguy was saying hello to everybody.

  • Yawn.

  • Cool.

  • Unpress all the mines without triggering the bomb.

  • [INAUDIBLE] thinks the naming funny for the library we referred to earlier.

  • Cool.

  • Give it a little hug.

  • Go online.

  • Yeah, that's true.

  • You can probably find a version to play online really easily.

  • How about on next stream we try to build a game called Dangerous Dave?

  • Fun little game to play.

  • Lots of childhood memories.

  • Dangerous Dave.

  • Hopefully my safe search doesn't give me any issues.

  • OK, interesting.

  • Dangerous Dave.

  • So it's kind of like a simple platformer.

  • And you collect gems and you find the door?

  • Is that the gist of it?

  • RODRIGO SANCHEZ: Looks do-able.

  • COLTON OGDEN: To me that looks like the gist of it, yeah.

  • It's similar to what we do with Mario, the Mario piece set.

  • Little fire obstacles, we could do like a simple platformer at some point.

  • In the spirit of Dangerous Dave or Mario or something like that.

  • Yeah, we could do something like that.

  • I don't know if it'll be the next stream but I'll definitely

  • keep it on the docket, for sure.

  • Thank you for the suggestion.

  • You don't have to deal with one huge file of codes, says [INAUDIBLE]..

  • Yeah.

  • And that's a big headache.

  • You don't want to do that.

  • It's a pain just trying to look through and seeing where problems are

  • and tracing them back and forth.

  • Much easier to look module by module and then

  • get an easier understanding of what you're dealing with as a whole,

  • holistically.

  • RODRIGO SANCHEZ: At least for [INAUDIBLE]..

  • COLTON OGDEN: Yeah, yeah.

  • Encapsulation, collaboration.

  • Yep.

  • Exactly.

  • Grids corner is three neighbors, edge is five neighbors.

  • There is eight neighbors.

  • Bummer, man.

  • Yeah, essentially that's-- yeah, for a corner you would only have-- well,

  • for a corner you would have three neighbors in the corner, yeah.

  • Five neighbors and an edge and eight neighbors anywhere else.

  • Yeah, that's correct.

  • Cool.

  • All right.

  • So again, just because I love seeing this.

  • This is our grid.

  • What we should do is, first thing that we should do is plant bombs, right?

  • So what we can do is, each grid, therefore, each index in the grid

  • needs to have a reference to whether it's a bomb or not.

  • It needs to know that.

  • It needs to know that piece of information.

  • Because if we're going to need to check each tile individually,

  • hey, are you a bomb?

  • If not, then also if we've discovered you, we need another attribute.

  • Have I clicked on you?

  • Are you visible, right?

  • So if you're a bomb, you may be a bomb and invisible.

  • If you're invisible, maybe you're not a bomb.

  • So maybe you have a number of bomb neighbors

  • that it's all going to be hidden initially.

  • But each tile does know how many bomb neighbors it has.

  • So it needs a reference to that as well.

  • And that number is going to be hidden until we click on it and unreveal it.

  • So we have a few variables, flags that we want to keep a reference to.

  • Now the game grid itself isn't going to maintain a reference to this.

  • This is going to be each individual index in the grid, right?

  • That's going to be an actual tile, right?

  • And so what we can do is we're going to need to create another class.

  • We're going to need to create a game tile or a grid tile class, right?

  • Let me go ahead and do this.

  • Grid tile class.

  • This is again, useless comment block but it's a good habit

  • to get into when you want to actually fill this out with a description

  • or with some information about how to use the class.

  • I'm going to say, grid tile is equal to class

  • just like we did with the game grid.

  • I'm going to say function grid tile init.

  • Function grid tile update.

  • I'm not sure if we're going to need update.

  • We will probably want render.

  • So the grid tile will defer all of the rendering.

  • This is another nested deferring of functionality.

  • We're taking from main, we're deferring the rendering of the grid

  • to the game grid class.

  • But within the game grid class, we're then

  • deferring the rendering of each tile to the grid tile class.

  • So things kind of trickle down and they defer.

  • And then you eventually do get these spider webs of functionality.

  • But this, again, it's easier to go down and see things at a more granular level

  • and separate your concerns or your worries

  • rather than have to grok through a tremendous body of code.

  • You can sort of isolate the problem a little bit more easily.

  • RODRIGO SANCHEZ: It's like a company.

  • COLTON OGDEN: Yeah, it is.

  • It is.

  • Essentially, yeah.

  • It's like having a president that defers to vice president or maybe a CEO

  • or board of directors that defer to a CEO, CTO, COO.

  • Those have teams.

  • The CTO might have a technical team.

  • The CEO might have a marketing team or a financial team.

  • Those might have interns, they might have full-time staff.

  • Those might be managers, which have interns, which have full-time staff.

  • You do get this hierarchy a lot in the real world.

  • And it maps well to games for the most part.

  • Now there are people that say that object-oriented programming is horrible

  • and we should use functional programming.

  • We should use other paradigms.

  • We should use logical programming.

  • But however you can think of the problem and solve it

  • more easily, that's essentially a step in the right direction.

  • Whatever gets your software done better and faster, better also

  • being bug-free, more maintainable.

  • Faster isn't always necessarily the best thing.

  • Maintainability is also very important.

  • Whatever gets that done ultimately is good in my opinion.

  • So what we want to do is render the actual tile.

  • And now the tile is going to maintain a reference to its xy.

  • So what we can do is I can say, it's going to need a reference to it's xy

  • because it's going to need a reference to--

  • is it necessarily true?

  • It might not need to reference it's xy, actually.

  • We might be able to get away without doing that.

  • It will minimally need whether it's a bomb.

  • So what we can do is, we could just set it in here.

  • So bomb.

  • So if the bomb is equal to, rather, better naming is bomb.

  • Self.isbomb.

  • So we've made it a Boolean variable, is, we've semantically changed its purpose.

  • So self.bomb could be anything, but self.isbomb,

  • now we know that that should be a Boolean expression.

  • And we'll just set that to the value of whatever is bomb is.

  • So in the constructor for the grid tiles,

  • we're going to actually determine whether something's a bomb or not.

  • I'm going to say self.hidden or rather, is hidden is equal to true.

  • So every tile, when it first gets created,

  • is going to be hidden by default so that players of the game

  • have to click that tile before it gets visible.

  • And then we're going to say self.bombneighbors, something

  • that you should not do in real life.

  • Self.bombneighbors is going to be equal to, let's say zero for now.

  • RODRIGO SANCHEZ: And a convention that I was introduced to was,

  • just like you did, bomb or isHidden for Booleans for counter variables,

  • kind of like num bomb neighbors or something like that.

  • COLTON OGDEN: Yeah.

  • We could do that, too.

  • Num bomb neighbors.

  • So it's a good semantic clarification of the variable.

  • So self.numberofbombneighbors is equal to zero.

  • And now what that's done is that's taken this away from ambiguously

  • being a verb to being a noun.

  • So now we know this is actually a noun 100%.

  • So number of bomb neighbors because we set that to zero.

  • And so what we can do now--

  • RODRIGO SANCHEZ: Bomb neighbors could be a function.

  • COLTON OGDEN: Yeah, exactly.

  • It could.

  • It could.

  • Something that maybe recursively detonates all of your neighbor tiles.

  • Wouldn't work in this game, but you could think of something like that.

  • So grid tile render, this essentially will outline the code, more or less.

  • But what is it going to do is it's going to basically say, if self.isHidden,

  • then we just want to draw the unrevealed tile, right?

  • So I can say love.graphics.drawgtextures and what this

  • should do is be given an x and a y.

  • Because we're going to call this from main.

  • We're going to call this from the grid.

  • We're going to offset this value in a nested for loop within the grid class

  • and pass in this value as an offset to this

  • so that this knows where to draw at the correct location.

  • So I'm going to say g textures tile and this is going to be just a plain tile.

  • And then I'm going to draw this at xy.

  • And then else, if it's not hidden, I want

  • to say if self.isbomb then love.graphics.drawgt

  • exturestitledepressedxy.

  • And then on top of that, I'm going to say love.graphics.drawgtexturesbombxy.

  • So we're doing two draw calls here.

  • We're saying, first draw the tile depressed.

  • And then on top of that, draw the bomb.

  • And so what we need to do is we need to actually draw the bomb sprite, right?

  • So let's go ahead and do that.

  • I'm going to say what we should probably do is draw all the numbers, too.

  • So in here what I can do--

  • what we could have done this whole time, actually,

  • is just draw the bomb onto this and save it separately.

  • So I might actually end up doing that.

  • And we can make it one draw call that way.

  • I'm going to just--

  • let's just draw a bomb here.

  • So I'm going to say boom, boom.

  • Boom, boom, boom, boom.

  • So I'm a terrible sprite artist but hopefully this works OK.

  • We want that.

  • So it's probably something like that.

  • And normally I would do that and then I would probably

  • make this a lighter color.

  • That's too light.

  • And then I would say probably something like that.

  • And then maybe like a bright color, like a red or an orange.

  • Yeah, something like that.

  • So it kind of works.

  • You can see, you get the gist of it.

  • Maybe even for a little bit of decor here

  • we can put a little shiny thing like that.

  • So you can tell kind of what it is.

  • But that's going to be our little bomb image.

  • It looks almost like a pouch more than a bomb.

  • But you get the idea.

  • Would it make more sense--

  • RODRIGO SANCHEZ: There's a little shout-out in the chat.

  • COLTON OGDEN: Would it make more sense if I did something like this, actually?

  • If I did like that?

  • Does that look more like a bomb?

  • RODRIGO SANCHEZ: That looks way better.

  • COLTON OGDEN: OK.

  • Cool.

  • So that's an actual bomb.

  • I'm going to save that in graphics.

  • I'm going to save that as bomb.

  • And I'll read the chat here in just a second.

  • Bomb.png.

  • I'm going to erase all of this stuff here.

  • Sort of overwrite it.

  • And I think it might be easy just to ultimately draw

  • all the numbers as separate sprites.

  • OK, cool.

  • Go ahead and figure out where I left off.

  • OK.

  • So [INAUDIBLE] is saying, the React Redux course you did last year

  • helped me a lot.

  • Now I'm working as a React name developer full-time.

  • Thank you a lot.

  • Oh, that's awesome.

  • So that was Jordan's course.

  • So Jordan Hayashi did a React native course on edX.

  • Super cool course.

  • Talked about React with the React basics, JavaScript,

  • advanced JavaScript, and then React Native.

  • So it's super cool that you got a job, [INAUDIBLE]..

  • And then also that you were able to conduct a workshop, that's incredible.

  • So awesome.

  • Congratulations.

  • Good job.

  • Keep working hard.

  • I think it's mobile.edx.org.

  • I believe that should be the course that you want to link to.

  • Oh, wait.

  • Is that right?

  • Mobile.edx.org?

  • Sorry, cs50.edx.org/mobile?

  • Yeah.

  • Sorry, my apologies.

  • Oh, yeah.

  • And Martin kindly tossed in the chat there.

  • Yeah.

  • I screwed up.

  • Ignore my link.

  • Mobile.edx.org is not anything.

  • That's a fictitious link.

  • It's fake news.

  • I'm going to now segue from drawing the bomb.

  • What I want to do is I want to draw all of the letters that

  • make up the tiles that have been revealed already.

  • So let's go over to here.

  • I'm just going to go ahead and choose a random color.

  • So and this may be terrible.

  • So let's go ahead and do something like that.

  • RODRIGO SANCHEZ: That's pretty good.

  • COLTON OGDEN: Something like that.

  • Thank you for your encouragement.

  • Much appreciated.

  • RODRIGO SANCHEZ: It's better than the bomb.

  • COLTON OGDEN: Yeah.

  • I think that's OK, right?

  • It might be slightly off-center.

  • No, even if we shift it to the right, then it'll

  • be off center on the right side.

  • So it's fine.

  • But that's OK, because this is also dark.

  • OK.

  • If I do this, then move that one over like that.

  • RODRIGO SANCHEZ: I think even though it looks strange in the zoomed in version

  • in the smaller version, when you shifted it to the right it looked centered.

  • COLTON OGDEN: When I shift it to the right in the smaller version?

  • RODRIGO SANCHEZ: Yeah.

  • I was looking at the preview.

  • COLTON OGDEN: Like that?

  • RODRIGO SANCHEZ: But it's kind of hard to tell.

  • I don't know.

  • I think either way it looks kind of off.

  • COLTON OGDEN: I think this looks OK.

  • For the sake of speed we'll go through this.

  • And I'll just call this 1.png.

  • And then deselect.

  • And I want to make these all different colors.

  • So let's go ahead and just erase this.

  • So two was red, I believe.

  • No, two is green.

  • Two is green.

  • So I'm going to go ahead and choose green here.

  • What does the two look like?

  • OK, something like that.

  • So I'll say like--

  • this is going to be terrible.

  • RODRIGO SANCHEZ: Got to start somewhere.

  • COLTON OGDEN: Yeah.

  • So let's see, how does that compare?

  • So it looks like it goes around like that.

  • Goes like that.

  • Great.

  • RODRIGO SANCHEZ: I mean, it's definitely legible.

  • COLTON OGDEN: Yeah.

  • It's legible.

  • I mean, it looks a little bit like trash.

  • RODRIGO SANCHEZ: It looks like a circle, like a cobra.

  • COLTON OGDEN: It does a little bit, yeah.

  • There.

  • That's a little bit better.

  • RODRIGO SANCHEZ: Yeah.

  • That looks a lot better.

  • COLTON OGDEN: OK.

  • RODRIGO SANCHEZ: This one seems more shifted down.

  • COLTON OGDEN: It does, yeah.

  • I don't want to spend too much time on it.

  • RODRIGO SANCHEZ: That's good, though.

  • COLTON OGDEN: I'll try shifting it up.

  • There we go.

  • RODRIGO SANCHEZ: Looks a lot better.

  • COLTON OGDEN: Cool.

  • And then I need to--

  • I'm not an artist at all by any stretch of the imagination.

  • And I'm sure that that's very clear.

  • But if you are a single developer working by yourself,

  • you will sometimes have to do something like this.

  • And so using simple techniques that don't

  • require you to be super artistic like the three dimensional technique

  • we looked at earlier, that's kind of a good way

  • to make your game look good without going too crazy with the artwork.

  • Emulating things that you see that are fairly simple, that's kind of the best

  • way to go about it I think.

  • Three's red.

  • So we'll do that.

  • OK, so how does that look?

  • I mean, I know what the number three looks like.

  • But for the sake of emulating the sprite artwork.

  • It's acceptable, right?

  • RODRIGO SANCHEZ: Yeah, yeah.

  • COLTON OGDEN: Do I want to shift it up?

  • Does that look better?

  • RODRIGO SANCHEZ: To the right a little bit.

  • COLTON OGDEN: We'll go with that.

  • We'll take that.

  • If I were doing this appropriately--

  • [CLAPPING]

  • --I appreciate it.

  • What I would be doing is ensuring that all of these glyphs are the same size

  • and therefore could be centered uniformly.

  • But I'm not doing that just for the sake of speed, just because we have--

  • oh, crap.

  • I saved the wrong one.

  • Shoot.

  • RODRIGO SANCHEZ: Uh-oh.

  • COLTON OGDEN: So what I'm going to do, yep.

  • RODRIGO SANCHEZ: And you can save the other one.

  • COLTON OGDEN: Save that and then redo that.

  • And then that's it, right?

  • OK.

  • And then save that one as three.

  • RODRIGO SANCHEZ: The magic of undo.

  • COLTON OGDEN: Undo is a beautiful thing.

  • OK.

  • I apologize if this is a little bit slow.

  • RODRIGO SANCHEZ: Actually, people are commenting that they like this.

  • COLTON OGDEN: Oh, OK. cool.

  • What do I-- so I need four.

  • What does four look like?

  • Four is like a purple, it looks like.

  • I can't really tell.

  • Well, we'll go with that.

  • So I'll say this is four.

  • And then four looks kind of like-- so they do like the--

  • RODRIGO SANCHEZ: Are they doing the corner?

  • COLTON OGDEN: It's going to be something like this.

  • RODRIGO SANCHEZ: You're doing the nine four.

  • COLTON OGDEN: Yeah.

  • Yeah, exactly.

  • Looks kind of like trash.

  • I mean, it looks actually pretty similar to theirs.

  • Maybe I will get rid of this.

  • Move over here.

  • Go like that.

  • I'm OK with that.

  • I think that looks OK.

  • It's not great.

  • It's not perfect. it's not beautiful.

  • But it works, right?

  • RODRIGO SANCHEZ: Yeah, I think that looks good.

  • COLTON OGDEN: Does that look OK?

  • That looks good.

  • OK.

  • RODRIGO SANCHEZ: And give some more personality.

  • COLTON OGDEN: Yeah, exactly.

  • Trash sprite work for the win.

  • What does a five look like?

  • Let's see.

  • So five is like a burnt orange or like a dark, dark red.

  • So I'll do that.

  • So go here.

  • And get rid of this.

  • I already used this color.

  • So I can just use regular orange.

  • So it'll be five.

  • So five looks like, OK, it looks basically like an S in this case.

  • So I'll do something like that.

  • That looks actually, that's actually really hard on the eyes.

  • RODRIGO SANCHEZ: The color?

  • COLTON OGDEN: Yeah.

  • It needs to be darker.

  • RODRIGO SANCHEZ: I think theirs was a lot darker.

  • COLTON OGDEN: That's hurting me a little bit.

  • OK, we'll do something like this.

  • That's better.

  • Starting to get light-headed looking at that color.

  • OK.

  • I mean, you could just use that as is.

  • It's not fancy.

  • It doesn't have any fancy glyph work on it.

  • But it works, right?

  • You can tell that's a five.

  • RODRIGO SANCHEZ: Do you think the bottom and top should be thicker?

  • Like two lines, maybe?

  • Nah.

  • COLTON OGDEN: Probably not.

  • It looks pretty similar to the other one.

  • Just has a smaller resolution.

  • So we'll do that.

  • Yeah, that should be fine.

  • Something like that or like that or like that.

  • RODRIGO SANCHEZ: I think it looks fine.

  • COLTON OGDEN: You think it's good?

  • Cool.

  • So that's five.

  • RODRIGO SANCHEZ: How many are in it?

  • COLTON OGDEN: There are eight, I believe.

  • RODRIGO SANCHEZ: Great.

  • COLTON OGDEN: OK.

  • We're getting there.

  • What is the-- this is someone else's version, too.

  • That's different.

  • Different version of it.

  • Where is a six?

  • Come on, six.

  • There's a seven there.

  • Oh, six.

  • OK, so six is kind of like a blue green.

  • Holy crap.

  • RODRIGO SANCHEZ: Wow.

  • COLTON OGDEN: That is--

  • RODRIGO SANCHEZ: --illegible

  • COLTON OGDEN: That an insane-- that looks like a map.

  • RODRIGO SANCHEZ: Yeah.

  • I was about say, like Civilization.

  • COLTON OGDEN: Looks like Florida a little bit.

  • Man, OK.

  • That's insane.

  • That's some next level Minesweeper right there.

  • I mean, if we did the algorithm correctly for generating the map--

  • RODRIGO SANCHEZ: We should be able to.

  • COLTON OGDEN: We could theoretically make this and it would work just fine.

  • It to be kind of slow during the initial.

  • Because what we're going to have to do is we're

  • going to pass over our entire grid.

  • And the grid, it's going to basically need

  • to check every single tile for a bomb.

  • And it's going to check every tile for how many bombs are around

  • that tile to give it the number.

  • RODRIGO SANCHEZ: The question is whether we would trigger a stack overflow.

  • COLTON OGDEN: Doing what?

  • RODRIGO SANCHEZ: Like, if the map got big enough, right?

  • The number of tiles.

  • Or not?

  • COLTON OGDEN: Oh, for the recursive.

  • RODRIGO SANCHEZ: Oh, wait.

  • The map isn't recursively generated.

  • It's just checking.

  • COLTON OGDEN: The map's not recursively generated, no.

  • RODRIGO SANCHEZ: Unless it just so happened that there were bombs every so

  • often and so you ended up taking the entire map or something.

  • I don't know.

  • COLTON OGDEN: Yeah, and even then, I don't think

  • that there would be that many calls.

  • Like if you multiplied--

  • I mean, I guess it could theoretically be a stack.

  • RODRIGO SANCHEZ: It would be very unlikely.

  • COLTON OGDEN: Yeah.

  • But theoretically I could see it happening, yeah.

  • All right.

  • So six will be a sort of a weird greenish blue color I think.

  • This color.

  • Maybe?

  • So six.

  • This might be actually a little bit too dark.

  • RODRIGO SANCHEZ: Their six looked pretty light.

  • COLTON OGDEN: We'll try this.

  • Looks like a G.

  • RODRIGO SANCHEZ: G.

  • COLTON OGDEN: Move that over here.

  • RODRIGO SANCHEZ: The four and the six.

  • COLTON OGDEN: That's a pretty good six.

  • RODRIGO SANCHEZ: That's better.

  • COLTON OGDEN: I'm proud of that six.

  • I'll say that.

  • K, come on.

  • Six, there we go.

  • Let's go ahead and make seven.

  • What color was seven?

  • Seven was like a--

  • RODRIGO SANCHEZ: Well, you did the six in the seven color, I think.

  • So you could do the seven in the six color if you wanted.

  • COLTON OGDEN: I'll do this as a seven.

  • Why not?

  • RODRIGO SANCHEZ: Seven should be--

  • COLTON OGDEN: Not like that.

  • Try that one more time.

  • And then boom, boom, boom, boom, boom.

  • It's too slanted.

  • RODRIGO SANCHEZ: Honestly, I draw my sevens like straight.

  • But that actually looks-- that's a good.

  • Do you want to be consistent with the--

  • COLTON OGDEN: Yeah.

  • Which means I need to start up here.

  • Actually, I can't fit it.

  • Oh, I guess that works OK, Right?

  • RODRIGO SANCHEZ: Yeah, I can see it.

  • COLTON OGDEN: It's a big seven.

  • It's bigger than the other numbers, but I'm not too picky at this point.

  • I kind of just want to finish making the numbers.

  • Eight is-- I guess it doesn't really matter what color we choose

  • as long as it's different, right?

  • RODRIGO SANCHEZ: True.

  • COLTON OGDEN: Can make eight a yellow or something.

  • Maybe something like, yeah.

  • Like bright yellow.

  • RODRIGO SANCHEZ: Like, this is really bad.

  • COLTON OGDEN: Yeah.

  • RODRIGO SANCHEZ: You got really lucky.

  • COLTON OGDEN: I hope it's not painful to look at.

  • I guess it's not terrible.

  • OK, so boom boom.

  • Whoops.

  • Boom, boom.

  • That's a pretty solid eight.

  • RODRIGO SANCHEZ: That's probably--

  • COLTON OGDEN: The best number so far.

  • It's a shame because you probably won't see it that often.

  • Again, apologies to the people on screen that are bored watching me

  • make numbers.

  • RODRIGO SANCHEZ: How to make numbers in CS50.

  • COLTON OGDEN: Yeah, I know, right?

  • Purple, we'll make this nice bright purple our nine.

  • RODRIGO SANCHEZ: That looks really good, too.

  • COLTON OGDEN: It's pretty solid.

  • RODRIGO SANCHEZ: But I thought you said there was only eight.

  • COLTON OGDEN: Oh, right.

  • You can't have nine neighbors.

  • That's silly.

  • OK.

  • RODRIGO SANCHEZ: Such a shame.

  • Looked so good.

  • COLTON OGDEN: I got into a loop, into a routine.

  • OK, cool.

  • So that's good.

  • We did all that.

  • That's beautiful.

  • We made all of our images.

  • For the sake of time, I'm not going to put them on a spreadsheet

  • and then do a programmatic like indexing of the tile table in order to do it.

  • I'm just going to load them all as separate textures.

  • Where do I want to be?

  • I want to be in here.

  • So I'm going to go over here.

  • And then I'm going to, one at a time, index

  • them with the string one, two, three, four, five, six, seven, eight, nine.

  • Although, I could do it with just the number itself and then that way

  • I could index into the table with that number when we look at it.

  • Or I can just two-string it.

  • Actually that's fine.

  • OK, cool.

  • Love.graphics.newimage graphics/1.png and then I'm going to kind of do this.

  • So that will be two, three, four, five, six, seven, eight.

  • Cool.

  • So now we have eight index indices into our grid there.

  • Bomb as well.

  • We got to do bomb.

  • That's important.

  • So bomb is going to be equal to love.

  • Love.graphics.newimagegraphics/bomb.png.

  • Cool.

  • Go over here to--

  • should I run it?

  • So we didn't actually set the thing to bomb, right?

  • So what we can do is, in our game grid, so another one of the things that we

  • need to do is instead of doing this four loop where

  • we draw just a statically-allocated grid of numbers in advance,

  • or rather numbers that map to textures via this ternary statement, what

  • we should do is actually instantiate all of the indices in our grid.

  • So what we can do is I can say, self.grid equals that.

  • And this is basically taking what's in main.lua and putting it into here.

  • And actually, grid is still referenced as a global variable from main.lua.

  • So that's why that's still even working.

  • But now I can say 4y is equal to one until self.height do.

  • And this allows us to actually support rectangular grids.

  • So I can do 4x is equal to one self.width

  • because now those numbers are separate.

  • Then I can say--

  • what we also need to do is table.insert into self.grid a new table

  • because it's a 2D table.

  • And then I'm going to do table.insert into self.grid at y.

  • Not a number one or two, but rather a new grid tile here.

  • So I can say grid tile.

  • And then in the grid tile is where I can say

  • local is bomb is going to be equal to math.random2.

  • So a 50% chance in this case, which is way too high,

  • I think, but just for demonstration.

  • And true or false.

  • Yes.

  • And then I'm going to say is bomb right in there in the grid tile,

  • because again our constructor takes in it is bomb variable

  • and Boolean to be true or false.

  • So what I'm going to do then is--

  • and in this particular instance, I'm also going to randomly determine--

  • I'm going to illustrate that not all depressed tiles need to be bombs.

  • So what I can do is I can just make it revealed as well.

  • So I'll make whether it's revealed or not a Boolean, I guess.

  • Or rather, what I can just do is say local is revealed

  • is going to be equal to math.random2 and true or false.

  • So now it's not only a 50% chance that it's a bomb, but a 50% chance

  • that it's also revealed.

  • And then I'm going to say--

  • what I should do is that I should say, local grid tile is equal to this.

  • So the reason I'm doing that is so that I

  • have a reference to this variable, that grid tile

  • that I'm going to add into the table.

  • I want added and then I'm also going to say hidden is equal to is revealed,

  • which is actually the opposite of what--

  • RODRIGO SANCHEZ: It's in.

  • COLTON OGDEN: Yeah.

  • So this should be is hidden.

  • Giving everything it says, in this case it doesn't really matter.

  • Either way we're just setting something to true or false at random.

  • But the semantic of that is important to take into consideration if we're

  • doing something more complicated.

  • Now, this will give us grid tiles that are randomly bombs, randomly hidden,

  • and a mixture of the two, right?

  • So if it's a bomb, then I'm going to draw.

  • Then it's a bomb.

  • Else, right?

  • Because if it's not hidden it could be either a bomb or not.

  • And so I'm going to draw whether it's a bomb or not.

  • And then eventually what I'm going to do is,

  • we're going to have that number attribute associated with each tile,

  • whether it's two, three, four, five, six, seven, eight.

  • And if it's any of those, then when we draw that instead of tile

  • depressed, basically.

  • And draw a bomb if it's a bomb.

  • And then if it is a bomb as well, then it wouldn't be inside render.

  • It would be inside of update or something when we actually click it.

  • If it's a bomb and we discover it, then that's

  • also going to be the trigger for the game over, too.

  • Because when you hit a bomb you get game over and then you get your score.

  • I have not been looking at any of the chat.

  • I apologize.

  • So let's do that.

  • Nine is impossible, yep.

  • [INAUDIBLE]

  • I got into a mental auto-pilot.

  • Wow, there's a lot of chat.

  • OK.

  • RODRIGO SANCHEZ: There it is.

  • COLTON OGDEN: OK.

  • Cool, cool.

  • Slytherin, the audacity.

  • Yeah, adamantium was saying, it is super useful

  • to see the process of making sprites.

  • Yeah, and again, I use Aseprite but there

  • are a lot of other tools like Photoshop and Gimp you can use to make sprites,

  • too.

  • It's kind of up to personal preference.

  • Aseprite is catered specifically to words 2D sprite development.

  • But it's also not free.

  • So you'll want to take that into consideration.

  • RockdayStudios, in my game developer, Godot

  • is an open source 2D 3D game engine.

  • No fees, no lawsuits.

  • Yeah, I've seen Godot.

  • I'm familiar with Godot.

  • Thank you for mentioning it, though.

  • Press F for two.

  • [INAUDIBLE]

  • Do you know what that's a reference to?

  • I'm not sure.

  • RODRIGO SANCHEZ: Forgot what that was.

  • COLTON OGDEN: Apologize.

  • I'm not sure what that means.

  • RODRIGO SANCHEZ: Maybe that's what we were creating the number two.

  • I don't know.

  • COLTON OGDEN: [INAUDIBLE] says, if it's an ad, it's a good one.

  • Yeah, I mean, people should be encouraged to mention technologies

  • in stacks that they like.

  • So no shame in that.

  • I think if somebody does it over and over and over again

  • and if it's something that's obviously a product that's trying

  • to be sold it should be a problem.

  • But I think mentioning Godot as open source is totally fine.

  • And people should feel encouraged to check out

  • those tools, those pieces of software.

  • Cool.

  • Instead of checking every tile, why not add a counter

  • around the bomb object which is hidden for the player?

  • Or was that the idea?

  • Yeah, every tile is going to be pre-calculated

  • so it's going to be cached.

  • Every tile is going to know exactly how many bombs are around it.

  • So when you click a tile and--

  • is that the case?

  • It's going to essentially recursively look at all of its neighbors.

  • If it's a bomb, it's not going to reveal it.

  • If it's another tile with a number that's not a bomb,

  • it's going to reveal that tile, which will then reveal its other tiles.

  • And if it's empty, it will reveal that tile

  • and also trigger the recursive thing.

  • So empty and numeric tiles have the same recursive functionality.

  • But if it's a bomb or an edge, that stops the recursive algorithm

  • from continuing.

  • And also actually-- well, no.

  • Is that the case?

  • Is that true?

  • No, that's not the case.

  • So if it is a number, it doesn't--

  • if it's a number, it's also an ending point.

  • So at a number, the number will stop recursively looking through tiles

  • as well.

  • That's a base case for the recursive algorithm.

  • If it hits a number tile it stops there.

  • It just basically hits a wall at that point.

  • Has anybody seen an eight in Minesweeper?

  • Yeah, I feel like that would be pretty tough to actually get.

  • But I'm sure a lot of people have seen it.

  • We need a puzzle with a small map and huge number of bombs.

  • Yeah, yeah, yeah, yeah.

  • What color were you making?

  • Yeah, troll.

  • Yeah.

  • Brain fart.

  • We need a nine and a zero for the sake of completeness.

  • Yeah, kind of feels like that.

  • [INAUDIBLE] Minesweeper, guys, don't worry.

  • Yeah.

  • Yeah, the extra z layer.

  • 4D Minesweeper, holy crap.

  • Nine not needed.

  • Idea.

  • What are your thoughts on a flag is needed.

  • What's the flag?

  • Do you remember what the flag is for?

  • I've seen it in Minesweeper but I honestly

  • don't remember what it's used for.

  • RODRIGO SANCHEZ: I haven't played it since I was, like, six.

  • COLTON OGDEN: Yeah.

  • If you want to let me know what the flag is for, I actually don't know.

  • 3D would need 26.

  • Oh, yeah it would.

  • It would need 26 indices for a 3D, a three by three by three.

  • Check around the tile.

  • Not nine but a flag, a flag and an exclamation point.

  • People are asking about validators on the web

  • I think validators at the HTML site are still circumventable.

  • So ultimately, you probably want back end validation.

  • Depends.

  • RODRIGO SANCHEZ: [INAUDIBLE].

  • COLTON OGDEN: [INAUDIBLE].

  • I like coming in here.

  • Every time I fail to understand what's happening

  • and that motivates me to go continue my backlog courses.

  • I don't know whether to take that well or not,

  • but I appreciate you coming to join us.

  • If you have any questions along the way, definitely let us know.

  • I would recommend starting out with the snake or the tic-tac-toe streams,

  • though, because those are a little bit easier

  • and they give you a good jumping point.

  • Depends on your audience.

  • I guess it's a bit of a mix.

  • But if your main audience is on mobile, you

  • could go mobile first and make it adaptable for bigger screens.

  • Yeah, they're talking more about the mobile stuff.

  • Press F to pay respect when you overrode the two number.

  • Oh, I see.

  • I see.

  • [INAUDIBLE]

  • Do the bombs get generated after the first click?

  • Bombs get generated at the beginning of the map.

  • So the whole map is completely generated at the very beginning, all the bombs,

  • and all of the neighbors of the bombs get their numbers pre-calculated.

  • And then you just reveal them by clicking on the mouse

  • and recursively revealing tiles.

  • Use the flag to make where you think are bombs, useless.

  • Flag is for marking a bomb that is--

  • oh.

  • So the ability to actually say, oh, I know this is a bomb.

  • To mark it.

  • OK, OK.

  • Yeah, I'll have to think about that.

  • I didn't think about that, actually, earlier.

  • But yeah, that would be smart.

  • And that would actually be pretty simple.

  • All you would have to do is--

  • we'd have to go into a mark mode.

  • And so if they click it when they're in mark mode, it just changes that.

  • Basically turns on a flag skin for an unrevealed tile.

  • So yeah, that's something we could do.

  • RODRIGO SANCHEZ: I'm actually about to head out.

  • COLTON OGDEN: OK.

  • Sure, sure.

  • Well, it was good.

  • RODRIGO SANCHEZ: Yeah.

  • Tomorrow I'll be here for the full time.

  • COLTON OGDEN: Oh yeah, that's true.

  • Yeah, JPguy just said, [INAUDIBLE] can check out chat while you program.

  • But Rodrigo is unfortunately leaving.

  • RODRIGO SANCHEZ: I'm out of time but I'll be back tomorrow.

  • And we'll hit object-oriented programming again a little bit.

  • COLTON OGDEN: OK, cool.

  • RODRIGO SANCHEZ: And switch over to Python.

  • COLTON OGDEN: Yeah.

  • RODRIGO SANCHEZ: You said that we haven't done much Python streaming.

  • COLTON OGDEN: Not a lot.

  • RODRIGO SANCHEZ: So that'll be fun.

  • All right.

  • It's been real.

  • COLTON OGDEN: It's been real, man.

  • Appreciate it.

  • Thank you very much.

  • OK.

  • So I do like the idea, yeah, right click.

  • Right click can set a flag.

  • We can take a look at that, actually.

  • We have another hour or so left.

  • Hopefully we can get all the features done.

  • I mean, we have several more steps to do.

  • This might end up being a two-part stream.

  • And if it is a two-part stream, we would definitely

  • do something like flags and multiple game states and all that sort of thing.

  • We'll see how much we can get finished after this.

  • And I'll commit everything to GitHub.

  • And this will probably, if it is a two-part stream,

  • it'll probably be next Monday.

  • But yeah.

  • Let's go ahead.

  • So the next step we needed to do--

  • oh yeah, we were going to draw the tile being depressed

  • or a bomb, depending on the situation.

  • So OK, so it's looking for grid tile.

  • We haven't added grid tile to our dependencies yet.

  • So let's go ahead and do that.

  • Requiresource/gridtile and it's not quite working yet.

  • So let me see why that is.

  • Oh, you know why it is?

  • It's because we're still doing this bit of code

  • here, which is not what we want to do.

  • We don't want this to happen.

  • What I want to do is I want to draw, rather, I

  • don't want to draw anything with that.

  • self.leftoffset plus-- and self.y offset plus yeah.

  • What'd we do?

  • Would we do it like that?

  • self dot-- OK.

  • We're going to do self.grid at yx, render, right?

  • Because we defined a render function in our grid tile class.

  • So these are all grid tiles now.

  • They aren't just numbers anymore.

  • It does take an x and a y, if I'm not mistaken,

  • right here, which is this x and y.

  • And we can just reuse the x and y that we have here.

  • So self.leftoffset, blah, blah, blah.

  • Wow, that looks great.

  • And, OK.

  • So we have a grid full of revealed tiles now.

  • Is hidden equal true?

  • Oh, you know why?

  • OK.

  • We didn't make is hidden actually a parameter of the function.

  • We just kind of assumed that it did.

  • And it's still not working.

  • So why is that the case?

  • Let me see.

  • Self.ishidden is equal to ishidden.

  • Oh, is equal to one.

  • So this was always getting true because the result of math.random2

  • is always going to be a truth value.

  • So it was always giving us true because the way the ternary statement works.

  • And same thing with this, we need to also set that.

  • So now it works.

  • Cool, awesome.

  • We see some bombs, right?

  • Looks great.

  • I like how that bomb ended up looking.

  • It looks pretty good, pretty good sprite work

  • for somebody who sucks terribly at making sprites.

  • Yeah, I like that a lot.

  • That's super cool.

  • [SPEAKING JAPANESE]

  • It's not difficult, is what [INAUDIBLE] just said.

  • It's not difficult. I appreciate the Japanese in the stream a lot,

  • as somebody who's learning Japanese.

  • I would prefer to see it in an actual kana I think.

  • But we have to set up--

  • I'd have to go to translate.google.com so I can actually type it.

  • So what he just said was--

  • I can just do it on stream, right?

  • Translate.google.com.

  • My computer is set on a different account.

  • It's set so I can type at the operating system level in Japanese.

  • [SPEAKING JAPANESE] It's not set to type in kanji.

  • We need to do that.

  • So [SPEAKING JAPANESE].

  • Let me see what he's typed and I'll remember.

  • There he did.

  • [SPEAKING JAPANESE]

  • So something like that, right?

  • Saying it's not that difficult, though.

  • [SPEAKING JAPANESE]

  • Anyway, try not to spend too much time on that.

  • Pretty basic sentence.

  • I'm not good at Japanese at all, but I read much better than I can actually

  • speak and understand.

  • But yeah.

  • Because the map is generated at the beginning randomly with correct math,

  • turn all tiles hidden and is presented to the player.

  • I mean, everything is going to be unrevealed at the very beginning.

  • We don't need anything specifically math-generated.

  • I mean, in the sense that we will be looping over all the tiles

  • to see how many neighbors they have that are bombs, we will need to do that.

  • And that's sort of like a pre-calculation.

  • But that's all we're doing.

  • Bombs are turned into flags when the game is won.

  • Oh, that's a cool feature.

  • [INAUDIBLE], are you Japanese?

  • [SPEAKING JAPANESE] Would that be how you say it?

  • [SPEAKING JAPANESE]

  • This was the game Windows offered by default. It should not be so easy.

  • I don't know.

  • It's a pretty easy.

  • I mean, all things considered it's fairly easy.

  • I would say it's not terribly easy.

  • [INAUDIBLE]'s got Greek.

  • I know what Greek looks like.

  • I can sort of read it.

  • [SPEAKING GREEK] I don't remember whether that's-- it's been a long time.

  • I used to know the Greek alphabet pretty well.

  • Not any words in Greek, but--

  • [SPEAKING GREEK]

  • I don't remember whether that's L or M, lambda or mu.

  • [SPEAKING GREEK]

  • In this case, it would be [SPEAKING JAPANESE]

  • because the equivalent in English, present progressive tense.

  • Correct.

  • [SPEAKING JAPANESE]

  • JP, do you speak Japanese?

  • Anyway, language divergence.

  • Oh, that's sick.

  • Fluent Japanese or?

  • That's super cool.

  • Curious how many people in here actually speak Japanese.

  • Everyone's going to start coming out and saying, oh, I speak Japanese, too.

  • Oh, I speak Japanese, too.

  • Nice.

  • That's cool, man.

  • I've only been studying hard for maybe a year, less than a year.

  • And on and off.

  • But I can read fairly well.

  • N3 says [INAUDIBLE].

  • Yeah, I'd guess I would probably be N4.

  • I'm much better at reading and writing than I am at speaking and listening.

  • Friend speaks.

  • He passed N1.

  • Learned it with himself without any textbooks.

  • Yeah, that's essentially what I'm doing.

  • I'm using a little bit of textbooks but mostly through actually playing games

  • in Japanese.

  • It's the main reason I wanted to learn.

  • 3DS with Japanese games.

  • If you guys want to practice kanji together, I'm totally down.

  • OK.

  • Sorry, I'm spending too much time on the Japanese tidbit.

  • It's a lot of fun but we should probably make progress on this.

  • So we have the grid here with the bombs.

  • Super cool, super awesome.

  • What we need to do now is make sure that all of the tiles

  • have the number of bombs as their neighbors and that that's correct.

  • So what we need to do is, we need to calculate--

  • once our grid is constructed, basically, we

  • need to call a function called calculate numbers, right?

  • And this will go in here.

  • And what this is going to do is iterate over the entire grid.

  • So I'm doing a 2D iteration.

  • And I need to basically look for every single tile.

  • I'm going to look to see all of the neighbors excluding

  • edges and corners and things like that.

  • [SPEAKING JAPANESE]

  • I'm a native Russian speaker and can speak a little French, as well.

  • Nice, nice.

  • Is asley here?

  • Actually, I haven't seen her in the chat.

  • So I'm kind of surprised, actually, now that I think about it.

  • And I got to remember that it's her birthday on Friday, supposedly.

  • So I need to verify that on Facebook.

  • But we should all remember that asley's birthday on Friday

  • and wish her a happy birthday.

  • Yeah.

  • So what we need to do is iterate over the entire grid

  • and then for every tile, we need to look at all of the neighbors.

  • So in this case, we say check top left, check top, check top right,

  • check left, check right, check bottom left, check bottom, check bottom right.

  • And there might be a more programmatic smooth way to do it.

  • But this is effectively what the logic is.

  • So we need to say, if x is greater than one and y is greater than one, then--

  • and another thing we need to do is say, local num bomb neighbors

  • is equal to zero.

  • We're just going to add one to this every single time that we find a bomb.

  • So what I can do is, I can say if self.tiles at x minus one,

  • y minus one dot, and it would be .isbomb,

  • then num bomb neighbors equals num bomb neighbors plus one, right?

  • So that's checking the top left.

  • So we're minusing on the x, minusing on the y.

  • So that's just that logic.

  • The top is a little bit simpler of a check.

  • It just basically says if y is greater than one,

  • then if self.tiles y minus 1x.isbomb then num bomb neighbors

  • equal num bomb neighbors plus one.

  • So this is the logic involved.

  • And this takes place for every single tile and all we're doing

  • is essentially bounds checking all of these checks of our neighbors.

  • So just making sure that we're not going to the left of the far left edge,

  • to the right of the far right edge, above the top,

  • below the bottom, that sort of thing.

  • So, same thing for the top right.

  • So if y is greater than one and x is less than what would this be?

  • Self.width, then if self.tiles as this is top right,

  • so this will be y minus one, x plus one.isbomb

  • then num bomb neighbors equals num bomb neighbors plus one, right?

  • Pretty simple.

  • We're just checking left, right, up, down in different permutations

  • and that will tell us whether a neighbor is a particular way, right?

  • So check the left.

  • If x is greater than one, I'll just copy this.

  • If self.tiles at y and then this is x minus one.

  • For the right, let's kind of do the same thing but reversed.

  • So if y is less than self.width, then we'll do y and this will be x plus one.

  • Bottom left is going to be if x is greater

  • than one and y is less than self.height, I'm going to do this, do this.

  • So not a terribly exciting bit of code here.

  • But kind of just, this is the core of our pre-calculation.

  • This is basically all we need to do.

  • There's no way to be at the limits, right?

  • E.g., self.tiles negative one or something.

  • Yeah, that's essentially why we're bounds checking,

  • to make sure that when we check to the right of x,

  • that we're less than our width.

  • Because if we are, then we can go up one index

  • and be at the right edge at the worst case, if not to the left of that

  • somewhere, between one and the width, right?

  • That is exactly why we are doing all of these if statements,

  • for the bounds checking.

  • That's the sole purpose of these if statements.

  • Otherwise we could just blindly do this x plus one

  • but that's not going to work when we're at the far right side of the screen

  • because there are no more indices to the right of that, right?

  • So this is why we're saying, if y is in this case, this needs to be different.

  • So x minus one, we're making sure that we're greater than one.

  • Because if we're at one and we do minus one,

  • we'll be at index zero, which is not a real index in Lua tables

  • because they're one indexed, right?

  • So that's effectively why we're doing this.

  • So self.tiles, and this is going to be also y plus one.

  • I have plus one, x minus one.

  • And this is why we're doing less than self.height, right?

  • So check the bottom.

  • So if y is less than self.height, then--

  • and then we can do this.

  • We're just checking x and y plus one.

  • And then bottom right, which is if y is less than self.height and x is

  • less than self.width.

  • And then we would do x plus one, y plus one.

  • Cool.

  • That's good.

  • And then now we'll have a number for this particular index in the grid,

  • right?

  • For that particular tile.

  • So I can say, we've done all of that.

  • Num bomb neighbors shouldn't be at that level.

  • It should actually be over here.

  • Store all bombs we see around tile.

  • Checking on neighbors.

  • This is kind of just to give a sense of what this particular code box is all

  • about.

  • So we're checking all the individual tiles and then at the end,

  • once we've checked all of the tiles, we can then say store number at that tile.

  • We can say self.grid at yx dot, and this would be num bomb neighbors

  • equals num bomb neighbors, right?

  • So now we have that.

  • So what we can then do is say, in our grid tile,

  • so now we're going to actually have that number, right?

  • So we can two string that.

  • Can say if it's a bomb, then we [INAUDIBLE] bomb.

  • Else, wait.

  • Do I have this reversed?

  • I have this reversed.

  • If self.isbomb, don't draw the bomb?

  • OK.

  • So I had that mixed up.

  • I mean, it looked OK but the logic is wrong.

  • So I'm essentially just going to do this.

  • Boom, boom, do a little trade, little switch.

  • Something like that.

  • Cool.

  • Now it's actually accurate.

  • If it's a bomb, draw the bomb, don't draw the depression.

  • I don't know what I was thinking.

  • But yeah, draw the bomb.

  • How does work right now?

  • OK, tiles at 68.

  • So I need to make sure that is OK.

  • 68 self.tiles.

  • Oh, it's not self.tiles, it's self.grid.

  • OK, whoops.

  • So we can fix that.

  • I'll just go over to self.tiles.

  • And make that self.grid.

  • Right, cool, looks great.

  • And then if it's a bomb--

  • so the depressed, this is where we're going

  • to actually draw the color, sorry, the number for that particular tile.

  • So I can say, local num bomb neighbors is equal to--

  • what would this be?

  • Grid tile.

  • OK, this is the grid tile.

  • This will be self num bomb neighbors.

  • Oh, wait.

  • What am I doing?

  • I'm sorry.

  • Brain fart.

  • So now that we have the reference to the number of bomb neighbors, what

  • we want to do is say, draw that tile.

  • We're going to map that to the particular tile

  • that we loaded earlier, right?

  • Which we stored in dependencies.

  • And they all started with a string numerical index here.

  • So it's a string but it's a number.

  • So what we can do is we can say, if self.numbombneighbors

  • is greater than zero, right?

  • If it's greater than zero, then we want to draw one of the number ones.

  • If it's zero, if it is zero, then I just want

  • to draw the depressed tile as it is just by itself.

  • So in here, I'm going to say love.graphics.drawgtextures

  • and then I'm going to say two string, self.numbombneighbors, right?

  • Which is good.

  • Gtextures and then there I can say xy.

  • And there we go.

  • So we have our numbers.

  • Now seeing them all together looks pretty wacky.

  • But in theory, this should actually be working.

  • So notice that there's this one here and it has this one tile next to it

  • that has a bomb, right?

  • And this two here is also kind of right here.

  • I'm guessing this two and this two, these two have to be bombs.

  • And there's no way we can really know right now.

  • What we should do is just always draw them.

  • So just check to see whether our algorithm is working.

  • Get rid of the hidden check here.

  • So I'm going to get rid of this right now for you.

  • Get rid of this.

  • We'll bring it back later but for now, I just want to draw every single tile.

  • OK.

  • Yeah.

  • Wait.

  • This five right here should be a six.

  • Because one, two, three, four, five, six.

  • I mean, these two are working, this three is working, this four is working,

  • this five is not working.

  • But this four is working.

  • This four is not working, OK.

  • Do I have something wrong with my top left?

  • Is my top left messed up?

  • These both are getting their top left incorrect, I think.

  • I don't know.

  • Well, this one is incorrect.

  • There's a one and a two.

  • This two is correct.

  • Four is correct.

  • This four is not correct.

  • Oh, wait.

  • Oh, you know why?

  • Because each bomb may also have a number.

  • So what we need to do is, we need to check

  • to see if this already has a bomb.

  • And if it does, don't even worry about numbers, right?

  • So yeah.

  • The bombs are--

  • I think that might be the problem.

  • I'm not 100% sure.

  • So I'm going to say if self.grid at yx.isbomb, then

  • we're not going to store a number here.

  • And I don't know if this matters.

  • Yeah, I guess this wouldn't matter actually.

  • But we can skip this logic at least.

  • Yeah.

  • I'm going to put all of this inside there.

  • It's at least an optimization.

  • So we'll do it for that purpose, I guess.

  • I'm a little confused as to why that's not working.

  • So whoops.

  • So now it's only going to work if it's not a bomb.

  • If not, self.gridisbomb.

  • Cool.

  • And I don't think that's going to work any better.

  • It's actually the exact same grid.

  • Is it?

  • No, it's not I don't think.

  • Yeah, there must be an issue with my logic because this four up here, right?

  • Oh, wait.

  • No, that's OK.

  • That's fine.

  • So, this five right here, right?

  • This five right here.

  • One, two, three, four, five, six.

  • So there must be a subtle mistake that I'm making,

  • maybe in my conditional logic here.

  • So let me just make sure that that's correct.

  • So if x is greater than one, y is greater than one, OK.

  • X minus one, y minus one, right?

  • Top left.

  • The top, just the y greater than one, right?

  • Make sure that, OK.

  • Y minus 1x, yeah, that's correct.

  • OK, top right.

  • Y is greater than one.

  • X is less than self.width.

  • So y minus one, x plus one.

  • OK, cool.

  • X is greater than one, yep.

  • X minus one.

  • If x is less than self.width, OK, y and x plus one, yep, correct.

  • Bottom left.

  • If x is greater than one and y is, OK.

  • Yep.

  • Yep.

  • Y is less than self.height.

  • OK.

  • OK.

  • If y is less than height and x is less than self.width.

  • OK.

  • Oh.

  • It's this.

  • Yx.

  • Let's try that.

  • one, two, three, four, five, six.

  • One, two, three, four, five.

  • One, two, three, four, five, six.

  • One, two, three, four, five.

  • OK.

  • I think it was just that.

  • It was a silly--

  • I had put x here and y here.

  • Should be reversed.

  • Y goes before x in this case with 2D indexing.

  • Cool.

  • Awesome.

  • Colton, consider all corners and edges first.

  • I don't think that's necessary.

  • Unless you're talking about this, the if statement in my conditional logic.

  • But I don't think it matters what order you check stuff in

  • as long as you check all of them.

  • Live debugging, NACLeric.

  • Yeah.

  • No, that was good.

  • That was a good illustration of what debugging looks like sometimes.

  • But no, this is great, right?

  • Now, let's go ahead and actually render this appropriately.

  • So we can say if self.ishidden then else so love.graphics.drawgtexturestile tile

  • at xy, right?

  • Cool.

  • So the hidden attribute is just random at this point.

  • So it's not really serving much of a purpose.

  • Now, you know what?

  • Another thing we needed to do, I needed to test the emptiness, right?

  • Did we have any empty?

  • I don't even think we had any empty tiles on the last one

  • just because there were so many bombs.

  • So I want to generate a lot fewer number of bombs than we had already.

  • So what I'm going to do, I'm going to set this to 10.

  • I'm going to make it a 1 in 10 chance, effectively.

  • And then I'm going to set hidden to be false always right now.

  • Nice.

  • OK, cool.

  • So that's a little bit more appropriate, I think.

  • And now they're all being shown on the screen.

  • This is cool.

  • I like this.

  • This is fun.

  • This is exciting.

  • Yeah, this is super cool.

  • So now let's set it to be true by default, right?

  • So now they're all hidden.

  • Now, this is where we need to start actually

  • implementing, being able to click on the individual boxes, right?

  • So yeah.

  • We can do this.

  • So what I'm going to do is, this is going to be my update function.

  • So in my update function, first of all, I'm going to add this to main.

  • I'm going to say, in main.

  • So right now I'm drawing the grid here.

  • I also want to update it.

  • So game grid update delta time.

  • Again, delta time is the number of seconds

  • that have passed since the last frame.

  • So it lets you keep things normalized across frame rates, which is cool.

  • I'm going to go into here.

  • I'm going to say, local xy is equal to love.mouse.getposition.

  • And now what I need to do is I basically need to iterate over every single grid

  • tile and say, is the cursor within the bounds of that tile, right?

  • And if it is, I need to draw a rectangle on top of that tile.

  • And the easiest way is to take the offsets

  • and to consideration and then just kind of do it based on--

  • a couple of ways we could do it.

  • So first of all, you need to convert.

  • When you're using the push library, in order

  • to actually get the actual pixels, the proper pixels, you need to do push.

  • And what is this?

  • To world?

  • And then now we can do love.graphics.print x two string x.

  • Y two string y.

  • And I'm going to put that zero and then virtual height minus 16.

  • To world.

  • Pushed to world.

  • Oh, is it to game?

  • Might be to game.

  • There we go.

  • But it's not rendering because I think it's a little bit too low.

  • Let's try it minus 48, let's try that out.

  • No, that's not working.

  • OK, that's fine.

  • Love.graphics.print x to string x, y to string y

  • at zero virtual height minus 48.

  • Oh, that's my bad.

  • That needs to be within the push start, push finish.

  • So that's fine.

  • We can do this.

  • This is a little bit weird.

  • But now you can see it works.

  • We have a virtual resolution, so 384 by 216, again, is our resolution.

  • So we can see that it is rendering.

  • And now the thing is, it's going to need to be I think cast to an integer.

  • I mean, we could do it with floats, too.

  • That's fine.

  • Doesn't really matter.

  • But that's pretty cool.

  • It's going to be important.

  • Let me make sure that I'm meeting everything.

  • If y is less than y height and x, it will enter twice, right?

  • In the state we should be up.

  • No.

  • It won't do the logic twice, if that's what you mean.

  • Oh, levels.

  • I like Bhavik's idea.

  • Level beginner 10 bombs, intermediate 40 bombs, expert 99 bombs.

  • That's cool.

  • Use the check neighbor function, same logic.

  • Abstract check neighbor and use it for a bomb or empty.

  • Oh, I see what you mean.

  • Yeah, yeah, yeah.

  • We could've done that, too.

  • Yeah, that would've been better design.

  • You'd check neighbor, which basically takes an x and y and then does this.

  • The only thing is that what you check and what conditions you check

  • are inverted.

  • And so you would need to pass in negative numbers to get it to work.

  • It's a little bit strange.

  • You could get it to work that way, though, for sure.

  • For checking a [INAUDIBLE],, blah, blah, blah, blah.

  • As an exercise, try and implement it on your own.

  • And actually, to complement that, I'm going to add this

  • to GitHub before I forget to do so.

  • I forgot to add the typing game code, the last bit we did to the stream.

  • And Brenda kindly informed me afterwards.

  • So hopefully I don't neglect doing so again today at the end of the stream.

  • We're going to call this Minesweeper stream, Twitch stream on Minesweeper.

  • Make it public, create repo.

  • Cool.

  • Copy that.

  • Go over here.

  • Exit Python, clear.

  • Where am I?

  • Add it there.

  • Need to go into Minesweeper.

  • Get init, get adstock, get ad first commit.

  • Get remote at origin.

  • [INAUDIBLE]

  • Get remote at origin.

  • That get push upstream origin master.

  • Cool.

  • So that's up and running now.

  • So if you refresh that, boom.

  • Now you have Minesweeper.

  • You can follow along if you want to or add your own logic to it.

  • Currently, the state of it is as such.

  • The next step we want to do is be able to determine whether we're

  • highlighting one of these tiles.

  • So let's do that.

  • So what I want to do is, I kind of want a hover effect.

  • So when I'm hovering over any of these tiles,

  • I kind of want it to show a semi-transparent rectangle.

  • And so I think that's what I'm going to do.

  • I'm going to say self.highlightedgrid equals--

  • rather, highlighted tile, I'm going to set that to--

  • actually, no.

  • I'm going to say self.ishighlighting to false because I

  • don't want it to be highlighting if I'm not, for example, over the grid.

  • Like if I'm over here, I don't want any of the tiles

  • to be highlighted just as a user experience.

  • I don't want them to be like, why is there a leftover

  • highlighted rectangle on the grid right here, you know?

  • It doesn't really make sense.

  • So instead, I'm going to--

  • what I want to do is, I want to--

  • I have to get a reference to each grid tile

  • and check to see within that grid tile if my cursor falls within it,

  • if it's within the bounds of that rectangle, right?

  • So I want to basically do a four y is equal to--

  • well, I just don't want to do four y is equal to one.

  • I want to do x pause, y pause.

  • So for y is equal to one self.height, and again, I have to also do this.

  • I have to do x pause, y pause.

  • And then x pause, y pause.

  • That.

  • I'm going to do a double loop over my grid.

  • And then I'm going to say four y is equal to one self.height do

  • four x is equal to one self.width do.

  • And then this is going to let me go over every single tile in the grid, right?

  • And so what I'm going to do then is say, local tile is equal to self.grid at yx.

  • So that's a reference to that particular tile in the grid.

  • I'm going to do if x pause is greater than tile--

  • they maintain an xy as a reference to their--

  • oh, they don't even maintain an xy.

  • OK.

  • So they maintain an xy relative to their iteration.

  • OK, so it would be greater than--

  • so this would be the offset of the grid, then, the first part.

  • So self.leftoffset.

  • Self.leftoffset plus x minus one times tile size, right?

  • And x pause is less than self.leftoffset plus x minus one times

  • tile size plus tile size.

  • And, we'll do a then first.

  • If y pause is-- just, I'd probably ideally combine this into one Boolean

  • expression, but because it's such a long line,

  • I'm going to actually split it into two checks, one

  • nested within the other one.

  • And this is also form of short circuit logic but bulky.

  • Basically lets me avoid checking y pause if x

  • pauses isn't even worth considering.

  • But we're doing kind of the same thing here.

  • I'm basically saying greater than--

  • and this is vertical offset but it's top offset.

  • Top offset plus y minus one times tile size.

  • And y pause is less than top offset plus y minus one times tile size

  • plus tile size.

  • So in this case, the x of the mouse is within the rectangular border

  • of whatever tile we're looking at, right?

  • So then I can say, self.ishighlighting is equal to true.

  • And then another thing I have to do to make sure

  • that we're not highlighting a rectangle indefinitely after we've

  • gotten outside the bounds of the grid, I should

  • say local highlighting something is equal to false, right?

  • And then at the end of all of this, rather this should be--

  • I basically want to check to see whether at the end of this whole block of code,

  • I want to say, did we find something to highlight?

  • And if we didn't, then self.highlighting should be toggled back off to false.

  • So it's basically going to match this variable

  • up here that we're keeping track of.

  • We're assuming false but anytime that we get a match on a tile,

  • then we're going to set that to true, right?

  • And then what I can do is I can say, if highlighting something,

  • and this isn't a self variable, then self.ishighlighting

  • is going to be equal to false, right?

  • So basically using this, we're keeping track of over this whole iteration,

  • did we find something to highlight?

  • If we didn't, if we're outside the bounds of the grid,

  • let's make sure that we turn off highlighting.

  • So self.ishighlighting should be set to false in that case, right?

  • Which is right here.

  • Highlighting something is equal to true, something

  • is highlighting is equal to true.

  • And then if we are highlighting something,

  • what we want to do is we want to say, self.highlightingtile and we'll just

  • say x is equal to x and y is equal to y.

  • And what this will allow us to do, now down here in the rendering thing,

  • we can say if self.highlightingtile, then,

  • so if we're highlighting something, if we've flagged something

  • as being highlighted, wait, rather is highlighting, sorry.

  • If self.ishighlighting, then what we're going to do is we're going to say

  • love.graphics--

  • no, we're not going to do it in here.

  • That's silly.

  • We do outside the loop.

  • We're going to say if self.ishighlighting, then we're

  • going to say love.graphics.rectangle, and this is going

  • to be a white, semi-transparent thing.

  • So we need to do love.graphics.setcolor21110.1 maybe.

  • So what this is doing is, you have to set that global variable for the color.

  • It's sort of like the marker.

  • And we need to do that before we actually draw the rectangle.

  • Because by default, it's just going to be white.

  • So it's just going to draw a white rectangle.

  • We don't want that.

  • We do want to also revert the color after that

  • to love.graphics.setcolor1111.

  • So love.graphics.rectangle and this is going to be at self.highlightingtile.x.

  • Self.highlightingtile.y, tile size, tile size.

  • And this should also be a fill rect, not a line rect.

  • Now, if I'm not mistaken this should work.

  • Maybe not.

  • OK.

  • Let me make sure that this is working.

  • OK.

  • Self.ishighlighting is equal to true.

  • X is equal to x, y is equal to y.

  • Oh, and then I think in addition to that,

  • we also have to do self.highlightingtile.x plus--

  • or rather we should do--

  • we have to also take the offsets into consideration, which I forgot to do.

  • So this would be top--

  • rather, this would be self dot the width offset, whatever that is.

  • And I apologize if I'm going a little bit fast.

  • We're very close to the end.

  • And we probably won't have time to get to the actual solving the game,

  • unfortunately, or the flags or the game states.

  • But I want to get us at least into highlighting the rectangles before we

  • end the stream.

  • So I'm going a little bit fast.

  • I apologize for that.

  • But we will get this part finished for sure.

  • .leftoffset plus that.

  • And then top offset plus that.

  • OK.

  • That should work.

  • Maybe not.

  • Is it maybe too transparent?

  • Maybe do like 0.3.

  • OK.

  • So something is awry.

  • I have failed in some fashion.

  • So OK, we're going over the grid.

  • For y it's a self.height, self.width.

  • Getting the tile at grid yx, which is good.

  • OK, that's what we want.

  • We are calling update, right?

  • Yep, game grid update delta time.

  • Cool.

  • We have the x position here, so we know about that.

  • So we're basically saying if the x position is greater than

  • self.leftoffset plus--

  • yeah, and there's the x and the y.

  • X minus one times tile size.

  • And x pos is less--

  • oh, wait.

  • Yeah, OK.

  • No, that's fine.

  • OK.

  • X pos is less than--

  • I think it should be greater than or equal to, actually.

  • Less than or equal to, maybe.

  • Maybe not.

  • Because then you'll have edges that are ambiguous.

  • But I guess that's OK.

  • Greater than or equal to, greater than or equal to, less than or equal to.

  • Blah, blah, blah.

  • So I screwed something up here.

  • We got to figure out what it is.

  • Self.ishighlighting.

  • Right.

  • OK.

  • What we should do, we should debug a little bit.

  • I'm going to do a couple of things.

  • So the first thing I'm going to do is, I'm going to go to dependencies.

  • I'm going to create a smaller version of--

  • can I do that with the start font?

  • I guess I probably can, right?

  • Start small is going to be love.graphics.newfont.

  • Fontsstart.ttf at size eight.

  • Actually, I think it said eight-pixel font already.

  • So that's good.

  • So back into our game grid, I can go over here.

  • I'm going to do love.graphics.setfont to g fonts, start small.

  • And then I'm going to set this back to just regular start.

  • So let's try that.

  • Yep, cool.

  • So that's good debug text.

  • Looks a little bit better.

  • Now after that, additionally, I'm going to do love.graphics.print.

  • And then we're going to say highlighting tile to string self.ishighlighting.

  • I'm going to see whether that works, right?

  • Does that actually work?

  • Oh, whoops.

  • That needs to be at this x and y.

  • Whoops, no.

  • Don't delete it.

  • OK.

  • Virtual height minus 36.

  • OK, OK.

  • So it's not even working.

  • So highlighting tile is equal to false.

  • OK.

  • And then just for good measure, I'm going

  • to say highlighting x to string self.highlightingtile.x.

  • And then at y to string self.highlightingtile.y.

  • Break this down into a new line.

  • Wait, where did I put that?

  • Self.highlightingtile, self.highlightingtile, oh.

  • I guess self.highlightingtile probably never got instantiated.

  • That's fine.

  • Self.highlightingtile equals x equals zero.

  • Y is equal to zero.

  • And I apologize, I'm not looking at the chart super in depth right now just

  • because I want to get this finished while we're a little low on time.

  • But I will read all of the comments after we finish this.

  • Promise.

  • Minus 24.

  • Cool.

  • OK.

  • Interesting.

  • So we are getting it but highlighting tile is not being set to true.

  • OK.

  • That's probably the issue.

  • OK.

  • Self.highlightingtile.

  • Self.ishighlighting is equal to true.

  • Wait.

  • If not highlighting something, there we go.

  • Now, the rendering isn't working but it's close, right?

  • You can see the rectangle there.

  • And I can actually probably make it a little bit brighter.

  • So let's go ahead and do that.

  • 0.4.

  • So it's a little bit brighter.

  • There we go.

  • That's cool.

  • Now, the rendering code isn't working for the rectangle.

  • So let's figure that out, too.

  • So something left offset plus self dot-- oh, right.

  • Yep.

  • Minus one times the tile size.

  • One times the tile size.

  • Cool.

  • Now as you can see, we have a visual indication of the tile

  • that we're hovering over.

  • So I apologize.

  • That was a little bit of a long amount of time

  • for a somewhat anticlimactic feature.

  • But it's pretty important.

  • It's a nice feedback to at least know, when I click on this tile right

  • here, for example, or this tile, that's going to be the tile that opens up.

  • And makes the game feel a little bit more immersive, right?

  • It's an important detail.

  • That stream altogether took a longer amount of time than--

  • I mean, I anticipated it might take up to two streams

  • to get this whole game up and running.

  • But I don't know.

  • I don't know.

  • We can definitely get this done by next stream.

  • I would have liked to see it get more finished today.

  • But we have a pretty good foundation.

  • We saw that the bombs are generating.

  • We saw that the numbers are generating.

  • It's great.

  • The next stream will be even better because we'll

  • talk about recursive algorithms.

  • We'll actually get everything solved.

  • Let me make sure that I'm committing everything first.

  • So highlighted tiles, do that.

  • Hit push.

  • We'll say next Monday will be the continuation of this.

  • I apologize we didn't get the whole game finished.

  • It's kind of a larger game.

  • And I always underestimate how much time it takes to interact with the chat.

  • But that's my favorite part of doing this.

  • So I will not compromise on that detail.

  • Let me just go back up to the chat, make sure that I read everything.

  • Ba, ba, ba, ba.

  • About the number of bomb checking, what about a double four looping

  • continuing when you're out of bounds?

  • I'm not entirely sure I understand that implementation,

  • if you want to maybe elaborate on it.

  • Oh.

  • Check out of bounds pause that takes an x and a y?

  • Sure.

  • Yeah, it's something like that.

  • A recursive way to do it?

  • Yeah, you probably don't need recursion for checking

  • just your immediate neighbors, though.

  • But yeah, recursion is how we're going to be

  • doing the actual checking for when you click a button, the actual checking

  • of that, that will be recursive.

  • But the actual instantiation of the game grid,

  • the creation of the bombs, the creation of the indices with the numbers, that

  • doesn't have to be recursive.

  • I guess it could be recursive, but there's

  • no real need for it to be recursive.

  • Oh, shoot.

  • Asley [INAUDIBLE] caught the flu.

  • I'm so sorry to hear that.

  • I know that that can be very brutal.

  • Hopefully you're staying OK.

  • It's an unfortunate timing this week if it is indeed your birthday.

  • So sorry to hear that.

  • Hope you get well soon.

  • Hope you're able to maybe at least watch this later, maybe even on Monday so

  • that you can watch both of them together,

  • the first part and the second part.

  • But thanks for tuning in to at least let us know what's going on.

  • I was very surprised to hear that you weren't joining the stream.

  • So it makes total sense now.

  • Yeah, get well soon.

  • Friday party.

  • Yeah, that's true.

  • Staypeaceful89, hey, everyone.

  • It's been a while.

  • Yeah, it has been awhile.

  • Thanks for joining.

  • It's been a long time.

  • Hope you're able to catch most of this.

  • If not, at least you'll be able to see it on YouTube after the fact.

  • Forsunlight, thank you so much as always.

  • No problem, it's my pleasure.

  • Anytime.

  • We'll do it again tomorrow and the next day.

  • Rodrigo will be joining us tomorrow for a game of 15.

  • And then David will be joining us on Friday

  • for render 50, which is a Python-based source code to PDF application, which

  • is pretty cool.

  • We use a lot for a printing source code and that sort of thing,

  • or rather, I should say for lecture and that sort of thing.

  • Push it, come on, says Bhavik.

  • Well, everything's pushed so you should now be able to see all the changes.

  • CS50 or game development?

  • CS50, didn't know we had the classes.

  • Yeah, it's hard to compete with CS50 proper.

  • But we do have a game development class that I taught,

  • which is cs50.edx.org/games.

  • You go to there, you can go to cs50.edx.org/web and you go

  • to cs50.edx.org/mobile.

  • So games I taught, Brian taught web, and then Jordan Hayashi taught the mobile

  • react native course.

  • All pretty cool courses.

  • Brian's and Jordan's are awesome.

  • Mine is OK.

  • I think I could have done a better job now than I did at the time.

  • But that was my first class, so I think all things considered,

  • I'm pretty happy with how the curriculum turned out.

  • But yeah.

  • You can always do better.

  • JPguy says, thank you for the stream.

  • My pleasure.

  • Bhavik Knight says, great stream.

  • Thanks, Bhavik.

  • I appreciate it.

  • Bellacurious, thank you very much.

  • Thank you, coltonicedream.

  • Much appreciated.

  • Empty tiles unpressed would be recursive.

  • Yes.

  • Empty tiles unpressed would be recursive.

  • That is exactly how we're going to be doing the revealing of tiles

  • in the next stream.

  • It will be a recursive algorithm where the base case is hitting a number tile

  • or a bomb, in which case, the bomb would not be revealed

  • but the number tile would be revealed.

  • NACLEric says, thanks for the stream.

  • These Lua apps inspire me to make an Ascii game.

  • Awesome.

  • Yeah, definitely share it with us if you do.

  • And mkloppenburg says, thanks once again.

  • My pleasure.

  • Appreciate it, Martin.

  • Thanks for joining us.

  • Will Nick start AI on edX?

  • That's a good question.

  • No plans at the moment.

  • But we can maybe talk about that with him.

  • Pudavedatel says, thanks, Colton, very interesting.

  • Thanks for joining and thanks for the follow.

  • Joe, don't be so hard on yourself.

  • You know, I don't see it as being hard on myself.

  • I see it as thinking I could do something a little bit better than I

  • did last time, right?

  • Always sort of being self-reflective and analytical and making better stuff,

  • I see no harm in doing that.

  • Now, saying oh, I'm terrible.

  • I suck.

  • I'm so bad, I think that's negative and that's toxic.

  • You don't want to do that.

  • And that doesn't help anybody.

  • You want to be productive with how you think

  • and how you approach solving your problems.

  • So I think those two are two separate ways of thinking and being

  • hard on yourself, you're right.

  • You don't want to do that.

  • That's bad.

  • But saying, oh, you could've been better,

  • I don't think there's anything wrong with that.

  • I think that's fine.

  • I'm not sure what language that is.

  • Mkloppenburg is speaking to JPguy.

  • Is it Dutch?

  • Since I think JPguy's from the Netherlands, right?

  • That's a good philosophy.

  • I'm only 15 and I want to become a programmer and you inspire me.

  • Oh, awesome.

  • Well, thanks for tuning in.

  • I'm glad that you enjoy the material and the content.

  • And I hope to see more of your stuff.

  • And again, I'm going to pitch it again, so bit.ly/ what was the link again?

  • I don't remember exactly offhand.

  • It was bit.ly-- that's what it was.

  • CS50twitchcodereview.

  • So go to there if you have some source code,

  • if you're a beginner or an intermediate programmer.

  • This is much more for what that is catered towards.

  • But submit your source code to us via a GitHub repo or a gist

  • and we will potentially take a look at it on the stream

  • and tell you what things we like about your style and your design

  • and what things we think might be better.

  • Right, and there's always usually room for improvement.

  • And there's certainly a lot of things that, as a beginner or intermediate,

  • it's good to reinforce good habits as much as it

  • is to point towards bad habits, right?

  • So yeah.

  • That was Minesweeper part 1.

  • So part 2, we'll do the actual recursive finding of bombs,

  • we'll do losing the game, we'll do point totals.

  • All of that stuff will be in the next Minesweeper stream next Monday.

  • So again, tomorrow's Rodrigo, Game of 15 in Python.

  • And then on, Friday David with Render 50,

  • which is another Python app for rendering PDFs.

  • And then Monday, I'll do my Minesweeper part 2.

  • And then I forget, we have a couple of other streams next week.

  • But these will all be posted on the Facebook group, CS50 Facebook group,

  • which is facebook.com/cs50.

  • Definitely follow that page so you can keep up to date with all of our events.

  • Join the CS50 Facebook group, so it's a separate page.

  • The CS50 Facebook group, I always post all of our events.

  • And follow us on Twitter, follow us on YouTube, follow us on Reddit.

  • All that fun stuff, do all of that.

  • It's all good stuff.

  • Lancemagus says, my sauce code is really bad.

  • No way I'm showing.

  • Yeah, don't feel scared to show us.

  • We would be happy to look at it and give you some feedback.

  • And I think especially if you're a beginner to intermediate,

  • I think getting feedback on how things could be better

  • is super vital to help you break habits that might

  • be really hard to unbreak later on.

  • So feel very encouraged and don't be discouraged and don't

  • be afraid of the criticism, because that is ultimately

  • how you can become a better programmer.

  • Spaghetti goes best with sauce.

  • No worries.

  • Cool.

  • Thank you, [INAUDIBLE].

  • Awesome, thank you.

  • Or I should say, you're welcome.

  • Cool.

  • All right, everybody.

  • See you tomorrow with Rodrigo.

  • Until then, enjoy the rest of your evening

  • and take a stab at the source code that we came up with today.

  • See if you can maybe implement your own features,

  • do your own version of the recursive algorithm before we do it next Monday

  • ourselves.

  • That's how you learn, ultimately, much better

  • than you would learn through copying my source code.

  • And solidfury says, peace.

  • Good to see you, solidfury.

  • All right.

  • Everybody, enjoy the rest of your day.

  • Until then, this was 650 on Twitch doing Minesweeper.

  • And I'll see you next time.

  • Bye-bye.

COLTON OGDEN: Good morning, good afternoon good evening,

字幕與單字

單字即點即查 點擊單字可以查詢單字解釋

B1 中級

MINESWEEPER FROM SCRATCH (Part 1) - CS50 on Twitch, EP.27 (MINESWEEPER FROM SCRATCH (PART 1) - CS50 on Twitch, EP. 27)

  • 8 0
    林宜悉 發佈於 2021 年 01 月 14 日
影片單字