 Welcome back to 105. So we're doing two D arrays today. So after today's lecture, you can actually do lab six. So after this, you should be able to actually know everything you need to know for lab six. So let's get into it. So previously we had one D arrays, they were okay, right? So to declare it, it was just some type, some name and then in square brackets and array size, whenever we want to declare it, and then optionally we could go ahead, give it some values. We know what types are now, we can just name it whatever we want, give it an array size that would be one or anything greater than that, and then some values. And then we saw we could optionally just omit the array size, we had that fun little macro that could figure out the array size for us. So any questions about the first array before we get into more fun arrays? So two D arrays, as you might imagine is just another set of square brackets. So syntax looks almost exactly the same. So there's a type, what each element should be. You give it a name, and now instead of having one dimension, wow, it has two dimensions. So they can be different sizes. So there's the first size and the second size. So all the rules aside are the exact same. So the type, again, type of each element, name, we can name it whatever we want. And then the first size is the number of arrays you want in the next dimension, or you can think of it as the number of rows if they're just two D arrays. And then the second size is just by the number of elements in each of these smaller arrays. So we could say it's like multiple occasion rate. So we could say we have two arrays of size three. So we can just create an array that's a two by three. So we can declare it table, let's just call it a table, assume it has a row and a column. We can say it has, in this case, two rows and then three columns. So just like one D arrays, all the dimensions are zero indexed. So if I wanna access the element in row one as a human, column two, as I understand it, well, that would translate to index zero since zero is the first index. And then that would be our row and then our column would be one which would correspond to index one would be essentially column two, right? So same as before, we can go ahead and just, oops, we can go ahead and we can just create them. So before when we created an array like this and then we tried to print all the values, they were just garbage. In this case, same thing, they're just garbage. So if we compile and we run this, we see that we iterate over every single index. So tables, so there's going to be two rows. So this is the first row. It has three elements in the row and it's just full of absolute nonsense. Then next one also has some nonsense in it, although some of the numbers seem to actually repeat, but that was just luck. So, any questions about this? So far, nothing really new, right? Trust me, it'll get weirder at the end. So, nothing new here. If we want, same story as before, we can assign values to elements if we want in the declaration, but the only difference here is we do an inner set of curly brackets for each row. So in this case, I can have one set of curly brackets around the entire thing, so that is my entire 2D array. And then each of these inner brackets here are all of the elements in that row. So in this case, I would say, row one has the elements one, two, three, and then row two has the elements four, five, six. So, first set of curly brackets for the entire array and then the next set for a row and then that looks like one of the single arrays we had before. So, we can go ahead and do that. So we would just say equal new set of curly brackets and one, two, three, then we go with four, five, six and then, boom, we initialized everything. So if we compile this and then we run it, we see that, hey, guess what, they're all initialized. They're all initialized in the order we did them. So this was row one, column one, then column two, then column three, so same thing here, they all nicely match up. So, questions about that? All right, nothing, that's good. It means we know arrays and this isn't actually that surprising. So, also just like I had there in that example, well, table at index one or zero one would be equal to two because that's this element right here. So, if we wanted to, it gets a bit ugly but we could actually just initialize our 2D array just with one set of curly brackets and that also seems to work. Oops, wrong one. So, if we go ahead and we just do something like, let me do this, do, do, do. So, if we do something like this, C wants to complain at us a little bit that we're missing braces and it's telling us, hey, you should put them around every row but turns out that, well, just a warning so we can go ahead, run it and we see we get the exact same thing. So it still seems to work. Those braces seem to be optional. But why does it actually work? So, it only works because arrays are stored in memory in something called row major order. So, they are stored similar to arrays. They'll just be elements beside each other but in this case we're going to store all the elements of a row beside each other in memory and then we'll go to the next row, store them all beside each other and this 2D array will be just one big contiguous array. So essentially the same thing is just having, in this case, an array that has six elements in it. So, if we really wanted to, in this case, we could represent the 2D array using a plain 1D array. So instead of having just like, instead we could have an int array called table and then whenever we index the elements we can just do something like this. We take the row index, times it by the number of columns and then add the column index. And luckily because everything is base zero, this actually works. So, if we take, in this case, the row being zero and the column being zero, if we do that calculation we get index zero. If we take the row zero and then column one then zero times whatever is zero plus column so we'd get zero, one, two, three as the indexes. And then similarly for the next row, well it would be one times the number of columns which is three, so plus zero so we would just get three and then we get four and then six or sorry, four then five. So everything pretty much translates. So this is why we call them row major order because well everything in the same row are beside each other in memory. We could have just done this calculation ourselves. We could have used pointer arithmetic if we really wanted to, all means the same thing. So any questions about that? All right, perfect. So if I wanted to do pointer arithmetic, if I take the address of the element at i, j, it's also the same as doing that same pointer arithmetic I did. So table which is a pointer plus and then this is just going to be an integer. So i times the number of columns plus j so it'll move me that many elements. So if it was i equal to one, j equal to two, then I would take that table array and then move it forward five characters and I would get this element. So we're good pointer arithmetic still. Everyone loves pointer arithmetic. So why we're doing this in C I guess. So similarly for one D arrays, we can just let C determine the number of rows but we can't leave out both of the indices here. So I can't do something like this and say, hey, C figure out the number of rows and columns I have because there'd be no way to tell what the hell I actually meant. So if I just have six numbers in a row, well I could have two rows and three columns. I could have three rows and two columns. I could have one row and six columns. I could have six rows and one column. So C actually needs to know the number of columns to calculate the number of rows. And more importantly, it doesn't really care about the number of columns you have. C just needs to be able to do this calculation itself. So it needs to know the number of columns because it needs to know how much to skip to get to the next array essentially. So we can leave out the first dimension only and C can figure it out. So if we tell it that, hey, I have three columns and then you figure out how many rows I have, it can do that because that actually has a solution. If I have just six elements here, well then it would know that I have actually two rows. So this applies for any number. I could have a three-dimensional array, a four-dimensional array, so on and so forth. All I can, the only thing I'm allowed to do is drop the first dimension and then I have to specify the rest of them. So questions about, yep. So I can only emit the first dimension because C needs to know this calculation and the number of rows doesn't factor in here. So I don't actually need a number of rows. C can figure it out similarly to just that one array and I can use something like that array size macro and using the size I can figure out how many rows it has in this case. But I need to at least know the number of columns. Past that, it doesn't really care. Right. All right. So other things we can do, so let's go back to the code example. So if I wanted to go back and say I specify something like this were, all right. So I have this where I actually define everything and when I define everything, well maybe since I give it the number of rows and the number of columns, another thing I could do is something like this. So I have here, I know I have two rows and three columns but I give it two rows and then only give it two columns. Well the C rule is you might think that hey, maybe this value that I didn't give it is undefined or something like that. I might get some garbage. But C has special rules where if you give it any number of elements here and it's a known size, so C knows that it is a two by three and there should be three columns in a row, it will go ahead and it will fill it by zeros. So if I run this, I'll see that it just filled in the missing element of each row with a zero. So it will do this, even if I only give it one element, if I do something like that, the first row would be just one zero zero. So I'd get one zero zero and C will do that even with one D arrays, if I actually give it the dimension of the array and then an initializer list, if it's smaller than the actual array, it will go ahead and fill it with zeros for us. So questions about that? And yeah, if I remove the other braces will I get or, oh, if I just completely remove the braces. So yeah, I've got a question is if I just do this, what happens? So if I just do this, it would likely just fill up the first row. So it just fills up the first row and then fills the rest of it with zeros. Wait, go back. All right, questions about that? Just more silly rules that may or may not be useful for us, but we should know what they actually are. So like I said, same idea for plain arrays. If I initialize at least one value and I say if I declare it with a known size, it will go ahead and just fill up the rest of it with zeros for us. Sometimes that actually proves to be useful. So other things. So remember one of the rules I said before where it was like if I have an array and I don't know the size at runtime, I have to go ahead and malloc it and get the space for it on the heap. Well, if you don't have a ancient version of C, which I guess was recently upgraded here, but this has been around for like 20 years, you don't actually have to use malloc for some arrays. So I can actually do something like this where I just ask the user for the length of the array and say I just prompt them to enter some number that is greater than zero. And then I can just go ahead and declare an array using that array length and C will go ahead and get memory for us. But since we're not using malloc, that will be on the stack. So it has the same limitations as when we had trying to return like a memory address before in a function. So we can't like return an address to array outside of this function or use anything. In this case, it's in main so it doesn't matter but that rule still applies. But in some cases, if you don't know the length of the array you can go ahead and you can do this if you want as long as you have a C compiler that is newer than the 90s, which is still older than all of you. So as long as the C compiler is your age or younger then you should be good. And then we can go ahead, we can use it, we can like randomize array, we can go ahead, we can just fill it in with random values, we can do whatever we want. So questions about that, perfectly legal to do. We can do that just changes where things are. All right, other fun things to do. So yeah, all the rules for the arrays apply. So if I wanted to, oops, I could do something like this where I can have a dynamic 2D array. So I could say enter the number of rows, enter the number of columns. The only rule is if I dynamically allocate an array I can't assign values for it like here because when I compile my code C has no idea to know how big it is if it can even hold this many values or anything like that. So the only rule is I cannot initialize it with anything but I can declare a 2D array using those two variables and then I can go ahead, I can call like randomize table on it and that will fill it with a bunch of random values and then I can just output all of the values I want to so I just iterate over them and print everything. So if I go ahead and run this, I can say let's enter the number of rows. I don't know, let's do a four by four and it can go ahead, it can generate it for me. Yay, but not terribly exciting yet, right? But kind of looks like lab six. So lab six, well, you're given like, you're given something like this to use and you just go ahead, use that to get your 2D array and then you can use it in every other function. And the only other rule is with the functions, while in the function declarations, I can't just say, hey, you know, just use, I can't do something like this because again, C needs to know at least the number of columns. The only thing I can admit is the first dimension, but I can do it dynamically so I can do something like this. But the only rule for this is this variable name here has to come before I actually use it in the function declaration because if I do something like this, if I do, oops, not num calls, if I do something like this, then I get use of undeclared identifier, it does not exist yet, it has to come before I actually use it. So I just have to do something like this in num calls. And then C is happy, it knows the number of columns in my 2D array and everything is fun. All right, other things I can do, other rules still apply. So if I do something like this, where I just in my array, I just admit the first parameter, so I let C figure out the number of rows. Well, if I had that array length macro like I had before, I can just have C figure out the number of rows for me. So I can just ask, okay, well, what is the length of, or yeah, what is the length of this array and it will fill in this dimension for me. So I would get two rows and if I run this, it will also work. It will just show me every element of the table. Again, nothing terribly surprising, right? All right, any questions before we get into the weird part where I spent like a whole day trying to craft this animation so you can actually understand it? All right, good. So now it's going to get slightly strange. So just to recap, if I, that is a mouse, all right. So just to recap, if I want to use dynamic lengths in like function arguments where if I have a 2D array, I need the number of columns. Well, the variable has to become before we actually use it. So it has to come before the array itself. In lab six, you're already given arguments that that's true for. So you don't really have to do anything but that's just to explain why they are written the way they are written. So just to recap, you can't write this because numcalls comes first. If I really wanted to, I could fill in the number of rows I want but it's not really going to do anything for us. It's just more typing. Don't actually have to do it. All right, so for 2D arrays, all columns need to be the same length but maybe you want your columns to be different sizes. So since each column is a different size, well, I can't use like a real 2D array because I don't know how big a column is and I need memory for it. I, what I have to do is I need to use malloc and have a slightly different approach. So that slightly different approach, which is going to be the entire example for most of the class unless I have time for another fun thing we can do is we have to create an array of pointers and then in that array of pointers, each pointer is itself an array. So the idea behind that, before when I used malloc to get an array, right, I just malloc some, I could just malloc some space. So I did like the size of an int times how many things I want. I got back a big block of memory and then I treated that as an array. So that was essentially like one row. So now the only thing that's going to be different is instead of having essentially one row, I will have multiple. So that means I need multiple pointers. So I have one pointer for each row and then I malloc each row individually and then I can go ahead and actually access all the elements in the row through that pointer. So each column essentially, or each row gets malloc. So that is a typo there that should say each row gets malloc. So here is that whole example. So this will actually let you input the number of rows and then for each row, you get to input a different number of columns you can have as many as you want. And then like the midterm, in order to know that you are done with the column, it will put a negative one at the end. So it's going to declare one more integer than we actually need. So it can go ahead and stick a minus one at the end. After that, it's just going to fill it with random values between one and a hundred. So let us break this down. And again, please stop me and ask any questions if this doesn't make sense. This is the thing that took me literal hours. So we would execute this, starts executing at main. We asked for the number of rows that just outputs to the user. Then we say input the number of rows and this is that function I had before that will just ask you over and over again until you input something that is greater than zero. So let's say I input the number of rows and I say it's two. So I would have a variable on my stack called num rows. It's an integer, its value is two. Good. All right. Now we take our first weird turn. So before we had like an int array, num rows. So that would declare that many integers. Instead of integers now, I'm declaring that many pointers to integers. So I'm declaring an array. So this is an array called table. It's going to have two elements. And then each element is going to be a pointer to an integer. So after this, because I did it on the stack, I would have essentially two pointers here. I don't know what their values are, but I have two. One would be at index one or zero. Don't know its value. The other would be index one. Don't know its value. They are both int pointers, right? Okay. So now we go into the for loop. For int i equals zero. So we would create a new variable called i. And then check is zero less than num rows. Well, it is not. So we go into the body of the loop with i equal to zero. Then printf number of columns. We would see something that says actually gets our input. So let's say the number of columns I input for the sake of making this slide in my sanity. I just input two. So I input two columns. Okay. So I need two columns for this row. So I need to malloc. I'm going to store integers. So it is the size of an integer times the number of columns because I need one number for each column. And then I'm going to add one to that. So I would add one to that. I should have brackets around that in the slide. Turns out it actually works still. But I should actually have brackets here because it would be like four times. Oh yeah, finding bugs live. So this would be four times two, which would be eight plus one, which would be nine bytes when I actually need 12 bytes for three things. So should have put a set of brackets around here. All right, C is fun. But after this malloc, as long as I write it correctly, I should get back 12 bytes that I can use for three integers. So that would be on the heap. And I don't know what the value is. And malloc returns me a pointer. So I get a pointer to the starting address of this block of memory, which I'm going to treat as an array. And in this case, all my boxes here are going to be integers. So I have three integers, don't know the values. Everyone good? Still good? All right. Then I have my loop, my inner loop here. So for i or j equal to zero, so I'm going to iterate over every single column and generate a number. So for this, well, j equals zero. So zero is less than them columns. So I have a variable called j. It is equal to zero. And then here, well, the value I'm accessing is table i. So i is currently zero. So that means I'm using this pointer, right? So table zero here, or table i in this case, is this pointer. So that will get me accessing this location here. And then this is just pointer arithmetic. So I access that pointer. And then I'm treating it as an array. In this case, j is equal to zero. So that would be this element right here, right? And then I generate a random number. In this case, it would be 84 probably. Good so far. So I go increment j, increment j to one. Check, oh, is one less than two? Sure it is. So I'll show that my j has been updated to one. Then here, I'm accessing table i. So yeah, table i, which is zero. So that is this pointer right here. And then I'm doing some pointer arithmetic or I'm accessing element, in this case, j equals to one. So it would be this element right here. I set it to some random value, probably 87. Go back up, j increments to two. Two is not less than two, so we are done this for loop. So j goes out of scope. It disappears, tying together everything, right? So questions so far, where we're at with that. All right, so now we got table i. So i is still zero, so it's this. And then we're accessing index num calls, which is equal to two. So that would be zero, one, two. It would be this one. We set it equal to negative one. And whenever we use this array, we can just go ahead and go over all the values until we hit a negative one, and then we know we're done. So now we're done that iteration of the loop. We can go back up. We would increment i from zero to one. Then check, oh, is i less than num rows? Oh, well, num rows is two, so one is less than two. So we go in here, we ask again the number of columns. Ask the user to input the length. In this case, I will save myself some writing and just input num calls equal to one. And then here, we do malloc again. So we would malloc, in this case, num calls is one plus one, so two integers. And then after malloc, we get a pointer back that points to somewhere in the heap that has some new memory for two integers. So it looks like this. Now, in this loop, we're starting again. So we declare a new j, j is equal to zero, zero is less than num calls, so we go into here. We're accessing table i. Now table i is one, which is just this pointer. And then we access element j, which is zero, which is this element right here. And we assign it some random value, in this case, probably 78. Then we increment j, j is now one. One is not less than one, so we are done this loop. And then here, we access table i, which is one. So table one, so we're using this pointer. And then we access element num calls, which is equal to one. So that would be zero, one. It would be this, and we set it equal to negative one. All right, so questions about that. So this isn't a true 2D array. This is an array of pointers. And then each of those pointers is an array, yeah. So in this, if you want row one, column two, so like row one, column two in human parlance, in human speak. So if we access like row one, which would be index zero. And then column one, which would be index two. Well, we can still use this with two indexes, right? So if I use table zero, that would be this pointer, right? And then using that pointer, if I just indexed a one into it, it would be zero, one, it would be this, 87. Yeah, yeah, table square bracket zero is just a pointer. But remember, like array decay, arrays are just a pointer. Yeah, so it's table square bracket zero plus one, if we did pointer arithmetic. But remember, instead of doing pointer arithmetic, because we would have to do pointer arithmetic and then dereference it. We could just do the square brackets and do it all in one step, right? So if I write that out, it looks exactly the same. I'd access table square bracket zero, square bracket one. Yeah, yeah, still the same syntax for this, but this one works slightly different, right? For a true 2D array, it would just calculate what index it needs directly because every column's the same size. For this, it's a bit more indirect. It has to find which pointer to use, like which pointer to use for the array, and then using that, it looks it up in the second array. So this uses essentially two separate arrays, and the true 2D array just uses, it just lays it out in memory all together because everything's the same size that can do that. Yeah, so this will work because arrays are pointers, right? And C will know based off the type I give a name. So I have to say, in this case, table is array of pointers in this case. But if I said it was the 2D array we had before, C will know, like, that's a 2D array and the number of columns is constant. So I don't have to do any weird pointer math. I can just figure it out because I know what order it's in, right? All right, any other questions? All right, this is, other question. Are table 0 and table 1 elements actually contiguous in the heap? So these elements on the heap know they will not be because this is the result of a malloc and this is a result as a malloc. They probably will not be contiguous with each other, but they're contiguous within themselves, right? So since these three were all the same malloc call, they're beside each other, which is why I can use them as array. And so are these two are beside each other because I can use them as an array. But these are not all beside each other, right? They could be somewhere else completely in memory, could be not light years away. It could be like thousands of bytes away. All right, any other questions with this? So this is, might take a bit to sink in, but hopefully me wasting, not wasting, me spending a lot of time on this, hopefully helped. So at the end, we're done, we'd exit the program. And we can go ahead and run it just to make sure that it actually runs. So here is that same thing. And then here, just to show you that the format works, if I wanted to print out everything, I could just iterate over the number of rows, print the row, and I'll do i plus one so I can turn it, crap, my bad. All right, so here is that program again. So here is the beginning of it where I say, enter the number of rows. And then I have a array of int pointers. Then for the number of rows, I say number of columns for the row. So I have more print lines here. I just ran out of space on the slide when I animated everything. Here, number of columns, I malloc it and I fixed it. And then here, I went over every single column, generated a random number, put a negative one at the end. So if I wanted to print everything, I can just iterate over the number of rows. I know how many rows there are, so I can use a for loop. So I'll print row and then i plus one to bring it into human base or index one, then start j equal to zero. And then here is where I access just normal 2D array syntax. So why that works again is because table i gives me a pointer. And then I use that pointer as an array, so I can just use this. I can just use index j, which that would go ahead, do the pointer arithmetic for me and then dereference it so I just get the value. And then I'll just print f, whatever that value is. So I just iterate over and over again until I hit negative one. I know negative one means I'm done. Like this is the last element in the array, so I shouldn't print it. Otherwise, I'll just print off what the element is, increment j, and then print a new line while I'm done. So if I run this, different column sizes, I could say enter number of rows. I'd say three, number of columns for row one, two, one, and then 10, something like that. And then if I run that, boom. So I get the same numbers, thankfully, as the example. So I get 84, 87, 78, and then the other gigantic row, I just get a whole bunch of other numbers. But works, kind of cool, right? So that will be very useful once we start on the next topic. But I go back, so like I said before, the previous example is not like a true multi-dimensional array in a real 2D array. If I write table ij, that's the same as just doing this. So the index I'm actually using because it's in row of major order. It's this i times the number of columns plus j, that's the actual index. But what I did in the previous example just again, I accessed, I had an array of int pointers. So if I just split that into two steps, I get, okay, so table i is a pointer to an int. I could just call that row if I want, and I could treat this pointer as an array. So I could just say row at j, and that gives me my element back. So I can just, so if I do table ij, that's the same thing as doing this, it's just saves me a line. But if it's confusing at first and you want to write that, you can write that, it does the same thing. All right, so the only difference is because in the declaration, I had to use malloc, right, it could be if I, yeah. So I could have omitted the number of elements if I wanted to. If I used malloc to actually allocate my array of pointers. So I could have wrote something like int star, table, and then the array, which means I could have dropped that because this decays into a pointer. So the actual type of table is actually int star star table. So it is a pointer to a pointer to an integer. Cool, right? When I showed you pointers, you saw two stars. You're like, why in the blue hell would I ever need something like that? But we do need that. So even if we want like a 2D array of integers, we technically need that. So that's our first double pointer of the course. Yay, thankfully in this course double pointers is about where our usefulness stops. So if you wanted like a 3D dynamically allocated array, well then it's probably a pointer to a pointer to a pointer. So there's three of them. But in this course, as far as I know, at least we only need 2D arrays. So this will be the end of how complicated pointers can get. So if you can understand this, you understood pointers so hard so far. Congratulations, you are now done with like the hard syntax part of C and understanding what the hell C is supposed to be. So, since we have time, I will do a quick little exercise as much time as I have left. To show you essentially something that is very similar to your lab six. So how lab six is set up is something like this. So there's skeleton code, it gives you the size to use. So they have constant int size, I like all capitals. They just had capital S, so you should follow their format and not mine cuz I don't get the final say. But I just do it like this, so it would look exactly like this. So they say, you can just declare a board of size size and it will be in this case a three by three. So I'm just going to write like a quick little tic-tac-toe checker. So I declare a 2D array of size times size. And then I can input x's and o's. And then maybe I just do a dot for a empty space. So I could just iterate over essentially the number of rows, iterate order the number of columns. And then that is how many characters I have to capture. So remember whenever we want to capture a character, we put a literal space before it because otherwise we might actually capture a space especially with your word problem for lab six. You do not want to capture a literal space. So this will ignore any white space if we do something like that. And we just store it to a character. So this board is just going to be a 2D array of characters. And you should just use a 2D array of characters for lab six. So some of you know what the word string means and know kind of what that is. We don't, most people don't know. We don't know, we will do strings next week. You do not need strings for lab six. In fact, you should not even understand what the hell I'm talking about. So just a 2D array of characters. So in this case, I fill it all up. And then I can go ahead, I could check and see if there's a winner. So let's see how much I can get through. Let's make sure it actually works. So input X's and O's and then a dot for an empty space. So I could do something like this. Let's say I just do something like this. So tic-tac-toe, I either need to have all the same characters horizontally. So all in a row vertically, all in a column, or in the diagonal. So in this case, there's X's in the diagonal, so X should win. So if I press Enter, well, fills up this board. It is now a three by three array of characters. So it would look bestowed like this. It would be index zero, zero, zero one, zero two. So on and so forth. And then here I have my check winner. So because it's a 2D array, it needs the int size. And we can neglect this parameter. And I can just use, in this case, the size that I was given in the function. And it would work for variable number of sizes if, for some reason, I wanted a tic-tac-toe board that was a size four. I could just change that and then everything magically changes. Don't really need to, yeah. How did it read the diagonal X's? So in here, let's see the fun solution. I guess I have five minutes. So in here, I just have a for loop that iterates over every single row and column. So in this case, it's not every single row and column. I only cared that I know it's a square board. So I just have one variable I that goes from zero all the way up to less than size and then in here, I'm going to check the first row and column all at once. So I need to figure out what the character is. I start off here. So I figure out what the character is in the first column of whatever row I'm checking. So I'm going to check row i for row char and call char. I check what the character is in essentially row zero but with whatever column I have. And then in order to win, I have to find all X's and all O's. So I check if that character is an X or an O. Otherwise, if it's like a dot or something, then it's not a valid character. You can't win. It needs to be an X or an O. Then I go and I check for, in this case, one and the rest of them. I'll check all the rest of that row or all the rest of that column. So because it's an AND, they all have to be true. So they all have to match the character that it started with. So if it started with an X, the rest of that row should have X's. Or the rest of that column should have X's if it started with an X. So I just go here, I check them. And then at the end, if row win is still true, then it means whatever that character was actually won in the row. Otherwise, if the column win was true, whatever that character was won in that column. And then same for diagonal. So diagonal, I started with the top left character. So I had one thing to check this diagonal. And then I had a bottom left character, so I checked this diagonal. And unlike your lab, I don't have to check them forward and backwards because order doesn't matter in tic-tac-toe. So didn't really matter. So what I did is I just went, I could have went top right and went down and then bottom right and up. Or I could just have done what I did and just went from the left side. Doesn't really matter in this case, will matter for you. Then in my loop here, I just went across the diagonal, so the top left. Well, if it started at zero, zero, the next would be one, one. And then two, two, and that would be that diagonal. The other diagonal, the bottom diagonal would start at zero. And then we start at size minus one. So it would just be size minus one, minus I, so going back up the other way. And then here I can check if we won in the bottom left or top left diagonal. So if I run that, let's see tic-tac-toe, wee. So in this case, if I have two winners, then my code's not that smart. It just reports all the winners, doesn't really care. But in tic-tac-toe, if you actually wrote the game yourself, you'd make sure that this is a good basis for it, but you'd make sure they take turns. And then if someone wins, you just automatically stop the game as whatever winner, so if you wanted to, this is a lot of the logic for it. But you could take this, you could tweak it, you could actually play tic-tac-toe if you really wanted to. All right, any other fun questions for this? Yeah, so we went over check winner. Thankfully it's on the recording, so it was too big to fit on the slides, plus I didn't know if I'd get to it. But yeah, this is basically a simpler version of lab six. But the ideas are kind of similar, right? Lab six, you're doing a crossword, order matters, so I have to go forward and backwards and do this. In this, I only have a few directions, and I don't have to go backwards. Same, but same idea. All right, any other questions before we wrap up? All right, perfect, enjoy your weekend. Just remember, pulling for you, we're all in this together.