Placeholder Image

Subtitles section Play video

  • COLTON OGDEN: Good morning.

  • Good afternoon.

  • Good evening, depending on where you are in the world.

  • My name is Colton Ogden, and this is CS50 on Twitch,

  • where we code stuff from scratch, or where I typically

  • code things from scratch.

  • If you're a first time viewer, that's what we'll be doing today.

  • We're going to be looking at the typing game.

  • So this is kind of an archetype for games.

  • It's not a very common archetype, certainly.

  • But it's a game that you may have seen implemented in such games

  • as Typing of the Dead, where you have a word in front of you,

  • and your goal is to type this word without mistakes.

  • And when you do, you do something like shoot a zombie,

  • or there are certain top down versions where you're a mage

  • and you cast spells.

  • Or you can just do it for sport and see how many points

  • you can accrue over a span of time.

  • And that's what we'll be doing today.

  • So shout outs to everybody that's in the chat already.

  • So we have a bunch of folks.

  • I'm going to go scroll up to the very top.

  • So Whipstreak was here actually from the very beginning.

  • So he says, I'm here.

  • Bhavik Knight says, hey all--

  • Bhavik Knight, a regular.

  • Whipstreak also very much a regular, but I

  • think Bhavik Knight hasn't missed a single stream, if I'm not mistaken.

  • The Korean Bot says, hello there.

  • The Quran Bot, sorry.

  • I mispronounced that.

  • TazNoor, hi everybody.

  • Hello, TazNoor.

  • Sashikant32, another regular, says hello.

  • Buddha Nag says, this is old student, with a smiley face.

  • And then later on, I believe mentioned here she was doing a final project

  • and looking for suggestions.

  • Bella Kirs, hello to you.

  • Let me just make sure I'm not missing anybody.

  • Asley, Nuwanda3333.

  • So shout outs to Asley.

  • She's responsible for this particular stream and the stream preceding it.

  • So she recommended that we do a typing game based stream.

  • And so that's what we're doing today.

  • So shout outs to her.

  • Everybody can thank Asley for her recommendation.

  • She's also recommended many other streams that we have done so far.

  • And asking Buddha Nag, who asked, you guys can suggest me a final year

  • project, please.

  • And then Asley was saying, no one can actually give you

  • an answer on your final project.

  • It needs to be something you're passionate about.

  • And I agree with that.

  • It's definitely something when you're doing

  • a final project or some sort of large project,

  • it helps to do something that is pertinent to your interests,

  • but also intersects with your area of expertise.

  • To Bhavik Knight's point, asking for, what is your expertise area, Buddha?

  • And then Bella says, you can watch the CS50 Fair

  • for inspiration, which is true.

  • Yes, we did showcase many awesome projects

  • for the CS50 Fair, which is on YouTube.

  • Kirayue says hi, CS50.

  • Hello, Kirayue.

  • Hello Shamxr.

  • I suggest having a brainstorm session, be it here or with your friends.

  • Yes, very true.

  • Brainstorm sessions are amazing.

  • I was wondering how to make an application about finding

  • teachers near us, says Buddha.

  • OK, that's an interesting use case, certainly.

  • Whipstreak says, where's my Colton at?

  • JP Guy is waving, says hello.

  • Hello, JP Guy.

  • Good to see you as well.

  • Nice rhythm, Colton.

  • Thank you.

  • I was thinking to make an app that a student will hire a teacher

  • and make an appointment near him or her.

  • Yeah, that'd be pretty cool.

  • An app like that would therefore require you

  • to have some sort of database of teachers,

  • and then maybe a separate log in system for teachers and students

  • so that they could both register and be sort of paired up with one another.

  • But that'd be very interesting.

  • I saw a project very similar to that at the Yale Fair, actually.

  • There was a student that had something similar to that.

  • So good idea.

  • [INAUDIBLE] says, hi, Colton.

  • Glad to see you.

  • Yay.

  • Hey, Colton.

  • Hey, Colton.

  • Everybody is very awesome.

  • Not for engineering.

  • It's for computer applications final year.

  • Oh, I see.

  • OK.

  • Kirayue, Colton, good to see you.

  • Sashikant, Asley.

  • I missed one CDCI.

  • Oh, right.

  • OK.

  • So just one out of 24 or 25 streams, Bhavik has missed only one,

  • the continuous integration with Travis.

  • That's OK.

  • At least you can watch the VOD thereafter.

  • But still, that's like higher than a 90% attendance rate.

  • So awesome.

  • Well, thanks everybody for joining today.

  • So let's sort of get into what we're talking about.

  • I'm going to flip over to my screen.

  • In case you're unfamiliar with what I'm going to be using to make today's game,

  • it's a framework called LOVE and a language called Lua.

  • So the framework is here, love2d.org.

  • It's an awesome 2D graphics programming framework.

  • Definitely grab it if you're not familiar with it.

  • There are other streams where I've talked about how to set it up

  • and the basics of it.

  • And today we're going to be kind of taking a lot of the principles we

  • talked about on Monday.

  • So on Monday of this week--

  • and you can watch the video on YouTube if you're watching it after the fact--

  • but we did Hangman, which is mostly textual, looking for text input

  • from the user, drawing simple shapes, and then trying to fill out a word,

  • and therefore looking at the letters of the word

  • to make sure that they match up with letters that we've already typed,

  • and so on and so forth.

  • Today we're taking some of those ideas and kind

  • of making a different game that has more of a time-based mechanic,

  • insofar as you have 60 seconds to type a word

  • or a series of words that are presented to the user.

  • And for every word you complete, you get a point.

  • And then at the end of the 60 seconds, you're

  • basically graded on how many words you successfully complete.

  • So if you did 20, 25, however many words--

  • it's not quite the same as maybe a typing test on, for example,

  • typing test or whatnot, where you can see a body of text

  • and then write it out, and then be graded

  • on how fast your words per minute would be sort of traditionally typing.

  • This is more like reactively typing, and only

  • being able to see one word at a time.

  • So it's a little bit different, a little more gamey.

  • This principle, this idea, has been used in commercial games, some pretty cool

  • commercial games, like Typing of the Dead,

  • which was a game that they recently added a new version for Windows, I

  • believe, Typing of the Dead Overkill.

  • That's what the more recent incarnation is of the game.

  • But if we look here-- and I'll try to make it nice and large--

  • this is what Typing of the Dead looked like, the original version that

  • came out circa 2000.

  • And this game basically has--

  • it's kind of like House of the Dead, which

  • is a shooter game, a arcade game where you had a light gun,

  • and you would shoot zombies as they come into the view of the screen.

  • And it was kind of tangible in that sense, in that you actually

  • had the physical gun and you're shooting at the zombies.

  • But this game doesn't require you to shoot at any zombies.

  • This game is kind of like, there are zombies coming at you,

  • and you are figuratively shooting at them by just typing words reactively

  • as they come onto the screen.

  • And so for every word that comes on that you successfully type-- in this case,

  • ping-pong and bang-bang--

  • the game will shoot the zombie for you, and treat it

  • as if you had successfully shot it.

  • And if you miss a letter, essentially the zombie will get closer to you

  • and attack you, and eventually you will lose.

  • Now, of course, to make a game like this, there's a lot of layers.

  • A full 3D game or even a 2D game that has these sort of mechanics,

  • we don't have time to do that in a single stream.

  • Instead, what we're going to do is just illustrate

  • the simple mechanic of a bit of text comes up in a screen,

  • a word comes up on the screen, and you have to type it out.

  • And it will show the full word and your progress.

  • And if you type it successfully in the right order,

  • the letters will fully appear.

  • Eventually you'll get the word.

  • You'll get a point.

  • If you miss a letter, then you'll go back to the beginning of the word,

  • and you'll act as if you failed that.

  • And that's effectively how you not lose, but do more poorly,

  • is that you just don't get as many words typed onto the screen, right?

  • So that's today's game.

  • That's the gist of--

  • at least the mechanic that we're going to illustrate today

  • in a couple of hours, a few hours.

  • The first thing I'm going to do, as always,

  • is I'm going to go to my Streams folder, where I have all of the repos

  • that I've done so far.

  • And I'm just going to create a folder called Typing Game.

  • I'm going to click and drag this to my text editor of choice,

  • which is Visual Studio Code.

  • If anybody isn't familiar, it's an awesome text editor.

  • It's free.

  • Definitely grab it.

  • And it's an empty folder.

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

  • The language that we're going to be using today is Lua.

  • If you're unfamiliar with Lua and LOVE, definitely

  • check out, for example, the Hangman stream, which we did on Monday.

  • I feel like we did a pretty good job of covering

  • a lot of the basics of LOVE 2D.

  • But certainly, if you go back farther onto some of the Twitch VODs

  • on YouTube or on Twitch--

  • more so on YouTube, because the Twitch VODs have an expiration day--

  • if you go back to some of those older videos,

  • you'll see that we cover LOVE 2D the framework in a little

  • more granular detail, and cover things like a game loop

  • and like the love.load, love.update, love.draw, in a bit more

  • detail and a little bit slower.

  • But today we're going to kind of go a little faster.

  • But we'll still cover more or less the basics.

  • And I'll give you the rundown of what's going on.

  • Another important thing that I do want to do--

  • and this is sort of what I try to do most of the time when we do streams--

  • is I'm going to go to GitHub.

  • And it's a little bit slow.

  • For some reason, GitHub always takes a minute to load up.

  • GitHub is an awesome website, if you're unfamiliar,

  • which allows you to store source code and allows

  • you to store the history of source code and collaborate with other people

  • and do a bunch of other awesome things, automate deployments

  • of your applications.

  • I'm going to create a repo called Typing Game Stream.

  • Typing game implemented on Twitch.

  • I'm going to make this public.

  • This is so that I can push all my code at the end of today.

  • And if you want, you can take the source code, mess with it,

  • do your own stuff to it, and get some more value out

  • of it if you decide not to code along with me, which might be--

  • maybe if you're busy at this exact moment in time,

  • you don't want to do that.

  • That's fine.

  • Or if you want to get the code right away

  • and implement it, yeah, absolutely.

  • So I'm firing up my terminal.

  • I'm going to go into the folder that I just created, Typing Game.

  • I'm going to get init.

  • I'm going to add all my files.

  • I'm going to say initial commits.

  • And if you're unfamiliar with what I'm doing here,

  • definitely check out Brian Yu's GitHub seminar on the CS50 YouTube,

  • or his first lecture in the web course that he taught.

  • Did an awesome job showcasing how to use the basics of Git.

  • I'm going to get remote add origin.

  • This is just letting me be able to push it to GitHub and do all that fun stuff.

  • And again, if you're not super familiar with GitHub,

  • they actually have all the steps here, sort

  • of the same thing that I'm doing here.

  • Every time you create a new repo to give you

  • the command line instructions to actually get your code onto GitHub.

  • So I've done that.

  • Now I have a get repo.

  • I'm going to clear this because it's a little bit too much.

  • I have a get repo.

  • I've pushed it.

  • There should be a main.lua now on there, which is empty.

  • I haven't done anything in the main.lua, but at the end of the stream,

  • I'll push it with all the changes that we've made today.

  • And you can follow along or download the code and make your own modifications.

  • Awesome.

  • Sandwhich says, this reminds me of Typing of the Dead.

  • And that's exactly what this game actually was.

  • Or, well, I don't have the image up anymore.

  • But I googled image Typing of the Dead, and that is what the game was.

  • This game, all of these games--

  • this was the screenshot in particular-- it was indeed Typing of the Dead.

  • And that's the mechanic.

  • That's really to illustrate the mechanic more so than the implementation

  • of Typing of the Dead.

  • Because the core game loop just involves you reactively typing words

  • as they pop up onto the screen.

  • So that is the Typing of the Dead.

  • House of the Dead is the game that Typing of the Dead was inspired by.

  • They use the same assets, and actually I think

  • that it was literally the same game, just with typing mechanics.

  • But yeah, it was a cool way to integrate typing,

  • which isn't always all that exciting, with some sort of gamification.

  • Fun stuff.

  • Cool.

  • Let me just make sure I didn't miss anything.

  • JP Guy is plugging Brian's video.

  • FTC227, thank you very much for joining today.

  • Cool, cool.

  • All right, so let's get started actually doing some programming here.

  • So Typing Game.

  • So this is the obligatory header comment, comment block.

  • Name, email, title, nothing all that terribly useful.

  • But this is how you do comments in Lua comment blocks.

  • You can also just do a single comment with dash dash.

  • I'm going to write the core LOVE functions.

  • So LOVE expects a certain set of functions

  • to exist at the beginning of your--

  • sorry, in your main.lua.

  • And these functions it will call automatically as part of a game loop

  • that it runs underneath the hood.

  • So it calls love.load at the beginning of the game.

  • It calls love.update every frame, with DT passed in,

  • which is the number of seconds as a fractional number

  • since the last frame has passed.

  • And love.draw, which is just meant to be specified for all the drawing

  • operations per frame.

  • So update gets called, then draw, every single frame.

  • So usually every 1/60 of a second, every 0.013

  • milliseconds-- or sorry, 0.013 seconds.

  • And then there are some other functions, like--

  • and I think we will need this-- love.keypressed key.

  • And I'm not going to take the Hangman code that we did last stream

  • and copy and paste that.

  • We're going to do everything from scratch.

  • But I might refer to that if, for example, I

  • need to remember something that we did last time.

  • It's all still fairly fresh, so I might be

  • able to do this just from scratch without doing that.

  • But we have that option.

  • It's in my folder if needed.

  • And definitely, if you have any questions or suggestions along the way,

  • as always, definitely plug them in the chat.

  • I try to do my best to keep up and read every single message.

  • And if I miss your message, definitely let me know, and I will read it.

  • Cool.

  • So the game altogether is going to look--

  • and I feel like I owe it to sort of draw this out in advance, just

  • so that we know what we're aiming towards.

  • But I think the ultimate goal-- and actually, let me get my tablet here,

  • just cause I can do a better job of illustrating this with an actual tablet

  • than with my finger on a trackpad.

  • But I have a little Wacom tablet here.

  • So super nice.

  • I'm going to get rid of this keyboard here.

  • Get a bunch of stuff.

  • Got a awesome writer's room with a bunch of cables and awesome cool stuff.

  • But I did a poor job of setting up.

  • So the table's a mess.

  • So I'm going to clean this.

  • But now I have a tablet, or at least I should.

  • There we go.

  • So now I can actually draw if I want to.

  • And this is an awesome tool.

  • Dan Coffey, one of CS50's producers, head producer,

  • he actually created this application, draw.cs50.io,

  • and we had a stream on it.

  • So if you're interested in how it works, definitely check it out.

  • So this is what the game is going to look like.

  • I'm just going to write game view.

  • And obviously, there's not going to be the word game view on the screen.

  • But we are going to have a word kind of in the middle, just like this.

  • And then below it is going to not--

  • I don't think be like individual spaces for the word, but more like--

  • it's just going to be empty, and then as we type, we'll have W,

  • and then it'll type O, and then R, as we typed R. And then if we fail,

  • it'll go back.

  • All of these will sort of clear away and go back to an empty string below it.

  • And then up at the very top left, I think

  • we should have some kind of timer.

  • So maybe 60, and then it'll go down to 59, blah, blah, blah.

  • It'll tick down up in here at the very top left.

  • And then at the very top right, I think we'll have our score.

  • And so this will just be kind of 1 and then 2 and then 3.

  • And then-- actually, you know what?

  • I think it might be good for us to make the score

  • relative to the length of the word.

  • So we can make the score increment.

  • For every character of the word, we'll add one point to the score.

  • So if we-- so a word in this case will be worth four points

  • when we complete it.

  • Because that would be cool.

  • It kind of balances out somebody who--

  • like for example, if you and I played the game, and maybe I was unlucky

  • and I got like 10 really long words and you got like six really short words,

  • you would be able to type those six really short words probably faster

  • than I could type the really big words.

  • So it's fair that we get higher scores depending on the length of the word.

  • So we'll do that.

  • And that's a design sort of consideration for us.

  • And really, I think that's ultimately all we need for the game view.

  • I mean, just a timer, the score, and then the word.

  • And then below it, the version of the word that we are ourselves typing.

  • And we can maybe do them in different fonts, so we--

  • or sorry, not different fonts, but different colors.

  • So word could be, for example, in maybe in yellow.

  • And then this, the actual word that we're typing, could be in white.

  • Just to kind of help us illustrate the difference between what we're typing

  • versus what the word we're aiming for is.

  • And I think this is how Typing of the Dead

  • works too, based on the screenshot that we just looked at.

  • But yeah.

  • Bhavik Knight says, how can we use a tablet to draw on draw50?

  • You should just be able to get a tablet and plug it in,

  • and then you might need to install drivers,

  • depending on how old the tablet is and what your operating system is.

  • But usually if you're using a Wacom tablet, for example,

  • Wacom releases all their drivers on their website.

  • So you just choose whichever your tablet is and your operating system,

  • and then just install it.

  • And it just works.

  • It just magically works.

  • But yeah.

  • Cookie Bow, hello, good to see you.

  • So this is going to be the game view.

  • I don't know if we're going to do a lot of game state

  • today and worry about like a start and an end.

  • I might have like a thing that says press space

  • to start, just so that the game doesn't immediately

  • start and start ticking down in time.

  • I think the user should be able to, at their own pace,

  • decide when they want to start the game.

  • And then when it ends, maybe it'll display their score.

  • Excuse me.

  • So I guess there will be a state.

  • There'll be like a start, a game, and a score state.

  • And that's effectively all we need to worry about.

  • And we probably don't need to use state machines

  • for that, because those are going to be very simple states that

  • don't have much logic beyond just pressing

  • a key for the start and the end.

  • So yeah, that'll be I think the gist of the game overall.

  • That'll be the meat and potatoes of it, so to speak.

  • So we can get into it.

  • Again, I love draw.cs50.io.

  • Great tool.

  • Awesome.

  • I think I should start more proactively preceding this stream

  • with a view of what the game will look like,

  • just so we know what we're working toward.

  • Awaro, thank you very much for following.

  • All right.

  • So I guess what we can do is we can sort of sketch out what everything will look

  • like, just so we can see it visually before we start actually plugging

  • in or wiring the application.

  • So let's do that.

  • So I'm going to say, I want my window width to be 1280, again, just

  • like last time, and window height to be 720.

  • And then love.load, I'm going to do love.window.set mode.

  • And this takes in the width and height to create the window for LOVE.

  • This is a LOVE 2D function, love.window.set mode.

  • And if I run this, now we see that the window takes up--

  • this is a LOVE window.

  • It's taking up the entire screen.

  • I'm running my laptop right now in 720p mode,

  • just so everything is nice and visible.

  • I'm going to make it so that Escape will exit out the window.

  • So I'm going to say if key is equal to Escape, then love.event.quit.

  • And we've seen this before a bunch of times on stream.

  • Love.keypressed just waits for any key to be pressed,

  • and you can define the behavior for each key inside of that function,

  • and LOVE will do this code whenever you press that key.

  • All you have to do is just check to see what the key is,

  • and then execute logic accordingly.

  • You can do whatever you want in this function,

  • but this is typically what you'll see.

  • You'll see if something, so the key is equal to some value, do some behavior.

  • So in this case, we're calling love.event.quit.

  • It's just a function that LOVE comes with that lets you quit the program.

  • So now I can press Escape.

  • I don't have to Command Q or click the red x at the top left,

  • and the game indeed quits.

  • Super simple.

  • The game probably isn't going to look that beautiful today,

  • just black and white probably with yellow text as well.

  • So I'm just going to start drawing some text.

  • I will, I think, pick a nicer font than what

  • we used last time, maybe a monospaced font,

  • just because last time the font was a little weird.

  • We had the L and the M were kind of weirdly spaced.

  • It was a nice enough looking font, and I think for most text purposes,

  • it works just fine.

  • But let's do a pixel or bitmap font that's monospace, just

  • so everything looks kind of consistent.

  • I'm using dafont.com.

  • This is a website I love to use to find fonts.

  • They all have their licenses shown on the right side of the page,

  • as you're looking through them.

  • So I'm probably going to choose one that's 100% free.

  • Upheaval's OK, because it's all caps.

  • And we don't really need to differentiate between upper

  • and lower case for this example.

  • But Pixellari's not bad.

  • It's not monospace, but it looks OK.

  • Press Start to Play.

  • This is one that I use a lot.

  • It's a very NES looking font, if you're familiar with the old NES

  • and the games.

  • Most of the NES games looked like they all had the same exact font.

  • So I think that's what it's trying to emulate there.

  • Neoletters, this is a cool looking font.

  • I like that.

  • Runescape, which is public domain, just the font

  • that they used for Runescape, which I hear they're sort of bringing back,

  • which is interesting.

  • I remember playing that many years ago, and it

  • was a very grindy, but at the time, pretty cool game.

  • Written in Java too, which is a rare thing to see for a sort of online game

  • like that.

  • Silk Screen's cool.

  • I'm trying not to spend too much more time.

  • Let's just figure out what would be a nice font.

  • This is a font that I often use, 04b03.

  • There was a really cool font that I used when I did a sort of a version of this.

  • I think it might have been this one, Alchemical.

  • I'm going to download this one.

  • This is public domain, so it's free to use for basically whatever you want.

  • So I downloaded it.

  • It gave me a TTF file.

  • I'm going to go ahead and copy that into my streams, typing game.

  • I'm going to create a Fonts folder, and copy it

  • right into there, alchemical.ttf.

  • I'm just going to call it font.ttf for ease of referring to it from the game.

  • So that's how you get a font.

  • Go to dafont.com, download a TTF file.

  • And then what I can do up here is I can say local font is equal

  • to love.graphics.newfont, fonts/font.ttf.

  • In love.load, I'm going to set that font as my actively drawing font.

  • So LOVE won't do anything with a font when you create--

  • I've just created an object here.

  • I haven't done anything with it.

  • I've just said, I want a font object.

  • But it's not being used to draw anything,

  • because LOVE basically can only have one font active at a given time.

  • So I can change that and say love.graphics.setfont, font,

  • and then now it'll start using it.

  • It will actually start drawing with it.

  • skinzo1998, thank you very much for the follow.

  • So now I can--

  • first I have to actually draw something to see that I'm changing the font,

  • that it's actually being used as the active font.

  • So I'm going to say love.graphics.print, "Hello, world."

  • Just a classic, timeless programmer test.

  • And it's very, very tiny.

  • So I'm actually going to set the font to be really large.

  • So you can pass a second parameter to a font object

  • and create it however large you want to.

  • I'm going to create this one to be 64 pixels big.

  • And when I do that, I indeed see that the font is rendering appropriately.

  • Hello, world.

  • And it looks kind of almost like a horror game kind of, but not 100%.

  • Also kind of looks like an oldie font, like a medieval style font.

  • And it's kind of cool.

  • It's appropriate for-- if we are very, very

  • roughly theming our game today on Typing of the Dead, seems appropriate.

  • Asley says, that font would be hard for dyslexics,

  • but then again, a dyslexic wouldn't play a typing game.

  • Very good point.

  • Very aptly noted.

  • I think 64 pixels is going to be good for us today.

  • So that'll be our font size for everything.

  • So every message in our whole game, we'll display that in 64 pixels.

  • Let's go ahead and sort of outline everything appropriately.

  • So I'm going to first, in the very center of the screen,

  • I'm going to emulate a word that the user would be asked to type.

  • And again, as we specified earlier, this should be

  • in yellow text instead of white text.

  • So I'm going to say love.graphics.printf,

  • and we'll just say the word is CS50.

  • And I'm going to say that it should start at 0, x.

  • It should be roughly in the center of the screen, so not

  • virtual height, window height divided by 2 minus 32.

  • The minus 32 is to sort of account for the height of the font, which

  • is going to be 64 pixels.

  • I'm going to say that it should fill the width of the window width.

  • And then it should be centered.

  • So this is a function of the printf function.

  • You basically start drawing it at zero.

  • You say that I'm going to format it relative to the window width,

  • like it's going to be formatted within a block that's the size of the window

  • width, starting at zero.

  • And if you center it, that gives it the result of centering between zero

  • and the window width, which gives it right in the middle of the screen.

  • So I do that.

  • Indeed, 650 gets drawn right there in the middle of the screen.

  • So super cool.

  • It's not being drawn in yellow text.

  • So I'm going to call a function called love.graphics.setColor.

  • And I might have to look up what the RGB is for yellow.

  • I think it's-- what would it be?

  • Would it be red and green RGB?

  • Not 100% sure.

  • I'm going to mess around with a few options here.

  • I'm going to say 1, 0, 1, 1.

  • Nope, that's magenta.

  • So that's not it.

  • I'm going to say 0, 1, 1, 1.

  • That's blue.

  • OK.

  • I'm going to say 1, 1, 0, 1.

  • I think this might be it.

  • Yeah.

  • So love.graphics.setColor, basically it accepts--

  • oh yeah, and people are saying there in the chat.

  • People-- sorry, the love.graphics.setColor

  • accepts four components, so red, green, blue, and yellow, RGB alpha.

  • Sorry, red, green, blue, and alpha, not yellow, if I said yellow.

  • RGBA, so Red, Green, Blue, and Alpha.

  • And basically these are just components between 0 and 1,

  • often between 0 and 255, that specify how much red, how much green, how much

  • blue, and how much transparency or opacity a color should have.

  • And by mixing and matching different values between those components,

  • you get different colors of different transparency levels.

  • So we're mixing red, full red, full green, and no blue gives us

  • a yellow color, as such.

  • Cool?

  • Now here, like below the CS50 in yellow would

  • be where we would draw our progress.

  • And really, it would be drawn--

  • hm.

  • So we couldn't draw just the word centered,

  • our incomplete word centered below our word.

  • Because ideally what we'd want to do is have it such that the--

  • I don't have draw.cs50.io anymore laid out.

  • But if we were to draw--

  • for example, if we were to have, in the middle of our screen,

  • CS50 like this, and somebody were to draw, like to say type the first C,

  • and we centered what they've typed so far, it would draw here.

  • But that's not what we want.

  • We want the C to start drawing here, and then

  • the S to draw here, and then the 5, and then the 0.

  • So what we really need to do is figure out a way to offset the CS50.

  • And one way we could do that is by centering it

  • relative to a smaller amount of space.

  • We can subtract the amount of space that it would take up.

  • We can get to that bridge when it comes to it.

  • But that's a consideration that we'll need to think about

  • as we implement our game.

  • So we can do that.

  • We'll worry about that a little bit later.

  • But below the CS50 in yellow is where we would

  • want to draw the progress of the word we're typing in white.

  • So draw the current--

  • sorry, draw the current goal word in yellow.

  • And then draw the progress of the word we're typing in white.

  • And we'll get to that pretty soon.

  • We're going to need some sort of data structure

  • to store our progress of the word, which we haven't done yet.

  • Another thing that we need to model is drawing the timer.

  • So we'll say that that's a to do.

  • And we're going to need to draw the timer in the top left so what we can do

  • is I can say I love.graphics.print.

  • And I'm just going to do a print.

  • I'm not going to do a printf.

  • I don't need to do a printf to draw relative to the top left, because it's

  • going to draw literally there.

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

  • And then let's just assume--

  • let's just temporarily put in 60, and that's it.

  • As a result of that, as a result of the fact that I'm just drawing literally

  • love.graphics.print without specifying an x and a y,

  • it's going to assume 0, 0.

  • Window width minus the font width, yes.

  • Effectively, that's going to be it, Bhavik Knight.

  • Good point.

  • And what it's actually going to be, is it's

  • going to be font width up till the length of the current word for what

  • we've typed.

  • Not the full word, because if we did that,

  • it would be shifted before the word actually begins,

  • and it wouldn't make sense, because we wouldn't be

  • centering invisible text to begin with.

  • But if we type in C, for example, we're going to want to subtract--

  • we're going to want to center it relative to window

  • width minus the length of the string C. And that will give us

  • the centering that we're looking for.

  • So very aptly noted.

  • Top left, we can see we have a slight bug here.

  • And this is the result of the fact that after we've

  • set the color to 1, 1, 0, 1, which is yellow,

  • we're not actually setting it back to white.

  • So you can only ever have one color active at once.

  • And by activating it and not reverting it back to white,

  • we basically put our game into a permanent yellow state

  • for drawing stuff.

  • We don't want that.

  • So what I'm going to do, I'm going to say love.graphics.setColor, 1, 1, 1, 1,

  • after I print CS50.

  • And then you can see in the top left, we do indeed

  • get our timer printed in the correct color.

  • Now, the next thing we want to do is draw the score in the top right.

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

  • And I'm going to do the same thing here, but I'm going to say, I guess, 24.

  • Now, this actually isn't going to be a print.

  • This needs to be a printf.

  • Because what we want to do for this is we

  • want this to actually be right aligned at the top right corner of the screen.

  • So always touching the right edge, but padded depending on how big

  • that number is.

  • And for this, we do need a printf.

  • Because printf, not only can it center, but it can also right align stuff.

  • Left align you don't usually need to format for, but right align you do

  • need to do so.

  • So what we're going to do, is I'm going to say love.graphics.printf.

  • Again, 0.

  • This is going to be 0 again.

  • So x on the 0.

  • So we're going to say, start at the left, far left.

  • And I'm going to say we're going to draw at 0 on the y.

  • We're going to do window width again.

  • We're going to say the formatting needs to be the width of the screen.

  • So it's from 0 to the end of the screen.

  • And then instead of centering it, I'm going to right align it.

  • And this may or may not be easy to see, depending on the text chat.

  • But at the very top right--

  • I'm going to move the window here--

  • you can indeed see that we have 24 written out

  • at the very top right, right aligned and not center aligned.

  • And today we actually also have a very special guest.

  • DAVID: Wow, a guest.

  • Guest is here.

  • David has to leave really quickly, since the team is

  • going to see me on the internet in a few seconds and realize that I'm not

  • where I need to be with them.

  • But I just wanted to wave hello to everyone.

  • COLTON OGDEN: I'm going to get some very angry messages here soon saying,

  • why the hell is David over there?

  • DAVID: I see you got some more Lua going on today.

  • COLTON OGDEN: Yeah, we do.

  • We're implementing a Typing of the Dead, the typing game, so to speak.

  • So I'll pull up an image so you can see what we're doing here.

  • Typing of the Dead.

  • DAVID: Colton has claimed for years to be a faster typer than me,

  • but we've never actually competed.

  • COLTON OGDEN: Yeah.

  • So here is what it looks like.

  • So it's kind of like--

  • in this case, it's an abstraction over a typing game.

  • You're actually fighting zombies.

  • But the goal of the loop is, you type this word here in yellow.

  • DAVID: Ping-pong.

  • COLTON OGDEN: Yeah.

  • Just that word, just ping-pong.

  • DAVID: This looks pretty easy.

  • COLTON OGDEN: So that's ping-pong typing.

  • And then you reactively are sort of looking for these words,

  • and typing them, and when you succeed, you shoot the zombies,

  • and you get a higher score.

  • DAVID: So this is all about making learning fun.

  • COLTON OGDEN: It is.

  • It is.

  • DAVID: Really it's just a typing game with some scary pictures behind it.

  • COLTON OGDEN: It is.

  • And notice that we chose kind of a scary font too,

  • so we're really bumping it up.

  • DAVID: This is great.

  • I'd love to play later on.

  • I have to go to another class actually.

  • But looking forward to tuning in online.

  • COLTON OGDEN: Yeah, we can compare scores on stream later, if you want.

  • DAVID: There we go.

  • Nice.

  • Well, good to see everyone.

  • I'll try to pop into chat later as well.

  • COLTON OGDEN: Oh, notice that we're also not the Simpsons today.

  • We're not turning yellow.

  • DAVID: I know.

  • You look a lot better today.

  • COLTON OGDEN: Thank you.

  • I appreciate it.

  • So do you.

  • So do you.

  • DAVID: Bye, everyone.

  • COLTON OGDEN: Cool.

  • See you.

  • Good luck.

  • All right.

  • So we have the different main textual components on the screen now.

  • So we have CS50, our yellow target word.

  • We have our timer in the top left, and then we have, if I scroll here again,

  • the score place holder in the top right.

  • And this is a common thing you'll see a lot, particularly in web development

  • actually, sort of building up a mock--

  • so this is just a mock--

  • of a UI before you actually wire it together,

  • before you implement all the pieces, just so that you make sure

  • everything looks OK.

  • And then we can start adding the logic and the functionality as we need to.

  • So yeah, everybody, thank you so much for giving David

  • some nice shout outs there.

  • Good idea.

  • Next secret stream could be you competing.

  • Yeah, that would be--

  • I mean, between us, I don't think David stands a chance.

  • But you know, we'll see.

  • We'll see.

  • So yeah, those are all the main UI components, I guess.

  • Again, we're going to get the actual progress of the word we're

  • typing in white later drawn.

  • Well actually, you know what we could do?

  • We could test this out.

  • We could determine whether the logic that Bhavik and I talked about

  • is accurate.

  • So let's do that.

  • Let's say love.graphics.printf.

  • And I'm going to say CS5 for right now.

  • And for consistency, I'm going to change this from double quotes

  • to single quotes.

  • CS5.

  • So let's pretend that the user has typed just the first three letters, but not

  • the zero, the remaining character.

  • I'm going to do kind of what we did up above.

  • Window minus 2.

  • And then I'm going to say plus 16, just so it's

  • a little bit below the other word.

  • Window width and center.

  • And then we can sort of see what I was alluding to before, the fact

  • that the CS5 isn't right below the CS50.

  • It's kind of shifted over because it's actually centered relative

  • to its own length, its own size.

  • It's not mapping right underneath it.

  • Because we're just literally centering across the entire width of the screen,

  • right?

  • So what Bhavik suggested was that we, instead of centering relative

  • to window width, we subtract the size of the string

  • that the user has currently typed, which we can do.

  • We can say window width minus--

  • and then offhand, I have to remember what exactly the function is.

  • But love font text size.

  • This would be something to Google, if you're not familiar,

  • if you don't remember.

  • I'm going to just decrease the size of the browser

  • here so we can read together.

  • It's going to be in here somewhere.

  • It will most likely be in here somewhere.

  • And we might have to look through the documentation

  • to determine exactly where it is.

  • So I'm going to say--

  • I'm going to go to love.graphics.

  • I'm going to look up the font.

  • I think it's a function on the font class.

  • So I'm going to go here, font, get--

  • let's see, getWidth.

  • OK.

  • So we can see here there's font getWidth.

  • So if I go to here--

  • so what it basically expects is that, given a font object, which we have

  • seen earlier, we can pass in a string.

  • And that string will give us--

  • I mean, it will take the font object.

  • It will get the string that we pass into it, and it'll say--

  • because it knows the size of its own characters, its own glyphs.

  • It'll give us a number that's actually the length of that particular string.

  • So I can say--

  • I'm going to bring this down to a new line, just for readability--

  • I'm going to say window width minus font getWidth the length of CS5.

  • And if I'm not mistaken, this should--

  • oh, you know what?

  • Actually, that's not what we need to do.

  • We need to-- it's actually the inverse.

  • It's the inverse of the string.

  • We need to give it the--

  • we need to minus the length of just the missing character.

  • And so that's a little bit trickier.

  • So we need to effectively determine what the string is between what we've typed

  • and the end.

  • So we can definitely do this.

  • I can say-- let's see.

  • What would be the best way to do this?

  • I guess CS50, it would have to be, for example, CS50 sub--

  • remember, because the sub function is the substring function.

  • So this is what allows us to specify a start and an end index

  • and give us to the end of the word.

  • Asley's saying, does only Lua have the font getWidth function?

  • It's a LOVE 2D function.

  • So it's not Lua itself, but the LOVE framework gives this to you.

  • And a lot of other game frameworks will give you something very similar

  • to this.

  • But what I need to do is I need to determine the width of basically CS50

  • minus CS5, which is just 0.

  • So I need to get--

  • I need to take the full string, and then sub it from the length of the string

  • that we've typed until the end of the string.

  • So is there a way to get the length of the string in Lua?

  • So let's determine this.

  • Lua get length of string.

  • It's probably a string getLength function, I would have to imagine.

  • String dot-- I'd imagine there has to be a string.length function.

  • Turn the-- oh, I saw it.

  • Length.

  • Oh, is it just dot leng?

  • String.leng?

  • Oh yeah.

  • Oh, so just call in leng.

  • OK, so that's easy.

  • Oh, string.leng Lua.

  • Oh, but we can also pass it, use the colon thing.

  • OK.

  • So if I say CS50 sub from--

  • so this would be the--

  • what's it called?

  • Basically, we need to start it at index 3 until the end.

  • Right?

  • Or would it be 4 until 4?

  • Yeah, because we need to get the character

  • after where we are until the end.

  • So this would be from 4 to 4, not from 3 to 4.

  • Because otherwise that will give us 50.

  • So we need 4 to 4.

  • So this would be the length of the string

  • plus until the end of the string.

  • So let's say-- this is somewhat a little bit complex,

  • but we're going to say CS5 leng plus 1 until CS50 leng.

  • And eventually, this isn't going to be CS50 here and CS5 and CS50.

  • These are going to be variables that change, depending on the current word,

  • and depending on what the user has currently typed.

  • But-- oh, it looks like I missed my parentheses up here a little bit.

  • Let's determine where I--

  • OK.

  • Sub, do that.

  • Do I have an extra parentheses there?

  • I guess I must.

  • OK, let's try that.

  • Uh, shoot.

  • My apologies.

  • Let's figure this out here, make sure-- oh right, OK.

  • Parentheses goes there.

  • I think?

  • Crap.

  • Oh, no.

  • Printf goes there.

  • OK.

  • So the getWidth then goes like that.

  • OK.

  • Minus font getWidth.

  • It was like that, right?

  • OK.

  • And then center goes there.

  • Where do I have this messed up?

  • OK.

  • I apologize.

  • That goes there.

  • CS50 sub.

  • Is it-- are you not allowed to pass in raw string--

  • let's test that in Lua.

  • So Lua, and if I say "Hello" leng.

  • Oh, OK.

  • That's what it is, I think.

  • You can't actually call it on raw string literals.

  • So I have to say local string is equal to CS50.

  • We'll call this fullString.

  • And we'll call this halfString, CS5.

  • I'm going to replace this with halfString.

  • This is going to be fullString.

  • This is good.

  • And this is actually better, because this

  • is going to model more closely what we're looking at eventually.

  • It'sTheBatman1 says, hello, Colton.

  • I used to watch your streams on YouTube.

  • I made the Twitch account just for this channel.

  • Great fan.

  • Awesome.

  • Thank you so much.

  • I appreciate you joining in and getting onto Twitch to be a part of the chat.

  • Super awesome.

  • What was here before?

  • Oh yeah, halfString.

  • halfString length plus 1.

  • fullString length.

  • And OK, so if I'm not mistaken, this should work now, right?

  • Yes.

  • Excellent.

  • And just as predicted, it does indeed fit right below the thing there.

  • Oh, I apologize.

  • Let me make the chat a little bit smaller.

  • Let's go here.

  • Mess around with some stuff a little bit.

  • Let me know how that looks.

  • Hopefully it's still readable.

  • OK, cool.

  • Awesome.

  • So we successfully did it.

  • So effectively, what we're doing is we have

  • to take the string that we want to render,

  • which is CS5, the progress of the word that we're currently writing.

  • We need to figure out what the difference is

  • between that string and the full string and shift

  • it left based on that amount effectively, is what we're doing.

  • And so I'm taking the length of the halfString, which

  • is going to be three, adding one, because we

  • want to start the substring--

  • if you're calculating that difference in strings.

  • I want to go to the next index, so we don't get one extra letter.

  • So starting at the fourth index, in this case, the zero.

  • And then I want to go until the end of the fullString, which

  • also is 4 for CS50.

  • And this will always give us the shifted string.

  • It always shift our progress of our current world

  • over just the right amount.

  • So that is honestly probably the most complicated part

  • of what we're going to be looking at today,

  • is just getting that formatted appropriately and shifted over.

  • But it looks great.

  • It looks awesome.

  • It's working.

  • So yeah.

  • Everything's awesome.

  • And we can test this.

  • Again, we can say the halfString CS, for example.

  • And again, it renders over just perfectly right below the word.

  • And if I were to, for example, make fullString--

  • I'm actually going to bring these up here.

  • fullString is going to be Colton, and we'll say halfString is Col.

  • And then I'm going to replace this with fullString.

  • Again, that also renders.

  • We can make it arbitrarily long.

  • I can say Supercalifragilistic, and say Supercali right there.

  • And again, everything is completely mapping

  • between the target word and the progress of the word

  • that we're typing on the screen.

  • So everything looks amazing.

  • And I think also eventually this is going to be all caps,

  • just so we don't differentiate between the two.

  • So the user can just type as they want to, and everything makes sense.

  • But yeah, that looks great.

  • That's the UI.

  • That's the gist of the UI.

  • It's not going to be much more complicated than that.

  • So let's get to actually wiring this together.

  • Let's start with a timer I think, because that's

  • a really easy thing to plug off and a cool thing to show in LOVE.

  • The library that we're going to use for that is called knife,

  • so knife library love.

  • And it's here.

  • So you can look and see-- oh, well rather this is the LOVE 2D forums page.

  • So if you're getting into the LOVE 2D programming scene at all,

  • and you want to ask a question of the community, go to love2d.org/forums.

  • They have an awesome set of forums.

  • And the knife library--

  • again, very appropriate for a zombie-themed stream,

  • because at the end of the world, zombies typically shooting them, stabbing them.

  • The knife library is great.

  • It's got a bunch of really cool modules that let you do some really cool stuff.

  • And we won't get into too much of it, but one

  • of the ones that we will look at-- and I think we've looked

  • at before on stream-- is called knife.

  • And there's a GitHub link here.

  • So you go to that, airstruck/knife.

  • You can clone it or download it from here.

  • Again there's the knife.

  • So just look for this knife image.

  • You'll know that you're at the right library.

  • And in particular, we're looking for knife.timer,

  • which is here at the bottom.

  • And I think we used knife.timer for the snake stream.

  • I don't 100% remember.

  • It's been a little while.

  • I should already have it, actually.

  • So I'm going to go to snake.

  • Snake backup.

  • Am I misremembering?

  • I might be misremembering.

  • I thought I had the--

  • oh, you know what?

  • We implemented our own version of a timer

  • for that, now that I think about it.

  • So yeah, we haven't really ever looked at timer, I don't--

  • have we not looked at timer?

  • I could have sworn we did a stream on it at some point.

  • Anyway.

  • I have it here somewhere, but I can't find it.

  • So I'm going to knife airstruck.

  • I'm just going to clone it again.

  • So grab the Git URL there, HTTPS.

  • I'm going to go to--

  • I'm going to click that, exit out of this LOVE interpreter.

  • See where I am.

  • OK.

  • I'm going to mkdir lib.

  • I'm going to cd lib.

  • I'm going to get clone that URL.

  • So I get the knife library.

  • And now if I go over to my Typing Game folder, go to lib,

  • it's indeed inside of my lib folder.

  • Now what I want is this knife folder.

  • So I'm going to copy that.

  • Call this cloned knife.

  • I'm going to paste this in here, and then get rid of cloned knife.

  • Because I effectively just wanted this sub folder

  • within the repo, which just has all the Lua modules.

  • And now what I can do is I can say, in my main.lua,

  • timer is equal to require lib/knife.timer.

  • You can go into directories.

  • You can sort of go down deeper and deeper by just using dot.

  • Just like you would for a file extension,

  • I can say require lib/knife.timer.

  • And actually, I think it's a little bit different.

  • I think it's not so much the directory, because you

  • do see the folders above is slash.

  • I think it's specifically any Lua module can be referenced

  • from within a folder with dot.

  • So I'm not 100% on that.

  • But typically you'll see path and then the folder and then

  • dot whatever Lua module you want.

  • So knife.timer in this case, which means knife/timer.lua, which is right here.

  • And what I can do now is I can say, in my love.load--

  • I'm going to just start this from the very beginning--

  • timer.every one second, I'm going to create an anonymous function.

  • And this is how you basically say every one second,

  • I want to execute some body of code, but I don't want to declare it somewhere.

  • You can define what's called an anonymous function, which is just

  • a function that lives somewhere.

  • It's some code, but doesn't have a name.

  • So you can't call it explicitly from somewhere else.

  • You can only basically put it into a function that

  • expects it, that it can call it later.

  • So that's a callback function.

  • And I'm going to say, every one second, execute the following function,

  • where I'm going to say the current time equals current time minus 1.

  • I'm going to up here say local current time is equal to 60.

  • And then down where we rendered the timer,

  • I'm going to instead print toString current time.

  • And if everything goes well--

  • OK, that's nil, so that's not appropriate.

  • So current time, because I had a typo.

  • Now we can see--

  • oh, and that's not going to work.

  • So you'll notice that it's not actually decrementing the 60 at all.

  • So it's still at 60.

  • If you're going to introduce sound effects,

  • can you move it to dependencies, says Bhavik.

  • We're not going to introduce sound effects.

  • So dependencies are going to be unnecessary

  • for this particular application.

  • But if we were making a larger game, certainly that would be something

  • you'd want to think about.

  • I'm going to-- an update.

  • One important step with timer, with the timer library, is that--

  • and Asley has the right intuition.

  • She says because we didn't call timer, you

  • have to call timer.update delta time within the love.update function,

  • and that will update the timer so that it actually

  • keeps track of when we need to execute those callback functions that we

  • specified, every one second.

  • So indeed, at the top left, you'll notice that it's decrementing the time.

  • So 55, 54, 53.

  • And we can therefore have the logic that says, when we reach 0 seconds,

  • the game should transition from some sort of play state to a score state,

  • a game over, that tells us what score we got,

  • and then expects us to press Spacebar to resume the game once again.

  • So that's cool.

  • We have that working.

  • Supercalifragilistic, we're going to see that a few times before we

  • actually have a dictionary of words.

  • And speaking of a dictionary of words, I think

  • it would be kind of fun to include that and randomize that as we begin.

  • So I'm going to say local words is equal to an empty table.

  • And I want to fill this up with a collection of words of varying sizes.

  • And it would be cool, I think, to use the CS50 speller list.

  • And I'm going to have to read and remember how we actually load a file.

  • It's not terribly difficult in LOVE.

  • But a lot of the time, when you get into this sort of thing,

  • you get into file system related limitations and needing

  • to put stuff in specific places.

  • PSET4, speller, large.

  • Yes, I don't remember offhand what the exact link is.

  • I think it's on our CDN.

  • So I'm going to go to cdn.cs50.net.

  • 2018, fall, psets, four, speller--

  • I don't know if one of these matters.

  • Spellers texts, is it-- oh no, dictionaries large.

  • There we go.

  • Let's download the dictionary.

  • So it's a large dictionary.

  • And it should just be a list of new line separated strings.

  • So I'm going to go into wherever I downloaded that.

  • Downloads is here, I think, right?

  • Yep.

  • Copy that.

  • It's 1.4 megabytes.

  • It's actually pretty big.

  • I'm going to go into--

  • now I don't remember offhand if this needs to be in a specific location,

  • but I'm going to put it into here, into my actual directory.

  • This may need to go into my application support directory on a Mac.

  • And this may need to go into a Windows directory, if you're on a PC.

  • There are specific things that LOVE expects for files like this,

  • but I think I can get away with having it be inside the directory itself,

  • the LOVE directory.

  • I'm going to have to look that up right now, actually.

  • So love load text file.

  • Reading from, displaying, and parsing a text file.

  • love.filesystem.read.

  • This is it.

  • OK.

  • Read the contents of a file.

  • So contents and size is equal to love.filesystem.read, then name

  • and a size.

  • And again, this is a big part of not just LOVE development or game

  • development, but just development is, if you don't remember a function,

  • Google it.

  • Get good at googling it.

  • Type the right words that are relevant to what you're

  • trying to do without being too verbose.

  • Often just the keywords are important.

  • And read through the documentation.

  • So very important thing.

  • AIZOTV, hello, thank you for joining.

  • Appreciate it.

  • And thanks, Bhavik, for tossing in the chat pset4 speller large.

  • Cool.

  • So let's call that.

  • Let's try that out first.

  • I don't remember offhand exactly whether the file needs to be in, like I said,

  • the application support directory.

  • But we're going to figure that out together right now.

  • I'm going to, again, contents, size, and basically copying what it

  • shows in the documentation entry here.

  • Read the contents of a file.

  • It needs to be a set of lines, not just a byte.

  • So contents is going to be a string.

  • And so we're going to need to separate that string on the newline character.

  • And offhand, I don't remember what the function is for that in LOVE.

  • But let's just make sure that this is working.

  • So read large.

  • Let's just say that.

  • love.filesystem.readlarge.

  • And I'm going to--

  • not print.

  • I guess I can do that down here.

  • I'll just say love.graphics.printf toString size.

  • And is this the number-- that's the number of bytes of the whole file,

  • correct?

  • How many bytes have been read, yes.

  • OK.

  • So we'll do that.

  • And I can reference it because these are two global variables.

  • So contents and size are going to be accessible from anywhere

  • in my application.

  • And we're going to move this up above to proceed to love.load functions.

  • So that it's-- and we're going to make them local,

  • so that they're just module level and not global, properly global,

  • which is just better practice.

  • In the event that we had multiple other files,

  • size wouldn't be accessible everywhere.

  • That's a kind of very generic variable name

  • that you don't want accessible throughout an entire application.

  • love.graphics.printf toString size bytes loaded.

  • I'm going to say this should be 0, 0, and then maybe

  • 64 window width and center.

  • So this message is going to be right in the middle of the screen.

  • And it works.

  • Actually, it doesn't need to be in a specific location.

  • So you can see that is indeed 1.4 megabytes.

  • So we have 1,439,228 bytes loaded from this large file, right?

  • So this is an entire dictionary of words that CS50

  • has included in its problem set.

  • And we can actually look in Visual Studio Code.

  • We can see that.

  • And we've looked at this on another stream

  • when we implemented speller in Python, we looked at this.

  • But these are all the words in the CS50 dictionary, and it's massive.

  • So I don't know how many lines there are exactly.

  • But we can go down here.

  • About 143,091 lines of text.

  • So pretty sizable.

  • We can safely say that every time we play this game,

  • it'll probably be different, quite different.

  • There will never be a same run of the game, so to speak.

  • But the next thing that we need to do is,

  • we need to split this dictionary up.

  • Because again, if we look at the documentation,

  • we can see that it not only gives us contents,

  • but that contents is a string.

  • It's not a list of strings.

  • It's not something that we can say, get a random index in this table,

  • and then make that the currently active word.

  • No, it's going to have to instead--

  • we're going to have to split this string up ourselves, right?

  • So I thought there was a file system read lines function.

  • Let me look at that. love.filesystem.

  • And then let's go ahead and look and see. love.filesystem.read.

  • Hm.

  • Dot lines.

  • There we go.

  • OK.

  • OK.

  • So there is a function.

  • So there is an iterator.

  • So this actually makes our life a little bit easier.

  • So let's do that instead, love.filesystem.lines.

  • And we're not going to get size anymore.

  • It's just, again, you can look at the documentation

  • to see that we get an iterator.

  • And an iterator in Lua is what we can use to call the pairs function, right?

  • So we don't have to call the pairs function because the pairs function

  • iterates over an iterator, and that's effectively

  • what love.filesystem.lines does.

  • So we can say basically for line in love.filesystem.lines,

  • which is kind of the same thing as calling pairs table for something

  • in pairs of something.

  • But basically, they call pairs underneath the hood

  • in love.filesystem.lines, which is why it's an iterator and it works.

  • That's why we can call for line.

  • Pairs returns an iterator, and then love.filesystem.lines

  • returns an iterator.

  • So I'm going to say for line in.

  • So first of all, let's get this out of love.load.

  • And local words equals empty table.

  • I'm going to say for line in love.filesystem.lines, do--

  • right, that's correct- table.insert words.

  • We're going to insert into our words dictionary line.

  • And Bhavik Knight is saying, is an iterator similar to a Python generator?

  • Yes, very similar.

  • And then now, we can do-- instead of saying number of bytes loaded,

  • I'm going to say toString the length of words, words loaded!

  • And if you run this, we indeed see, just as we

  • looked at in our VS Code, our editor, we have 143,091 words now in a table,

  • now loaded somewhere in memory, that we can choose.

  • And to test this, let's go ahead and let's seed our random number generator.

  • So this is an important step.

  • Any time you develop a game, and you want to use randomization,

  • you're going to need to call math.randomseed os.time.

  • And a random seed just basically says to your random number generator,

  • here's a starting value.

  • And this is how you're going to base your pseudo random algorithm on.

  • This is what's going to be the foundation of it, so to speak.

  • And then we're going to seed it with the particular value of os.time, which

  • returns the number in milliseconds, since the UNIX Epoch, which

  • is a really large number that's always different every single time

  • we run our game.

  • And as a result, our random number generator

  • will be different every single time we run our game here.

  • So I'm going to change the word now.

  • So instead of fullString being equal to Supercalifragilistic,

  • fullString is going to be equal to words at index math.random.

  • And then it takes in a number, and that's going to be length of words.

  • So we want to get a random index into our words table.

  • So choose a random number between 1 and the size

  • of the table, which is 143,091.

  • And halfString, we're not going to actually know what halfString is.

  • But we can generate halfString by saying fullString sub

  • 1 to length of fullString minus 1.

  • And-- oh, I did an update, which is not what I want to do.

  • This needs to be done at the start of the game.

  • That's my fault. Let's go ahead and put this over here.

  • And-- whoops.

  • local, fullString, halfString.

  • Where did I mess that up?

  • main.lua:19, attempt to index local fullString.

  • Wait, what?

  • Sorry.

  • Main dot-- OK, attempt to index the local fullString a nil value.

  • It's getting-- oh, wait.

  • It's getting a nil value.

  • Oh, because I did this between words being initialized and not--

  • or sorry, word being defined and then initialized needs to come afterwards.

  • Sorry.

  • My fault. Stupid silly bug.

  • But we can see that now we're getting a random word, sharpens.

  • And since I basically said give me a random substring that's

  • the word minus 1, the start of the word minus 1,

  • it's giving us everything but the S at the end, so sharpen.

  • We can see it aligns perfectly, and it's a random word.

  • Cool, cool.

  • The time since the game started, right, says Asley.

  • I'm not sure what you're referring to.

  • I apologize.

  • Bhavik Knight says, time since the Epoch.

  • Oh.

  • Oh, right.

  • The time that's being generated.

  • The os.time is the--

  • so if we look up--

  • this will be a nice little bit of information--

  • UNIX epic time.

  • And you can see this here.

  • UNIX time, known as POSIX time or the UNIX Epoch time.

  • Basically, it's the number of milliseconds

  • since Thursday, January 1, 1970, UTC, minus leap seconds, apparently.

  • And this is just a really long number in milliseconds

  • that has happened over the last--

  • what is that, 49 years?

  • Roughly, a little over 49 years.

  • And this number, just by definition, is going to be different

  • every single time you get it.

  • It can only be a different number because it's only ever increasing.

  • So it's always going to be different.

  • And this is why people use this to seed the random number generator, usually

  • in the context of anything.

  • But this is effectively what os.time is, is what it is.

  • And this is something that you see all over the place.

  • You can use-- I think there's a UNIX command to get it too.

  • But if you ever are curious, and you see this in the future,

  • this is exactly what that is.

  • And it's very useful in games programming.

  • So cool.

  • Before inserting words in the table, says ItsTheBatMan.

  • Yes, yes.

  • Thank you.

  • Any particular reason it's 1970?

  • Thanks, that was very cool information.

  • Yeah, because that was the creation of the UNIX operating system,

  • UNIX Epoch time, I believe.

  • I believe that's the reason.

  • Number of seconds-- oh, maybe not.

  • Maybe that's not the reason that it is chosen.

  • I'm going to look at the history section and I think probably

  • determine what the purpose is.

  • Maybe?

  • I'm not sure.

  • It could have been something arbitrary.

  • I had thought I had heard it was because that was when UNIX was created,

  • but the fact that it's on January 1 kind of

  • makes me feel like they just chose an arbitrary year and a starting point.

  • So that's probably what it is.

  • But yeah.

  • No, it's an interesting bit of information.

  • Yeah, no need to apologize, Asley.

  • I think it's cool.

  • I think certainly other people will find the information useful as well.

  • But yeah.

  • We have a dictionary loaded in memory.

  • We have a random word that we're choosing.

  • And we are properly using a variable to encapsulate not only the word, but also

  • the substring of that word.

  • So everything is kind of falling into place.

  • Now the score shouldn't be 24.

  • It shouldn't be starting at 24.

  • We should have a separate variable to keep track of that.

  • So we'll keep a variable called score, set it to 0.

  • And then down here, where we have the score rendered,

  • we're going to change the 24 to toString score.

  • And then now, indeed, we get 0 being rendered at the top right.

  • In case you can't see it, there it is.

  • So everything is great.

  • Now what we need to do is we need to actually type

  • the word that we're trying to type.

  • The whole gameplay loop, so to speak.

  • So why don't we start figuring that out?

  • This function should probably be in a--

  • so what we should probably do, is we should

  • say local fullString, local halfString, and bring this into another function.

  • Because we're going to need to call this later, when we end up

  • resetting the word.

  • So I'm going to create a new function at the very bottom called chooseWord.

  • I'm going to get rid of the--

  • oops.

  • I'm going to get rid of the local bit here.

  • And now fullString and halfString are going to be set to a new value.

  • But we can call this-- we can trigger that whenever we want to,

  • rather than triggering it just one time at the start of the application,

  • having to copy and paste that code somewhere else,

  • we can make a function for it, make it a little bit easier to call.

  • I'm going to put this into a function as well.

  • I'm going to say local words, and then I'm going to--

  • actually, I can initialize that to an empty table.

  • I'm going to copy this.

  • And I'm going to say, initializeDictionary.

  • And I also need to call chooseWord.

  • And then down here, I'm going to also say initializeDictionary.

  • Copy that for loop that we did earlier.

  • Let's run it, make sure everything works, which it does.

  • So everything's a little bit cleaner.

  • You don't want too much bulk up above where your game actually

  • begins, not too much logic, just because the top of the program

  • should ideally just be what's called a data table.

  • So basically, just a definition of variables.

  • Just to keep everything kind of easy to reference

  • and consistent with other paradigms.

  • Generally, I like to only allocate the top portion of the program

  • for variables and library loading.

  • And library loading should often be, to Bhavik's point earlier,

  • relegated to a separate module in the event

  • that we have a lot of libraries and resources that need to be loaded.

  • But in this case, we're not going to over engineer it too much.

  • We're just going to kind of put everything in the same file for right

  • now, and know that if we were to get more complicated,

  • we have that as a fallback to keep everything cleaner.

  • So cool.

  • Everything is still less than 100 lines of code.

  • We're only at 77 lines.

  • I'm actually going to commit this right now.

  • So git add, git status.

  • Make sure everything is good.

  • Make sure that everything is loaded.

  • OK.

  • Cool.

  • Get commit basic UI foundation and dictionary.

  • I'm going to push everything up to GitHub.

  • So once I do that, and I go back to that repo, and I refresh it,

  • you'll see, once it refreshes, the current setup-- oh, large is not here,

  • actually.

  • We are going to need that.

  • Why is large not in there?

  • Does large-- does get not by default include unsuffixed files?

  • Because that would be interesting.

  • Oh, to Bhavik's point, it will overflow in 2038, UNIX Epoch time.

  • That's interesting, actually.

  • I didn't think about that.

  • But yeah, that's a good point.

  • Hopefully by then, all computers are 64-bit, and not--

  • yeah, that won't be as much of an issue.

  • But that'll be basically like Y2K all over again.

  • And yeah, as they mentioned, because it's

  • when the time started for UNIX computers,

  • and the time stamp is marked at 0.

  • Damnerel, thanks for joining us.

  • OK, so the dictionary is not being included, I don't think,

  • which is kind of a problem.

  • github include files without suffix.

  • Oh, that's adding them to the get ignore file.

  • But that's not what we want.

  • We want the opposite.

  • This is not relevant to what we were talking about.

  • How to track files with no extensions.

  • Here we go.

  • This is for get lfs track.

  • If anybody knows offhand how to add files without extensions in GitHub,

  • just let me know.

  • I've actually never run into this issue before.

  • I mean, yeah.

  • What I could do is just add .txt to it.

  • So I think that's what I'll do.

  • I'm just going to add .txt.

  • Worry about it later.

  • Add, go back into our code.

  • Just say-- where is it?

  • initializeDictionary, large.txt.

  • And now let's make sure it works first, which it does.

  • Awesome.

  • Go in here.

  • If I could do this now--

  • and then I say added large.txt, git push.

  • Will this now work?

  • Hopefully.

  • Uh, that didn't work either.

  • Wait, what the hell's going on?

  • OK.

  • Modified.

  • Why are these files not being tracked?

  • Fonts in large.

  • Could have sworn git add added--

  • oh, wait.

  • Am I in the wrong place?

  • Oh.

  • That's why.

  • I was in the wrong folder.

  • That's my bad.

  • OK.

  • Sorry.

  • User error.

  • User error.

  • Git commit added files.

  • Wow, I feel quite silly.

  • I was in the lib folder trying to add stuff that wasn't in there.

  • So now everything is working.

  • I'm actually going to toucha.gitignore.

  • I'm going to go into here, and in my gitignore, I'm going to do this.

  • OK.

  • Git status.

  • Git commit.

  • Remove ds store.

  • Oh, that didn't work.

  • OK, I think I have to get rm.ds store.

  • Cool.

  • So now we have a gitignore.

  • So we're getting rid of the ds store which is cool.

  • And I think the large thing was probably not even an issue.

  • I think I just thought it was because I was being silly.

  • But we should be good.

  • Damnerel, how long have we been streaming?

  • We've been streaming for about an hour and 27 minutes,

  • if you include the music times.

  • So about an hour and 15 minutes.

  • Adamantine-- hey, Colton, and anybody, can someone

  • explain what game we're working on?

  • I got here late.

  • Sure, absolutely.

  • Let's go ahead and take a look real quick.

  • The-- let me get rid of this.

  • So this is a typing game.

  • So what this is going to do, as you can see on the top left, there's a timer.

  • So it starts at 60, goes down to 0.

  • And the goal is to type any word that you see here in yellow,

  • and it'll be shown in white down below the word.

  • And if you type the letters correctly, the word will keep going.

  • And if you get it wrong, it'll revert back

  • to the beginning, where you aren't typing any letters at all.

  • At the very top right, which you can't really see from the chat, is a score.

  • The score is going to be relative to the number of characters

  • that you typed for each word.

  • So for example, the word sharpens has one, two, three, four, five, six,

  • seven, eight characters.

  • So by typing sharpens correctly, you get eight points.

  • Therefore, it's balanced if you and someone else

  • play and get differing lengths of words.

  • And at the end of the 60 seconds, you will be told what your score is.

  • We're also loading a dictionary at the start of the game, CS50's own speller

  • large dictionary, so that we have a more robust selection of words

  • than what we had last time when we basically hard

  • defined a limited dictionary of words for Hangman.

  • So yeah, that's the gist of what we're doing.

  • Didn't know that, Damnerel.

  • I'm not sure what that's a reference to.

  • That's why I customized my prompt to show the full path before one

  • line above it.

  • Yeah, no.

  • Normally mine does.

  • I think for when I taught the GD50 class,

  • we wanted the prompt to only have the dollar sign.

  • And I never customized it to not do that.

  • So that's why.

  • And I can see why now that that's very valuable to have.

  • So silly user error.

  • My mistake.

  • I goofed.

  • But we're good.

  • We made it.

  • We figured it out.

  • Let us get to the actual input side of the game, the actual game part of it.

  • So what I'm going to do is halfString does not need

  • to be initialized to anything, right?

  • Let's see if that works.

  • Yep, it does.

  • When we first start typing, there is going to be no halfString.

  • It's going to be only an empty string.

  • And once we start typing characters, it will start forming the halfString.

  • And so what we need to do is actually look

  • into our love.keypressed and start kind of doing

  • what we did last time actually.

  • In a loop, we're going to kind of check to see

  • if we've typed in any characters, A through Z, at the keyboard.

  • Yeah, Marek has the PS1 export line there

  • in the chat, which is how you basically change your terminal to reflect,

  • as it says, PWD, which is the Present Working Directory.

  • It has that in there, in the chat.

  • Ddaddio, which is actually my dad, is in the chat.

  • So shout outs to my dad.

  • Says hello boys and girls.

  • Good to have you, dad.

  • Good to have you in the chat.

  • We are making a typing game, in case you haven't been following along.

  • So the goal is at the end of 60 seconds, it

  • will test to see how many of these words you can type.

  • We have 143,000 words loaded in a dictionary,

  • and the currently active word is in yellow.

  • And the next part that we're implementing now

  • is going to be actually showing the half word below that word.

  • So the half word means the word up to--

  • basically, however many characters we have

  • typed that are within that first word correctly.

  • So for example, if I were to type S-H-A correctly,

  • that would be the current half word.

  • So basically just a substring of the current string that's active.

  • So what we did in Hangman on Monday, was we

  • did a for loop that basically iterated over the entire alphabet.

  • And we basically checked to see in every key, for every letter of the alphabet,

  • did the user press that key on this frame?

  • So for-- and we defined the alphabet to be a string up here as a constant.

  • So I can do that.

  • I can say alphabet is equal to abcdefghijklmnopqrstuvwxyz.

  • So that's a string.

  • That's just literally the alphabet in lowercase.

  • And I can say for.

  • And it was a--

  • I'm going to have to remember offhand what it was.

  • We're going to get every single letter of the alphabet.

  • So I basically say for 1 until the length of alphabet, so from 1 to 26,

  • I want to do local char is going to be equal to-- char, short for character--

  • char is going to be equal to alphabet sub, so substring.

  • Get a substring off of the alphabet string.

  • So just means we can choose any arbitrary starting and end index.

  • So for example, 1, 1, so from the first character to the first character,

  • that would be A. From the second character

  • to the second character, that would be B.

  • From the first character to the third character, that would be ABC.

  • So we can kind of do something like that and get

  • every single individual character by just saying--

  • sorry, for i is equal to 1, we can say i, i.

  • So local character is going to be equal to the substring of i, i on alphabet,

  • so from 1 to 1, from 2 to 2, from 3 to 3, from 4 to 4,

  • because we're doing from 1 to 26 here.

  • And then I can say if key is equal to char, which is just

  • basically a way of us getting to check to see if they've

  • typed in every single letter of the alphabet, so from A to Z,

  • this is where we need to determine whether it's the currently sought

  • after character in the word.

  • So for example, s is going to be the character that the computer is looking

  • for us to type, because that's the next character in the word,

  • up to where we are right now.

  • We haven't typed in any characters at all.

  • So it's saying, OK, your current progress is zero effectively.

  • In order to make progress, you need to type s, in order for us to continue,

  • to get to h, and then to get to a, and then to get to r.

  • So we're basically waiting for s.

  • So we need a way to check to see what the currently active character is.

  • We need basically a way for us to see whether it is indeed s.

  • And I've noticed that it's sharpens every single time because this

  • needs to actually go up here.

  • Seeding a random number generator needs to take place

  • before you call any random functions.

  • And chooseWord calls math.random.

  • So I was making the mistake of seeding the number

  • generator after calling math.random, which

  • defeats the entire purpose of that.

  • So now reprisals, overtask, snapshooter.

  • So now it's actually working appropriately.

  • Everybody is giving my dad a nice shout out.

  • Off topic, where do you find these big text files,

  • like the speller dictionary or the US states info?

  • I mean for that, honestly, I don't know offhand.

  • I would say Google it probably.

  • The speller dictionary I have to imagine David or someone else at TF one

  • year did some googling or some research, or maybe even manually did it,

  • or used an API to dump it.

  • But a lot of the time, you can just say US info data

  • text file in Google or something, or CSV,

  • and then use a script to convert that CSV into a text file.

  • Basically massage the data to do whatever you need

  • to do to get it in the right format.

  • We can make a function to check each key press in a given randomized word.

  • Yes, that's effectively-- well, that's effectively what we're doing, yeah.

  • We've allocated it to the keypress function.

  • GDE1984, snapshooter isn't a word, or better not be.

  • It sounds ridiculous.

  • I don't know.

  • Let's find out.

  • Dictionary, snapshooter.

  • I guess snapshot is a word, and I guess a snapshooter

  • is somebody that takes snapshots.

  • And you can also go to Merriam Webster when in doubt

  • and see whether a word is correct or not.

  • And again, I don't know where this word was taken from, snapshooter.

  • Or sorry, I don't know where the dictionary was taken from.

  • The text file that we have, I don't know whether it

  • was dumped from Merriam Webster or from some other API.

  • Different dictionaries will have different sort

  • of expectations or different guidelines and criteria for what's

  • correct when it comes to words.

  • But I'll say snapshooter.

  • And it looks like it is indeed correct, according to Merriam Webster, which

  • is a fair safe enough source of truth.

  • And as I predicted, which was a complete guess,

  • it is a person who takes snapshots.

  • So if you've learned anything from today's stream,

  • snapshooter is a person that takes snapshots.

  • Very useful information.

  • And a very ridiculous word, to GDE's comment.

  • Bhavik Knight, there are many large password cracking dictionaries as well

  • for brute force.

  • Oh, interesting.

  • Sounds like Bhavik Knight has been up to some hacking.

  • Hopefully white hat hacking.

  • Andre who's in the chat.

  • Hello, Andre.

  • Thanks so much for joining us today.

  • Good to have you.

  • We are pretty far along.

  • We have a typing game sort of in the works here.

  • And the next step is actually getting input to work.

  • So why don't we figure out what the--

  • like I said earlier, how to ascertain the currently sought after character.

  • So basically, remember, every string is essentially a sequence of characters.

  • And we can index into a string based on a number.

  • So index 1 in this string is going to be r.

  • Index 2 is going to be o.

  • Index 3 is going to be m.

  • So we need to maintain a reference to that index.

  • So local current char index is going to be equal to 1.

  • Right?

  • And this means that this is going to be the character we

  • need to look at to decide whether our key press is

  • the same as that character, right?

  • So if key is equal to that character in this loop--

  • so if the key that the user has pressed to trigger this callback function

  • is equal to the character that we've grabbed from the alphabet,

  • and then we can say and the char--

  • or rather, well, I don't want to do and here.

  • I want a couple of things to happen.

  • There's two things we want to happen if they press a letter.

  • If it's correct, we want to basically make halfString one character larger.

  • We want to append a character to halfString

  • so that, for example, if they've typed y here, halfString no longer is empty.

  • It becomes the string y.

  • And then if they type o, it becomes the string yo, and if they type h,

  • it becomes the string yo-h.

  • If they type an incorrect character-- so there's two chains, two flows here.

  • If they type an incorrect character, we want the whole halfString

  • to essentially become nothing, right?

  • We want it to revert back to the beginning,

  • because they've gotten it wrong.

  • Right?

  • And in addition, we want to be able to say,

  • if they have reached the end of the string and gotten it correct--

  • if they've typed in, for example, all the way to the second s in blastulas

  • here--

  • again, a word that I don't know.

  • Sounds like a medical word.

  • But if they type the second s, they have completed that word.

  • It should also revert down to-- the halfString should also become nothing,

  • then the word should change to a new word,

  • and the score should be added to--

  • the size of this word should be added to the score, effectively.

  • So we should get one, two, three, four, five, six, seven, eight,

  • nine points in the event that that is the case.

  • So there's a few things that we want to have happen all within this block

  • here, this love.keypressed area.

  • JMC, does Lua have zero indexing or one?

  • It's one indexing.

  • It's a little bit rough, if you're coming from other languages.

  • Yikes is correct.

  • But you do get used to it.

  • You do get used to it.

  • A source of many bugs for me when I was using MATLAB, says Andre.

  • Yes.

  • Really should be standardized.

  • Those one off errors are so dumb.

  • Yeah, no, I mean, I think a reason is probably

  • because it caters to people that are not technically programming language

  • savvy necessarily at LOVE, at least when it was originally engineered.

  • I have to imagine that's the reason why it's one indexed,

  • and a lot of these tools that are more user friendly are one indexed.

  • Humans generally are not used to thinking

  • from a zero indexed frame of mind.

  • But when you're used to programming, it's certainly the opposite.

  • You are used to thinking from zero index.

  • So they can sort of be a double edged sword,

  • depending on what background you have.

  • But you get used to it.

  • Especially if you program back and forth between languages,

  • you sort of kind of get into the habit of constantly

  • keeping track of what the indexing scheme is that you're using.

  • OK.

  • So if the key is equal to the char, if we

  • have pressed this key of the alphabet, if we

  • have typed the current correct letter, else

  • if we typed the wrong letter, right?

  • So just putting some comments in here to flesh out what I want to do.

  • So if the--

  • I guess we can use char.

  • So if char is equal to.

  • And so again, we're going to have a current char index--

  • or char index-- depending on how you want to pronounce it.

  • So basically, whatever-- think of having an invisible pointer that's

  • going across each letter as we successfully type it.

  • The first one is going to be p here.

  • So it's always going to start at one.

  • So it's going to be p, then r, then o, then c, as we type letters correctly.

  • And current char index needs to be incremented as a result of that.

  • We'll see that, if we type a correct letter.

  • But if the char is going to be equal to-- if it's the case that char is

  • equal to word--

  • and I believe it's word.

  • That's the-- sorry, nope, fullString.

  • fullString sub-- so remember, we need to use sub here--

  • currentCharIndex, currentCharIndex.

  • So this is how we get the character at a specific number,

  • is you use the sub function-- in this case, currentCharIndex,

  • currentCharIndex, both of those being the same

  • will give us the one letter at that index.

  • So if it's one, it's going to give us the substring from one to one, which

  • is one character.

  • Then just fleshing this syntax out here a little bit.

  • So if we did type the correct letter, first of all, let's go ahead

  • and if current char index is equal to the length of the full string--

  • so if it's the case that--

  • this is basically our win condition, right?

  • Or successfully typed full word condition, right?

  • If our current char index is the same length as the string itself,

  • that means we've typed the last character.

  • So we can effectively say currentCharIndex is equal to 1.

  • The word itself, so fullString, is going to be equal to words math.random words.

  • And halfString should be equal to an empty string.

  • And again, remember, we did this over here in chooseWord.

  • We made a function that does the same exact things.

  • So I can just say chooseWord here instead, right?

  • Save me a little bit of typing, make things a little bit cleaner.

  • currentCharIndex is equal to 1.

  • I mean, we can put this in here too.

  • Right?

  • Because this is always going to do this, every time we want to do that.

  • Just put as much of that into the function

  • as you can so that you only have to call the function.

  • You don't have to call a bunch of code, right?

  • You can minimize the length of your program.

  • And that's ultimately what should be one of your big goals, conserving,

  • of course, clarity of meaning.

  • You don't want to make your program so short and so arcane

  • that it's hard to understand.

  • But if you can get rid of as much clutter as possible, definitely a plus.

  • Smaller programs are usually easier to debug.

  • So if the currentCharIndex is equal to the length of the fullString,

  • choose another word.

  • This is going to revert halfString to nothing.

  • It's going to change the current word to a brand new word.

  • And another important thing that we need to do, of course,

  • is add the length of the word to our score, which is super easy.

  • We can just say score equals score plus the length of the fullString, right?

  • Now we're doing this before we choose the word, because if we

  • choose the word, then this is going to be a completely different length.

  • So we add the score, and then we choose a new word.

  • And that's how we effectively get to the next word

  • right away when we've fully typed out the current word.

  • And that's if the currentCharIndex is equal to the fullString,

  • the fullString length, and only in that situation.

  • But if it's not equal to the fullString length,

  • if we're not at the end of the word, then what we need to do

  • is we just need to increment the currentCharIndex.

  • So we say currentCharIndex equals currentCharIndex plus 1.

  • And so what that does is it just shifts our invisible pointer over

  • to the next character, right?

  • And so if our logic is correct, in as far as rendering

  • the fullString and the halfString, then it'll render it appropriately,

  • but just by incrementing the counter.

  • So currently it's not doing that.

  • The halfString is just actually being rendered as is.

  • So what we can do is instead render just the--

  • or rather, is it the case that the halfString--

  • there might be a more clean way to be doing it

  • than what we're doing, actually.

  • We might not even need to keep track of the halfString.

  • We can just render the fullString subbing from--

  • well, first of all, Pipalone, thank you very much for following.

  • We can do something here.

  • We can say local halfString is equal to currentCharIndex

  • is equal to 1 and nothing, or fullString sub from one to currentCharIndex.

  • Right?

  • Minus 1.

  • Yes.

  • And then we can print halfString here.

  • And as a result of that, we actually don't need--

  • is this correct?

  • Is this right?

  • We don't need to keep track of this halfString here.

  • And I think that's correct.

  • Let's make sure that that's right.

  • Basically, what we're doing, instead of storing a halfString,

  • we're just calculating it before we render it every time.

  • So we're basically saying if the currentCharIndex

  • is one, which means that the current character we're trying to type

  • is the first character of the string, then it should just be an empty string.

  • But otherwise, we should just take a fullString,

  • or take a substring of the fullString, starting at 1

  • and ending at the currentCharIndex minus 1.

  • Which means that if it's 2, this will give us 1, 1, right?

  • So this should actually work quite well in theory.

  • We'll see if my intuition is correct.

  • I believe it is.

  • If we typed the wrong letter, then here is where all we have to do

  • is just say currentCharIndex is equal to 1.

  • Let's see if this works.

  • heelb, and it goes back to nothing there.

  • OK, cool.

  • So everything seems to be working.

  • I'm going to have my score visible, just so we can see what's going on.

  • Let's try that again.

  • heeltap.

  • Ah, so it does work.

  • So we have 7 up at the top.

  • Our score is 7, because heeltap was seven characters.

  • It changed the word to fortuity, right?

  • Let's try that.

  • F-O-R-T-U-I-T-Y. Awesome.

  • S-C-A-N-D-A-- I'm going to purposefully mess up--

  • V. OK.

  • Cool.

  • So it went back to nothing.

  • And the timer is going down.

  • So I would say that everything seems to be working pretty well.

  • We're getting the right score.

  • It's going back to zero if we mess up.

  • Everything is aligned perfectly.

  • The current word and the half word, or the fullString and the halfString

  • are both rendering completely lined up.

  • And we're calculating the halfString dynamically.

  • Now, there's a slight bug.

  • So first of all, our timer is now in the negatives.

  • We're going to negative 10, negative 11 seconds.

  • That's not very useful.

  • We don't have a start screen to our game, and we don't have an end screen.

  • And the end screen will also fix the bug with the timer

  • going into the negative numbers.

  • But everything is working just fantastically at the moment.

  • I'm going to commit this code actually.

  • Let me see.

  • Get status.

  • I didn't add anything new, did I?

  • Nope.

  • So words and score and halfString rendering working.

  • OK.

  • So now on GitHub, if you pull the code, everything

  • is working perfectly fine for now.

  • We have a few pieces we need to fix and we need to do.

  • But everything's great.

  • Some people are going to bang their keyboard into the wall

  • if that pnemono 45-letter word shows up.

  • Hey, but 45 points, that's a lot of points.

  • The game is sick, says Adamantinebipartine.

  • Thank you.

  • I appreciate that.

  • It's looking pretty good.

  • And the code is actually looking pretty clean.

  • Trying to add comments a little bit more proactively this time.

  • But yeah.

  • And it's only 105 lines, which is very small for sort of the features

  • that we have so far.

  • And very elegantly written out so far.

  • And I mean, 105 lines with a lot of comments.

  • So it's actually pretty condensed for what it is.

  • Now, let's go ahead and implement kind of like a start and an end state

  • to the game.

  • And that will kind of finish it up.

  • So I guess today--

  • I always sort of give myself a three hour buffer,

  • but we did everything within less than two hours so far.

  • So it's going to be a shorter stream I think, but that should be all right.

  • For folks that are especially trying to get through the entire game

  • quickly and follow along, that might be a good thing.

  • But certainly I'll stick around for questions,

  • and then we still have these two states to finish up.

  • So we'll do that.

  • So first step, let's do a start date.

  • I'm going to go ahead and say local start is equal to true.

  • So I'm just creating a variable called start is equal to true.

  • And if start-- oh sorry, if not start, then--

  • whoops.

  • And really, this should be if not start and not game over.

  • I'm going to create another variable called gameOver.

  • This is going to be false.

  • And here in love.draw, I'm going to do what we did yesterday,

  • or rather two days ago, and kind of create like a very simple panel

  • so that we can have a basic press Space to start the game.

  • Oh, and a blinking cursor.

  • That's a good suggestion, actually.

  • I kind of like that.

  • I kind of like that idea.

  • Yeah, I'm going to do that.

  • I'm going to do that.

  • As soon as we do the start thing, we're going to implement a blinking cursor.

  • That's a really cool suggestion.

  • So let's go ahead.

  • I'm going to first of all draw.

  • So this is in the draw function.

  • When the game is just starting, I want to draw a box that

  • says Press Space to start the game.

  • So I'm going to say draw a starting panel.

  • If start, then love.graphics.rectangle.

  • Oh, and another important thing we're going to need to do in a second

  • is make sure timer.update isn't updating during the start and game over.

  • We need to reset the timer as well when we restart the game.

  • So we need to look at that.

  • OK, so love.graphics.rectangle.

  • This is going to be starting at 128, 128.

  • So about 120 pixels from the top left.

  • And it's going to be the size of window width minus 256

  • and window height minus 256.

  • This is so that it is 128 pixels from all sides.

  • Because we shifted over 128 pixels, we need

  • to take into consideration 128 from the bottom right as well to center it.

  • So that's why we're minusing 256 and not 128 again.

  • So again, xy width and height.

  • This also needs to be a fill rect.

  • And I need to set the color before we do that to some light

  • gray, so 0.5, 0.5, 0.5, 1.

  • And love.graphics.setColor to 1, 1, 1, 1.

  • And that's full RGBA, which is white.

  • love.graphics.printf.

  • Press Space to start.

  • This is going to be relative to zero, virtual height minus--

  • sorry, not virtual height, window height divided by 2 minus 32.

  • This is going to be window with fill and center.

  • And if I'm not mistaken, that looks pretty good.

  • If I press Start, I'm not checking for space in here.

  • So if start and key is equal to space, then start is equal to false.

  • Cool.

  • That works.

  • Then we get beatless.

  • And then I can keep typing.

  • Messed that up.

  • Objectifying.

  • Cool.

  • So that works great.

  • And then my dad suggested a blinking cursor.

  • So I'd like to do that as well.

  • One other thing I would like to make sure

  • though is that the timer is not updating when it's in start.

  • So if not start and not game over, then timer.update.

  • And now we should see the timer at the very far left.

  • It's not actually actively updating until I press Space.

  • And then now it's going to start updating because we are not in start,

  • and we're not in a game over state.

  • So a blinking cursor.

  • That's a cool idea.

  • Let's go ahead and--

  • how is that going to work?

  • What's the best way for that to work?

  • So we're going to need-- basically, we can do it as an underscore.

  • We can implement it as an underscore.

  • halfString-- OK.

  • So I have an idea for how I want to do this.

  • I'm going to create a variable called underscore.

  • And you could use a pipe character to do an actual cursor too.

  • I'm going to do it as an underscore that blinks, just

  • to kind of keep things kind of more like you would

  • see in a lot of other typing games.

  • I believe underscore is a little bit more common than I guess cursor.

  • I mean, I guess a pipe is good too.

  • They're both fine.

  • We'll do a pipe character.

  • So we'll say local cursor, and we'll set that to false for now.

  • And then timer.every 0.5.

  • I'm going to create an anonymous function.

  • So this is the timer class that we used earlier, which every one

  • second will decrement our timer.

  • We can use this every five seconds to toggle cursor.

  • So I can say cursor is equal to not cursor.

  • And what that does is, if it's false, cursor becomes true, and if it's true,

  • cursor becomes false.

  • So it just toggles the state of cursor between true and false,

  • between 0 and 1, basically.

  • And then in my--

  • where I basically generate the halfString,

  • the halfString is going to get added to it

  • the cursor, the pipe character, at the index

  • where we are typing the next letter.

  • So I can say halfString-- so I can say if cursor, then.

  • Space this out a little bit.

  • So add cursor to the halfString text based on cursor state.

  • I can say halfString equals halfString dot dot.

  • Remember, dot dot is what you use to add strings to each other.

  • And then the pipe character.

  • Now hopefully this font has a pipe character.

  • If not, it will be a little bit--

  • it won't render appropriately.

  • So you might have to use the underscore after all or get a different font.

  • But hopefully this works.

  • Let's try it.

  • That's so cool.

  • Yeah, that works.

  • I'm a big fan.

  • Yep.

  • It's a little bit wacky right there though.

  • Yeah, that's the only problem, is that because the cursor is--

  • it kind of screws up our padding.

  • It screws up the padding of the string a little bit.

  • So a better way to do this would actually

  • be to treat this as a separate string.

  • An underscore that leads.

  • Oh, I see what you mean.

  • Yeah, that might work.

  • Like that?

  • It still causes issues because it's dynamically

  • figuring out the size of the font, the size of the string.

  • And the underscore is thicker than the i in this case.

  • So rather than it mapping up evenly perfectly, it's padding it too much.

  • And so it's shifting it off.

  • So what we need to do is we would need to treat

  • the underscore as a separate string and draw that after the r, which

  • is totally possible, totally doable.

  • Actually not that hard to do.

  • So what we can do is, I'm just going to take this down here and say

  • if cursor, then love.graphics.print.

  • And then I'm going to print an underscore.

  • And I'm going to print it at-- let's see.

  • Where would this be?

  • How would we figure this out?

  • We would say-- could we get the position of the full word

  • and just use the x-coordinate of that for the x of the half word?

  • Ostensibly.

  • I'm not 100% sure whether that function returns a position or not.

  • So this would be like love.graphics.printf.

  • Yeah, it doesn't return anything.

  • And so what we would need to do, you get the center of the screen,

  • because it's not always centered.

  • How would we do this?

  • This would be very easy if everything were left aligned,

  • but because everything is center aligned, it's a little bit more

  • complicated.

  • Little bit of a brain teaser.

  • love.graphics.print underscore at-- well,

  • we can mess around with it a little bit and see if we might deduce something

  • from just tinkering.

  • So it's going to be a printf.

  • It's not going to be a print, most likely.

  • Yeah, because being centered shifted left.

  • Being center shifted left.

  • This would be love.graphics.printf with the window width.

  • OK.

  • So starting at zero virtual--

  • not virtual.

  • So used to that.

  • And that's such a bad habit of that.

  • WINDOW_WIDTH divided by 2 plus 16.

  • And this is going to be relevant to the WINDOW_WIDTH minus--

  • let's put this on another line--

  • minus-- and then we'll end center--

  • minus-- would it be fullString minus the--

  • current character minus 1, the substring of that?

  • So it would be fullString, or the minus of that.

  • Irene is in the chat.

  • Hello, Irene.

  • Good to see you.

  • After every word typed, cursor increments should works.

  • Yeah, no, that's what we're doing.

  • But the problem is that the padding is dynamic,

  • and it's relative to the size of the glyphs of the font.

  • And so if we're, for example, using an underscore

  • below where we're using an i, the i has less padding than the underscore,

  • and therefore the underscore is shifting the half word more

  • than the underscore would be.

  • Or the underscore is shifting it more than the i would be doing so.

  • And for all other characters that are, for example,

  • thinner or thicker than the underscore, it would do the same thing.

  • Bhavik Knight says, full minus the half string length plus one.

  • I think that that is correct.

  • So WINDOW_WIDTH minus the fullString length.

  • Would that be correct?

  • Is that correct?

  • fullString length plus-- no, I think it's--

  • then we have to take the length of the fullString minus the halfString, which

  • is effectively what you're doing.

  • The plus 1 isn't helpful here.

  • Greenie says LOVE 2D.

  • Yes.

  • Oh, I remember you.

  • Greenie996.

  • OK, so the full width minus the halfString width.

  • And we have the halfString width there actually, right?

  • I think that's the case, right?

  • So WINDOW_WIDTH minus the fullString length plus the halfString length.

  • Am I not mistaken?

  • I guess I am mistaken.

  • If cursor then-- wait, did I screw that up?

  • Wait, what?

  • Oh, WINDOW_HEIGHT divided by 2.

  • Sorry.

  • Oh no.

  • That is not what we want.

  • That is right in the middle.

  • OK.

  • No, it's not quite there yet.

  • But at least it's static.

  • OK.

  • Let's see.

  • Minus the fullString length.

  • Oh, length, not the--

  • wrong function, wrong function.

  • Font, getLength fullString, font, getLength, halfString.

  • Whoops.

  • getWidth, not length.

  • It's close.

  • It's not quite there yet.

  • It looks like very, very poorly written software at this point.

  • Would an if-then else block work wrapping the whole command on line 101?

  • So Irene, we're not trying to add underscore to the string

  • that we're rendering because that screws up the padding.

  • We're trying to draw the underscore as a separate function altogether

  • so that it doesn't influence the padding of the halfString effectively.

  • So no, we need them to be separate.

  • Would it be easier to just manually position

  • the words at WINDOW_WIDTH divided by 2 minus font length fullString divided

  • by 2?

  • Wouldn't it be easier to just manually position

  • the words at WINDOW_WIDTH divided by 2 minus font length fullString divided

  • by 2?

  • Yeah, that would work.

  • That's a simple way to do it.

  • Why don't we do that?

  • Sounds good, Andre.

  • OK.

  • Position the words at WINDOW_WIDTH divided by 2, font length, font--

  • it would be getWidth fullString divided by 2.

  • Yeah, we can do that.

  • Let's try that actually.

  • That's pretty cool.

  • And then therefore, we have the--

  • well, does that fix the cursor issue?

  • I guess it does.

  • Yeah.

  • Yeah, I guess it does.

  • Because we just add halfString to that value.

  • And then that's where we draw the underscore.

  • So yeah.

  • So that would be drawn at WINDOW_WIDTH divided

  • by 2 minus font getWidth fullString divided by 2, see if that works.

  • Oh no.

  • And it can't-- it has to be print, and we have to get rid of center.

  • Right?

  • Oh.

  • And then the last thing after that--

  • so first of all, that is definitely not correct.

  • That is not behavior that we want, that we're looking for.

  • I would say we messed up pretty bad to get into that kind of position.

  • But the reason it's doing that is because if you call print, just print

  • and not printf, this last parameter is a rotation value.

  • And we're passing it WINDOW_WIDTH, which is a really high rotation value.

  • But no, we want it to actually be this.

  • So awesome.

  • But that looks so cool, says Irene.

  • Yeah.

  • A real challenge would be making it rotate over time, like an expert mode.

  • And then you're challenged to sort of still type the word while keeping track

  • of what it is while it's rotating.

  • Greenie says, Lua, why?

  • Because Lua is a pretty easy language, pretty nice, pretty common in the games

  • industry.

  • But also more importantly, because we're using LOVE 2D, which is this framework.

  • It's a really awesome 2D game framework.

  • And it happens to use Lua as its programming language.

  • But certainly, you can game program in almost any language in the world,

  • whatever suits your fancy.

  • But a lot of the time you're sort of bound to whatever the best tools are.

  • And LOVE 2D is a tool that tends to be pretty nice.

  • OK, we're doing the same thing here, WINDOW_WIDTH

  • divided by 2 minus font getWidth halfString divided by 2.

  • Oh, but it can't be halfString divided by 2 in this case, because it's--

  • we again have to minus this font getWidth thing here, which is that.

  • halfString x, going to be equal to WINDOW_WIDTH divided by 2 minus--

  • I think that would actually work.

  • So then I can say halfString, and then this

  • will be halfString x, halfString x, do that, bring all this up here.

  • Does that work?

  • Nope, that does not work.

  • We're having a great time, messing with all kinds of stuff.

  • This is the beauty of small features that

  • are awesome have huge ramifications sometimes.

  • And sometimes they have no ramifications.

  • Sometimes getting a simple feature is really easy and awesome.

  • But there's tremendous value in exploring something like this

  • to illustrate why sometimes bug fixes are not

  • so easy, why it takes a long time to release new features for stuff.

  • Let me see.

  • Oh wait, no.

  • It's always going to draw-- no, it's always going to draw it

  • at the same-- yeah, what am I thinking?

  • OK.

  • No, it just needs to be--

  • yeah, the same thing as--

  • it just needs to be drawn at the fullString position,

  • and it'll be the exact same.

  • Yeah.

  • I'm silly.

  • I over thought this.

  • We can say WINDOW_WIDTH divided by 2 minus font getWidth fullString.

  • And then I can get rid of this monstrous line of code.

  • And then now still not correctly rendering.

  • Wait, hold on.

  • Oh.

  • Wait.

  • Divided by 2.

  • Right.

  • There we go.

  • Perfect.

  • So now all I need to do is--

  • so we have everything working the way it was before.

  • We have the autocatalysis.

  • We have the two strings.

  • They're being centered.

  • But we're doing it manually.

  • So now we have fine grained control over where the positions are.

  • We know what they are definitively.

  • So I can say if cursor--

  • back to the cursor part--

  • draw it at the--

  • and not WINDOW_WIDTH minus all this stuff.

  • Instead, I'm going to draw it at WINDOW_WIDTH

  • divided by 2 minus font getWidth of fullString divided by 2 plus--

  • and then this is where we're going to add the length of halfString.

  • So I can say font getWidth of halfString.

  • Now, if I'm not mistaken--

  • whoa, OK.

  • That is not what I was looking for.

  • OK, hold on.

  • Why-- oh, because it's being it's being centered.

  • Right.

  • Print, get rid of center.

  • Not zero.

  • Oh, right.

  • This needs to go here.

  • There we go.

  • We did it.

  • That was fun.

  • But now I would say that that little feature actually

  • added a tremendous amount of UX, user experience value, to our game.

  • Right?

  • So shout outs to my dad and other folks in the stream for that awesome idea.

  • We got it working together.

  • It was a little bit challenging because using the auto centering

  • didn't really give us fine grained control over things we're rendering,

  • and it was a little bit difficult to sort of programmatically figure

  • that out.

  • But thanks to Andre, Andre for his awesome suggestion.

  • We can just explicitly, since we know everything's

  • going to be perfectly center aligned, we really

  • don't need love.graphics.printf and its center functionality,

  • because we can just use the constraints of our window

  • and say WINDOW_WIDTH divided by 2 minus the length of the string divided by 2.

  • And that is effectively the same exact thing.

  • So awesome.

  • You guys are incredible.

  • It was a great time.

  • We don't have a game over and a score screen yet.

  • So let's add that as our last feature.

  • Let me just make sure I didn't also miss anything else in the chat

  • while I was coding that.

  • What about SFML, says Greenie996.

  • SFML's a great library for C++.

  • I've used it myself.

  • But C++ is a little bit tricky, especially for people just getting

  • into it.

  • It's kind of a pain to debug, honestly.

  • Because Lua is effectively wrapping a C++ engine that's open GL based,

  • it's not that much more efficient just to use LOVE 2D for 2D games.

  • SFML has a lot more utility for a lot more fancy stuff,

  • for actual engine and 3D stuff even.

  • But for 2D games, I don't ever see myself

  • wanting to go to SFML over LOVE 2D, to be honest.

  • It really depends on the use case though.

  • There's definitely a case to be made for it.

  • Is Colton emo?

  • No, Colton is not emo.

  • Here to say hello, says ForSunlight.

  • Will watch it on YouTube.

  • Cool.

  • Thanks for joining.

  • Appreciate it.

  • And there's a brain game engine.

  • That's pretty cool.

  • I didn't realize that.

  • Keeping it kid friendly, so I won't say it aloud.

  • The exposition should be the same.

  • We could have done this early, like a rectangle box divided

  • in parts equal to the fullString, and put the char

  • in the box as the user types.

  • Oh yeah, a box would be pretty cool actually.

  • I like that idea.

  • I challenge you, Bhavik, to implement that on your own.

  • You can now just append the cursor.

  • Oh true, we could append the cursor.

  • You're right.

  • You are correct.

  • Is that right?

  • Yes, it is.

  • Because we're just putting it in a definitive location.

  • Adamantine, maybe you could add a trailing space to the cursor.

  • I'm not sure if that would be of any use.

  • Now try that with the pipe, says Buddha.

  • Yeah, we could do it with the pipe too.

  • I know that certain people wanted the underscore.

  • But it's as simple as just replacing this character here, so why not?

  • We'll do it.

  • Arguably it looks better, because now the underscore isn't, for example,

  • wider than the f, which it was before.

  • So pretty cool.

  • It looks like an actual UI for a application or something.

  • It's pretty cool.

  • Not terribly difficult though, right?

  • We made it.

  • Colton, I started the game dev course.

  • Still in the first lecture assignment.

  • It's sick though.

  • Awesome.

  • Thank you so much.

  • Yeah, the Pong lecture, it starts off fairly tame,

  • but by the end of the course, you'll have done quite a bit.

  • The problem sets are purposefully pretty--

  • I wouldn't say too ambitious, but they definitely

  • give you exposure to larger code bases and doing a lot of really cool stuff.

  • [INAUDIBLE] says we fixed the bug by changing the center

  • alignment to the left alignment, right?

  • This was so hard to follow.

  • Yeah, effectively, that's what we did.

  • Not for the fullString, but for the halfString.

  • We set that to center alignment, effectively.

  • Well, what we did was we basically center aligned it.

  • We were drawing the halfString left aligned using the fullString's center

  • alignment.

  • So we determined what the fullString center alignment is.

  • We divide the window by 2, then we divide the string by 2

  • and move it to the left by that amount.

  • And then we take the halfString, and we just draw it at that same position,

  • just a little bit lower.

  • And as a result of that, the halfString isn't centered anymore.

  • But it's always relative to the fullString.

  • So it's always immediately below it, and it follows it as we type.

  • So that's effectively what we did.

  • And then we just drew the cursor right at the end of that string.

  • So yeah, all in all, it turned out to be pretty nice.

  • Let's go ahead and draw the game over panel.

  • So if gameOver, then--

  • I'm just going to copy and paste this, the start stuff, down over here.

  • And I'm also-- first of all, press Space to--

  • we'll say, let's do this.

  • Game over.

  • And then I'm going to say your score toString score.

  • And then this is going to be at plus 16.

  • And then press Space to restart.

  • This is going to be at plus 48.

  • And then up here, if start--

  • if gameOver and key is equal to Space, then gameOver is equal to false.

  • And in the timer, since the timer is where one second decreases, that's

  • where it sort of maintains its own counter, its own progress for that,

  • I'm going to say currentTime is currentTime minus 1,

  • and then if currentTime is equal to 0, then gameOver is equal to true.

  • currentTime is equal to 60.

  • Right?

  • So this should work.

  • gameOver is true.

  • Let's make sure this is working right.

  • Start and not gameOver.

  • Oh, and score-- well, score gets reset in here.

  • So score is going to be set to zero.

  • OK.

  • So let's just play and then let the--

  • inhospitableness.

  • knightliness.

  • We'll let the timer sort of run out.

  • Duotone, undreamed, gobblers, dumpty, composites, mc-- ooh.

  • We have a bug.

  • Certain words have apostrophes.

  • That's unfortunate. mcallister's.

  • OK, well that's great that that happened,

  • because we can maybe add that to our code while we wait for this to time

  • out, because I cannot type an apostrophe or have a check.

  • So I'm just going to go ahead and unfortunately wait for that.

  • We can just add apostrophe to the alphabet though, to be fair.

  • We can add, I think, any character to that, and it'll work.

  • So cool.

  • We have a score of 112.

  • Press Space to restart.

  • Press Space to restart.

  • And then mcallister's.

  • So actually what we need to do is choose word as well right there.

  • So let's go ahead and add an apostrophe to this.

  • I'm going to add a--

  • well actually, this needs to be in double quotes then.

  • I'm going to add a--

  • I don't think it's going to ask for a dot.

  • Yeah, and the text is a little bit too high up.

  • So what I'm going to do is I'm going to make this 54 and this 18.

  • And then I'm not sure if there are any other characters besides apostrophe

  • that we should take into consideration.

  • I guess we can look at the large text really quick and see.

  • So I see apostrophe.

  • Might just be apostrophe, honestly.

  • Yeah, we'll assume just apostrophe for right now.

  • Let's do that.

  • Press Space to start.

  • Sub-- and nice.

  • Now it actually kindly gave us an apostrophe word right away.

  • So I had apostrophe s, and it indeed works.

  • So now the alphabet stream, because our code was-- the way

  • that we structured our code, all we had to do

  • is add the apostrophe character to the alphabet string,

  • and it works perfectly.

  • So awesome.

  • Whereafter, Canadianization's.

  • That's a word.

  • This is a great way to be introduced to some weird words too.

  • Fictionisation with an s.

  • Tris, carrara, transfusing, crassitude.

  • That's a word I don't know.

  • How crass something is, or someone is?

  • Nonrelevant, augmenter, clavierist.

  • Shoot.

  • Nazify.

  • Oh, did I screw that up?

  • Oh, I need to lower that padding, not make it go higher up.

  • But cool.

  • So score is 202.

  • Press Space to restart.

  • Timer gets back to 60, and then we're at 0 seconds again.

  • Padding is incorrectly-- I incorrectly did that.

  • So this actually needs to be--

  • wait, did I not WINDOW_WIDTH divided by 2 plus 54 plus 18.

  • Why was the game over messed up?

  • I don't remember.

  • WINDOW_WIDTH divided by 2 plus 54.

  • That's weird.

  • 64?

  • That might be too much.

  • But we'll try 60.

  • Strange.

  • I'm going to set the timer right away to 3, just so we can test right away.

  • Two, One, boom.

  • OK, a little bit better.

  • Let's make it-- yeah, 64 was good.

  • 64.

  • Three, two, one, boom.

  • OK.

  • Good enough.

  • So this is great.

  • This is fantastic.

  • I'm going to start the score off at 60--

  • sorry, the timer off at 60.

  • I'll keep the words loaded in there, in case anybody wants to grab the code

  • and try it with their own dictionary.

  • Maybe that's something that you want to do.

  • The alphabet, the way we have it is robust

  • enough so that you could add different types of characters

  • to there if you wanted to.

  • It should be 68 I think.

  • Is your calculation correct?

  • I'm not sure.

  • If we have time, can we add that feature?

  • Ask for the player name, and add the top five high score

  • player name window at the game over?

  • That's going to be a bit long, Bhavik, and we're already

  • at the 2 and 1/2 hour mark.

  • So I think we can maybe save that for another--

  • like a tutorial for another game.

  • It's going to be a little bit of work.

  • And I haven't planned--

  • I don't think we have enough time for that.

  • But for another game, I can certainly think about adding that feature, maybe

  • on another stream.

  • And if you want to try doing it, you can certainly do that too.

  • You would need to look at the love dot--

  • I can at least give you a starting point for that.

  • It's in the love.filesystem thing.

  • So you would need to do love.filesystem dot--

  • what is it?

  • It is-- you would have to write to a file, basically.

  • And it would choose by default in your--

  • I think it would be your app data folder on Windows.

  • I don't remember your machine.

  • I think you're on Windows.

  • But it would basically be writing to a file in your app data folder

  • for this game, and you would write to a text file.

  • And then you could do the same thing that we did earlier today

  • with love.filesystem.lines.

  • You can create a new state.

  • And this is an example of probably where I would want to use the state machine

  • and create a new state class, because the high score has a bit of logic in it

  • and a bit more rendering code.

  • And it would be a bit--

  • our main.lua is arguably large enough to start piecing out at the moment.

  • But love.filesystem.lines would be your best friend here.

  • You would need to calculate whether or not

  • the score that you got on this iteration is

  • higher than the high score of a particular index.

  • So you need to iterate through all your high scores,

  • pick the one that's lower than the score that you currently have,

  • and put yours above that one, and then shift all of the other ones

  • down by one.

  • And then you would need to load this at the start of the game, your high scores

  • from that file from your app data folder.

  • And this file system folder exists in different locations

  • on different platforms.

  • Like I said, on Windows, you look, it's going to be in your app data somewhere.

  • On a Mac, it's in application support, LOVE, and then

  • the name of a folder for whatever game.

  • And then here on Linux, it looks like xdg data home love, or local share

  • love.

  • And different thing on Android as well.

  • But yeah, it's a cool exercise certainly.

  • Try it out on your own.

  • It would be fantastic if you could do it on your own.

  • I think you would gain a lot of value out of trying it out.

  • Are you writing 202 words per minute?

  • No, no, no, no.

  • 202 characters in that case.

  • I write approximately 130 words per minute,

  • but 202 would be absolutely insane.

  • But yeah, that's just the number of characters.

  • We can use a database to support that, says Bhavik.

  • Typically you wouldn't use a database for something like that, Bhavik.

  • That's a bit overkill.

  • Because honestly, all you're doing is storing some text

  • for their name and then their score.

  • Definitely don't want to reach for a database too quickly like that.

  • The only time you really use a database for game

  • is usually for like an MMO RPG, kind of like an online game.

  • In which case, your data is a lot more complicated.

  • For most games, arcade style games, or even RPGs,

  • you can just store it locally as some sort of text file or an encoded file

  • that you parse at runtime.

  • That would be much more commonly seen and embraced,

  • I think, for this sort of use case.

  • And for most gaming use cases.

  • Certainly you could use SQLite, if you wanted to.

  • But only if your data model was really complicated.

  • Starting to love love.lua, says Buddha.

  • Yeah, no, it's an awesome framework.

  • Can we convert the character per minute to words per minute?

  • Yeah, all you would do is just count the number of successful words

  • that they typed instead of counting the characters.

  • But this is not an accurate words per minute test.

  • This is reactive typing.

  • It's not the same thing as typing a body of text,

  • which you're going to get completely different values, completely

  • different readings off of those.

  • Because your brain will be able to read a sentence in advance

  • and fill in and more automatically type then reading just one word

  • as you see it at a time.

  • You'll be much slower doing it this way.

  • So this isn't an accurate typing test per se.

  • This is more of a reactive typing sort of game

  • based on just your reaction time, effectively.

  • Did I take a typing test with Brian?

  • I did not.

  • Looking up stuff in the database in games

  • would be an idea really when you have a massive amount of data,

  • to Andre's point.

  • Yes, yeah, absolutely.

  • Would be cool to watch you.

  • Yeah, no.

  • That's OK.

  • I don't think he and I are going to do a typing test.

  • David and I might do one though, because David threw the gauntlet down.

  • David wants to have a typing test, a typing competition.

  • So I mean, if he wants to throw down, if he wants to maybe put a couple of bucks

  • down, let's throw the glove down.

  • Last I checked, he was quite a bit slower than me.

  • But we'll see if maybe he's gotten faster since then.

  • This was a few years ago.

  • Problem is with a database lookup, no matter

  • how fast it is compared to loading a text file,

  • it's still going to be slower than looking it up in memory.

  • Correct.

  • Correct.

  • The use cases isn't usually good enough to merit--

  • especially the complexity in setting up a database,

  • and then having to model tables, and making your data therefore very rigid,

  • if you're using like a SQL type database.

  • If you're using JSON or something, like a Mongo database,

  • it's a little bit easier to justify.

  • But even still, there's a lot of overhead

  • and just complexity that you could just get by just literally writing bytes

  • to a file, and then just treating that as some sort of consistent encoding,

  • depending on what you're trying to store,

  • whether it's characters or inventories or locations.

  • Games are much more flexible with how they usually store their data,

  • but also much more efficient in most use cases because you can literally

  • determine in a specific encoding for something

  • and write that out byte by byte and sort of take that

  • to be expected every time that you want to load that data.

  • Games traditionally, back in the old days, in read only memory,

  • were all encoded as very unique byte layouts.

  • Bit layouts, even, depending on what you were storing, just

  • to fit as much information as you could into such very finite amounts of stuff.

  • Cool.

  • Thanks, Fatma, for joining.

  • See you next time.

  • I think this is probably going to be the wrapping up point for this stream.

  • So we did a lot today.

  • It was awesome.

  • We got-- the cursor functionality was super cool.

  • I'm super excited about that.

  • The typing game was fairly easy to implement.

  • So hopefully, if this is your first time looking at LOVE 2D,

  • it gave you a sense of what it's like programming in the LOVE paradigm

  • and making games and whatnot.

  • This week, possibly on Friday, we'll do an HTML stream, if not next week.

  • Next week I'm also in the talks with some other folks

  • to get a couple of other streams up for you.

  • And that way you guys can get a break from me.

  • I know we've had a lot of me, and would have a lot of me this week.

  • But certainly, with the undergrads all still out on vacation,

  • it's a little bit tricky to find other people to do streams besides just me.

  • But you know, we had David last week.

  • So we can maybe get David in here again.

  • I think David's doing a stream next Friday.

  • I'm going to have to reconfirm that.

  • I think we're going to take a look at render50,

  • which is an application that we use to convert source code to PDF documents

  • so that we can print them and see them as PDFs.

  • But we'll see.

  • Everything will be released on the Facebook group.

  • So whenever we do get all the details finalized, you can expect it there.

  • If you're not following the Facebook group, definitely check it out.

  • I believe it's facebook.com/cs50.

  • I'll let this load a little bit slowly so I can just confirm this.

  • Yeah.

  • So facebook.com/cs50.

  • Go there.

  • You can see all the events that we post, which we

  • post an event for every single stream.

  • If you're not following our Twitch channel,

  • definitely follow our Twitch channel.

  • If you're not following or subscribed to our YouTube channel,

  • follow our-- sorry, subscribe to our YouTube channel.

  • Because all these videos get posted to YouTube,

  • and you may actually be watching this video on YouTube after the fact.

  • But we do streams every week, or at least try to.

  • Given the holidays, we weren't streaming as much.

  • But definitely follow us, subscribe to us, all that fun stuff,

  • so that you get notifications and you can keep up

  • with all of our awesome content.

  • Especially once all the undergrads get back,

  • and we cover a lot of cool other interesting topics.

  • Did I listen to Walk Like an Egyptian yet, says Asley.

  • I did not.

  • But in the near future, I anticipate doing so.

  • Your streams are fun.

  • No worries.

  • I want to thank you on the snake stream.

  • Everything seems so super scary, even though I

  • knew a little bit of programming, but now I understand almost everything.

  • That's awesome.

  • Yeah, and you've been here very consistently.

  • And that's really the biggest battle is just staying consistent and doing

  • something and attending, coming here and trying to learn,

  • even when you don't feel like doing it.

  • So props to you for putting in the hard work.

  • You put in the hard work, and you will get results.

  • That is just how this sort of thing works.

  • And this is good for me too.

  • I mean, I have to stay consistent with this.

  • And it's helping me stay sharp and get roughly comfortable programming

  • in front of other people.

  • I remember the first couple of times I did this, I screwed up,

  • and it was a big deal.

  • And now we do this every week, and it's not a problem.

  • So thanks to all of you who are watching regularly and afterwards.

  • You guys make this all possible.

  • So these are great.

  • All the streams I've watched are cool, but yours are the best.

  • Thanks, Irene.

  • I appreciate that very much.

  • Yeah.

  • It's a good time.

  • It's a pleasure programming with all of you and getting all of your assistance

  • as well.

  • You guys are very, very eager to help and always provide awesome assistance

  • when I need it.

  • Like my ask the audience, my lifeline.

  • Whom would I be asking if I have some question about Docker?

  • I tried to do PIP 3 install CLI 50, had some errors.

  • Post in the Facebook group and tag Kareem.

  • He'd be one to ask probably.

  • I actually don't have a terrible amount of familiarity with Docker.

  • But if you post in the Facebook, someone will probably respond.

  • Especially if CLI 50 is broken or something or is not working,

  • definitely bring it to everyone's attention.

  • Appreciate everybody for the kind words.

  • Thank you all so much.

  • And thanks, Bella, for tuning in.

  • Thank you for another amazing stream.

  • Appreciate it.

  • All streams are awesome, says Bhavik.

  • Appreciate that very much.

  • I don't miss the other streams.

  • OK sure, says Bhavik.

  • Yeah, it's great.

  • Is this going to be C or Python says Corn--

  • am I reading it correctly?

  • CornyGM says, is this going to be C or Python?

  • For which stream are you referring to?

  • Are you referring to render50?

  • Because in that case, next week that's going to be Python.

  • And I am probably going to do an HTML stream on Friday, which is neither.

  • But if you're interested in web dev and you've never done any web dev,

  • definitely tune in for that one, if we're doing that on Friday, maybe

  • next week, but most likely Friday.

  • If you need suggestions about future streams for the near future,

  • a stream on implementing splines would be brilliant.

  • So implementing splines would be tricky, like the actual math, calculating that.

  • Because I'm not a mathematician.

  • But using splines in LOVE 2D to accomplish something,

  • if that's what you mean, that's definitely something

  • possible we could take a look at.

  • Because splines are really cool.

  • You can use them for a lot of cool stuff.

  • But if it comes down to the mathematics of it,

  • I would need to spend a lot of time studying that and implementing

  • a lot of from scratch stuff to do that.

  • But no, super cool suggestion.

  • Minesweeper.

  • Oh.

  • That's really-- I like that idea a lot actually, Bhavik.

  • Thank you for that one.

  • I'll put that one towards the top of the list.

  • Because that's a game I've never implemented,

  • and that could be really cool.

  • I'd have to play it a little bit and get a sense of it.

  • But yeah.

  • I'm half joking because Irene and I are currently being annoyed by them.

  • Oh.

  • Yeah, I mean, they're a little finicky, especially

  • if you're using them programmatically and not

  • through like Photoshop or something, where

  • you have visual control over them.

  • But certainly they can be great.

  • They can also be a headache.

  • Minesweeper would be awesome.

  • Oh, the maths part.

  • Yeah, I do not know the math behind it.

  • Adamantine, Donkey Kong Country.

  • That's a bit more ambitious.

  • But certainly other people have asked for platformers.

  • So we can take a look at a platformer and maybe

  • talk about a basic platformer.

  • The games course, Adamantine, if you keep going into it,

  • we do cover Mario, which a lot of the same concepts

  • apply to Donkey Kong Country.

  • So you'll get a sense of how that works if you continue through the class

  • and make it through the Mario lecture, the Mario lecture and problem set.

  • Super fun.

  • Super fun one.

  • We did a lot of really cool stuff there.

  • Irene is chiming in, splines are annoying.

  • All right.

  • Cool, everybody.

  • Well, thanks again so much for tuning in.

  • I appreciated it, as always.

  • Had a good time.

  • We made another game.

  • And we have many more to make.

  • Tune in on Friday this week, most likely, for HTML.

  • If not, next week, next Monday.

  • And next week for David's stream on render50 and possibly another stream,

  • I'm talking to some other people.

  • I have Rodrigo, who you've seen on stream,

  • is doing a stream on game of 15, but in Lua, which is--

  • game of 15 is a CS50 visit from prior years.

  • We'll be doing that in LOVE 2D and Lua.

  • And he's got a cool distro that he's shared with me

  • and that he's going to go through with all of you very soon.

  • So possibly next week, if not the week after.

  • And then expect a stream from Kareem in the near future as well.

  • And then after that, Nick will be back in town,

  • and other folks will be back in town, Veronica and Emily,

  • all kinds of people.

  • All kinds of awesome content coming in the near future.

  • All right.

  • Well, this was CS50 on Twitch.

  • This was the typing game.

  • Thanks so much.

  • Catch all of you very soon.

  • Enjoy the rest of your day.

  • And happy programming.

  • Bye bye.

COLTON OGDEN: Good morning.

Subtitles and vocabulary

Click the word to look it up Click the word to find further inforamtion about it