 Alright, in this video, I'm going to be explaining how my Tetris works in STL2 and with C. So the new things I had to learn were how to use multi-threading p-threads and how to render text in STL2. So I'm going to explain those real quick for the people who just want to learn how those work and want to get out. So I'm going to start with multi-threading. So when you want to create a thread, you do p-thread t and give it a name. Falling thread id is what I gave it. Then we run this function if and then we run the function that gets us started. So this function will return 0 if it's successful, so the block in if will not run. If it doesn't return 0, there was an error, so we stop our program. Alright, so what we have to give this function p-thread create. We got to give it the address to our thread. We got to give it null for some reason. And then we got to give it the function that we wanted to run. So this is a function. You got to do a start because I'm pretty sure this is a function pointer. So we got to give it a function pointer to what we want this to run. And this right here is what we're sending in. We're sending a void pointer to our parameters of void pointer, which is actually a struct. A pointer to a struct, the struct is our game. So yeah, if threads can only take in one parameter, so if you want a lot of stuff in your thread, if you want to give it multiple parameters, then you're going to have to put it all in a struct. Alright, now let's do text. I'll describe how to render text. So you need to have a font. In our case, we used open sans right here at the top. Alright, and what you got to do first is you got to run this function. Well, first you got to import the libraries. I don't know exactly which libraries are specific, but I know it's stlttf and everything else probably. So we do ttf in it. If it fails, it returns negative one. So we print an error. Cannot initialize stlttf, error, get error. Then we do, if it doesn't fail, then it keeps on going. And we put our font in a variable we call our font. So we run the function ttf open font. We first give it our file path to the font we want to use. So again, in our case, we got the folder font and our font. Then we give it the size that we want it to be. After that, if our font is null, that means that if this didn't exist right here, then it would be null. We do could not load font, and then we do an exit failure. Alright, after that, we run this function. So I don't know what this does. I copied it from Stack Overflow answer. What I imagine it does is it takes the coordinates where you wanted to place the renderer. Your string and your font makes that a texture and a rectangle, and renders that as a texture and a rectangle or a surface. So yeah, we run those. Again, I'll let me describe it. We give it the renderer. We give it the position. This is x. This is y. We give it the string. We give it the font. Then we give it a texture and a rectangle. And I do this two times because did I already show the game? I can do this two times for these two. Sorry if I already showed it. And then we do stl render copy. That just puts it on the screen basically. And then at the end, we close our font. So that was threads and that was texts for all of you who just wanted to know that. So now I'm just going to describe the basic format or structure of our program. So we got main and main. As you can see over here, it takes the input. This is where it gets input. All right. So we give input and that input goes to me. We have a struct called game. So this is where our entire game is structured. Let me move it right here. Game and so what main does is main sends input to logic. All right. Logic edits game. Game gets sent to render. Game gets rendered. Logic changes game, but it also takes game as an input. That's just how it works. So logic takes the game struct and main as an input. And logic edits the game based on all of these factors. And render just takes game and renders what's there. All right. So now let's go to the game struct. So this is a decently sized struct. So right here we have the play area. Our play area is basically just this generic where it's actually 20 by 10. 10 right here. All right. Let's do 20 and by 10. All right. And so it doesn't actually have the following piece. This that's going down, it is not part of that array. What it does have is settled pieces. So pieces that have already fallen. So what logic does is it will check for any full things. So once you have a full thing, a full line, it clears it. So that's what logic does every time one of these pieces gets settled. So let's look at that. Our pieces are all stored in a four dimensional array. And it's actually what most of the code in our file is. So let me open this just to explain how it works. So the first index is the piece. Is the piece we want. So it can be the I piece, the L piece, the square piece, whatever. All right. The second index is the rotation. This is the first rotation. Second one, third one, fourth one, et cetera. There's only four for each. And the third and fourth is just if this is empty or full. So it's the X and the Y that we should be checking. All right. Oh, what's next? We have our cached index. So it really should just be called the index cube. It is this right here, the numbers, the index for all of these. And after that, we have our placement. It's where our red piece is. So the one that's active. So right here, right here, this is placement. So let's just do P. P is an array that has two numbers. So let's say two three. So it would be two to the left. No, two to the right and three down. And that's where it starts rendering this corner. This should actually be empty because of the way I have it structured. But yeah, basically that gives you information of where it is relative to the top. All right. The piece index, that is the index of the piece that we are currently dropping. The rotation, same thing. That's the rotation of the piece that we are currently dropping. Our rotation can change in the middle because we spin it while it's running. Our alt index, that is when you do C, you change and you have this alternate thing right here. So that is the index for that. All right. Then we have our score, the amount of lines you've cleared, downtime. It should be called something like delayed milliseconds. Basically it's how many milliseconds it takes for the piece to drop to go one down on its own. I have it set to 20 milliseconds. It would have been a constant when I was planning on making it go faster after reaching a certain score and stuff like that. But it was too hard not to implement but to play. State. State can be one of these three. So running, these just affect how the game is rendered and what is going on. So running is normally when it's game over state, it's black and it renders it as black. All right. Score, score.c. That is the string that we are sending to the get, text, and rect. Same for lines. That is the second string. Alt in it. That basically just tells you if you have pressed C before. So right now alt in it is zero. Now it is one and now it is zero. Basically this affects the way it works. So right here when it is zero, if you press C, it gets the next one. But now if you press C again, it flips them. You can't do this indefinitely, of course. You have to keep switching them. You can only switch them once to turn to what it is playing, stuff like that. So that is basically the entire struct. Let's go back to the main function. So in main, we set the game at the start. We also set the seed for random numbers. And this is the first index we are going to be using. These two, they are just for when we were to make setting up the tetramino array easier. So yeah, for our main loop, we are pulling this event. So if the event is quit, we do quit. We set the game, state to quit, and then we stop this when it is done. It finishes and we destroy the whole thing. We turn it off. But in the case of key down, it sends stuff to key press. So that is a function in logic. And based on that, for the left arrow, does a function piece left? For the right, does piece right? Down, piece down. Up, piece spin. For space, that's the hard drop. What that does is, while it hasn't settled, it drops it. Once it has settled, it stops dropping it. But no, yeah. When you press C, it switches the piece. If can't switch, this is a global variable. I know it's a bad practice to have global variables or whatever. But it's a small program. So yeah, it switches the piece. If it can't switch, it's basically like, have you pressed switch more than once? Have you pressed switch once already? All right. Areas for resetting the game. The divide, this, it was for debugging purposes, but I kept it basically. Yeah, it switches the piece index that we're using. So yeah. Let's go over the movement functions. So let's do the basic ones. Down, let's do left and right. Oh, actually, first let's do what the thread was, what the multiple thread was for, the second thread. So when you have a program and it's all running on one thread, we basically, what the program is, it is just a sequence of steps. So step one, step two, step three. And since computers are so fast, these steps look like they're being done instantly. So, but if, let's say, step two involves a delay of, let's say, two seconds. In our case, it was 200 milliseconds. Then we can't go to step three until step two is done. In our case, our second thread was to have our piece drop every 200 milliseconds. Now we can't be waiting every 200 milliseconds to have the next, to have our keyboard input and stuff working. So what we need is a second thread to do that for us, which is what this thread does. It does a bit of starting up stuff to do, like doing a random number. Filling up the queue, getting the next piece, resing the placement, whatever. It doesn't need to be there. It didn't need to be there, but I just put it there for the sake of it. Just to have it up. It's probably not a good idea to put it there, but I did anyways. So anyways, while we haven't quit the game, and if the game is in the running state, we wait 200 milliseconds, then we do peace down. That is what this thread is supposed to do. Now the way we could have solved it is if we were constantly checking, let's say we have a, let's go back to the one, two, three, four step. It goes one, two, three. And in two, we would have, if it has, if x, let's say x is like, we're trying to say, if it has been 200 milliseconds since last drop. And if it hasn't, it continues. So that happens instantaneously. We could have done that. But I didn't, but, you know, I wanted to learn multiple threads. So, so I didn't. All right. Now let's go to how the pieces work. I'm going to take a little break. Oh, it's been 17 minutes already, huh? All right. Let's continue. So right here, we are being sent the key codes again. It checks for any events and gives you the key codes. All right. Again, like I described, left goes left, right goes right, down goes down, up is spin, space is hard drop, whatever. And the movement stuff. Let's start with the basics. Down is kind of a little bit special because if you, if you have a collision while going down, that means you have to settle the piece. So, so, you know, initially, you know, it goes down on time. And then it checks if the piece collides, if it does, and if that piece is at the top, then the game is over. But if it doesn't, then, you know, put the piece back up and settle, settle the piece and continue on with the game. All right. Piece left. You know, if it hasn't collided, you know, it goes left and if it's collided, then it goes right one, goes back right one. What am I saying? It goes right once or while it's colliding. While it's colliding, it goes right. Same for right, except, you know, left and right are switched. All right. So for spin, let's actually check what this collides function is supposed to do. So basically what it does is that if we're overlapping with, with something. So, let me, sorry. Basically it checks if our, if our piece is out of bounds. So remember when I said right here is where our placement is. It's actually a little bit above that. But what it basically does is for every, it scans the whole, the whole array kind of. It goes to, it goes to the index, to the piece index and the rotation index. And then it scans where it is. Bop, bop, bop. These are the ones that are here. Anything is out of bounds or if let's say you already have like a whole block right there or whatever. And this is touching and this is overlapping then it returns to. So if it's out of bounds like here or if it's overlapping like here, it returns to. Now, yeah. That's what, that's what collides does. Now edge correct. That is, that is special for, for spinning because spinning, you can't just move it right up again. And we can't just immediately, if it collides then we have to, we have to just return it back to where it was. So when we do spinning, if it does something like this, oh my God. And it's, let's say it's colliding here. Then we have to move it this way. But what if we already have something here and if it moves that way, it gets stuck. All right. We don't have this problem when moving left and right because in order to be there, then it must have not been colliding. So if it does that, then we have to spin it. We have to put it back where it was again. So yeah. In that, in that case, I made it so that if it's colliding at all, I don't care if it's overlapping or if it has something below or something above. We have to, we have to move it once, right? Move the piece once to the left. If it's still colliding, move it again to the left. If that doesn't work, do it for the right once. And then again, if that is, if that doesn't work. And then we do the same for up once. And then again. All right. We're worried. And if it doesn't work, then let's go back up here. And when we're spinning, it returns it back to where it was. So, so yeah. Where was I? Me. Oh yeah. Settling pieces. This is where everything has to happen. So we do settle piece again. If it, if it collides, if it collides with anything when it goes down. Basically what we're doing is we're taking, we're taking the placement data. So sorry about that. We're taking the placement data. And we are, so yeah. I should have been doing square this whole time. So we're taking this, you know, p, let's call placement again. And our index, i, and rotation, and then our x and y. And then we are translating that into, we just care about this x and y basically. And if it's empty, full, full, whatever. And we're translating all these folds into something in our big, our big play area. So let's say this was here. And we would put it in a square here. Same goes here. Square here. That's what, that's what place, that's what settle pieces does. It also does a lot, another couple of things. It takes settled, which we used up here. It's another global variable, which we used that right here. So if it, when we're doing the space. So once it has settled, you stopped, you stopped getting, going the piece down, putting the piece down. And moving the piece down, sorry. All right. So, so that's there to stop, to stop hard drops are happening forever. Then we move the piece index. We make it the new, the cash index or the next one up in the queue. Then we do next piece. Now what this next piece function does is, oh my God. So this next piece function does is it takes, it resets the random number. It moves everything back up. So it moves, it moves the cash back up. So let's say we have a square, L, I, whatever. So it's settled. It moves this back up here. And the next piece moves this up here, this up here. It's fun in the bottom up here. And then it creates a new one here. And by doing that, it does a new random number. And that's the new index. So that's basically what next piece does. Now let's go back to, to settle, to settling the piece because that is, that has a lot of stuff in it. Sorry about that. Reset placement. Basically it sets the placement of the piece back to zero. So everything's zero zero. Clear lines. So that checks if anything, if any lines, if any lines have been filled. So, and then it says can switch back to true. So let's talk about this can switch stuff first. So when we have C, we can switch, but we don't want it to switch again. And now that it's settled, it goes back to true so we can switch it again. All right. That's it. So when we're clearing lines, this is the last part that I have to, that I will explain. So when we're clearing lines, let's talk about this first. Once you're done clearing, wait, actually no. Sorry. When we're clearing lines, we run this function, find lines. What, what find lines does is we're checking, we're checking every single, every single row. And we're, we're adding up every single piece. So let's say right here, let's say we have three, we have three full, full lines, right? It checks this one. It checks the next one. It checks the next one. And it checks this one. All right. It adds up all the full, all the full pieces. And, and then it returns, it returns which, which row this was. So let's look at this again. Now, this is where it finds lines. This is how it finds it. If the total, if the, if when you add all of them equals clear bar, then, you know, you're returning the row. If, if it's not, you're returning negative one. So let's get a clear bar. What clear bar is, it's just the number that we set settled square to be and we multiply it by the columns in each row. Okay. So once, so once we have this row number, if it is, if it is negative one and we're done, we update the score and update the, the string for the score and we return. But if it's not negative one, we do this. We first, we first clear, first try to clear this, the top, because if it's not clear, if it's not cleared, then it's automatically zero. This might cause some problems, but that can be exploited, but I kind of don't really care. Hopefully. Oh, no, yeah. No, this is fine. All right. So it clears, so it clears the top, the top line, the one at the very to be top. Then it goes, it goes to the row that we gave Y or say row goes to that right here. And then it sets everything back to zero, zero, zero, zero. All right. What it does is it takes everything on top and then brings it, brings it one down. All right. It increases the temporary lines. We use this for scaling the score. And this is a global variable, by the way. So, you know, more bad practices. And then we run the, we run the function again, runs find lines until we're done until we can't find any more full lines. And I think that's it. Let me just explain a little bit of rendering. This video is already too long, so, but I'm still going to explain. If it's running state, then it renders, then it renders the game like normal. So what it'll do is it checks in the screen and gives you the white pieces. And then it takes the, takes the piece index, the rotation and everything for the, for the, for the falling pieces that we have. And renders those as red that we have right here. It renders the grid, which you, you, it'd be hard to see the grid because the screen is 1440p and I think I'm recording it on 1920 by 1080. And render right screen. This is basically rendering the queue. We know this, this right here is rendering the alternate index. Oh, what was that called? Yeah, the little, little thing that we can switch. And this one is rendering the queue or the cache. Oh, that's all. Oh, thank you for watching. If you made it this far. Yeah.