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.  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.  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.  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.  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 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.  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.  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.  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.  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.


In the military, when you get promoted, when you move from one rank to another, you get what's called a promotion order. And on that promotion order, they read that promotion order, you take the oath of office again. But on that promotion order, they talk about two things. They talk about how the president of the United States has placed special trust and confidence in you because you have shown the capacity to serve in a higher grade. You are being a given promotion not because of what you've done in the past in the military, but because of what we expect you to do in the future. To do great work in the future, to become a leader, and to guide and mentor and lead and inspire other people to go do great things.


So let's do that. I'm going to go. Okay. If I go to the bank feeds then and I look at my transactions. Here's the transaction. It didn't pick it up. It's not seeing it directly. So let's go into it. And the only way it would see it to match it is by by dollar amount and possibly by date. Let's go into it and say, okay, I want to match this to an invoice. If I go into the match, then it's pulling up that payment. So now you can see it saying, okay, now I see the payment that we're matching to. So there's the suggested matches one connected. So now I'm going to record this, which should record the, the, the deposit. So the next transaction. So I'm going to, it's actually going to do a transaction. So I'm going to save it. And so now it's recorded that. And if I go to my balance sheet and run it, I should have a checking account item within it for that, for that deposit of the 420. Let's check it by dollar amount over here. There it is right there. So now it's recorded with a deposit form here and it made the deposit for. So if I go into it, it made the deposit form and it looks just like that deposit form that I was going to, that I was showing you that we could have done on our side. All right. And then the other side decreased the clearing account. The other side decrees to clear an account and the payments to deposit and boom, it's back down. So now it actually made a transaction.


I don't commonly find things on the internet that make me want to throw the monitor out the window, but this is one. My friend Jake emailed me. I will copy and paste his message. Hey Logan I found a website that I thought you might like. It's got a ton of deleted scenes from kids' shows. Man, some of them are funny as hell. I clicked the link. The website was a colorful page with a list of links to video downloads. I scrolled down again and found a link for a video called SpongeBob SquarePants Sunday Deleted Clips. I clicked the download link. It finished downloading in 10 minutes. I opened the file. The episode starts with SpongeBob trying to find ingredients to make his sundae, but when he was about to eat it, it cut to static for 50 seconds and then cuts to the scene where he sees the marching band. One of the marching band members is seen with a rope in a loop. Another member is seen with a lighter and a jug of gasoline. That went on like it usually did but after the part when his stinky breath crashes into the marching band, all of them explode as their blood and guts went flying everywhere. They weren't cartoon blood and guts, they were totally hyperalistic ones. It cut to SpongeBob and Patrick in the movie theater. SpongeBob does the gag where he turns to the fish next to him and asks if his horrible ugliness bothers him. The fish falls out of gun and shouts. I can't take it anymore. Patrick runs away causing him to drop the gun and leaving it behind. Soon Patrick and SpongeBob are the only people in the theater. Patrick notices the gun on the floor. He picks up the gun and says in a demonic voice. This is for your end, good SpongeBob. SpongeBob then yells in terror. No Patrick please, I'm your best friend. Patrick then replies. That's enough. He then shoots SpongeBob with the gun which causes him to bleed hyperalistic blood and he dies instantly. Patrick's laughter can be heard as the episode fades to black. I emailed my friend and told him what I saw. He didn't believe me and joked about me being a sicko. I wonder who had made this clip and why. I went back to the website and entered the live chat. I asked about the episode I had seen. A number of people said they had seen the same video. At least I'm not the only one who has those images burned into my memory.


So there you go. Two lintels are set. I wish all jobs weren't like this. This has been one of the easiest little jobs we've ever done. It is after break and we're ready to get bashing. I don't even have one seat. This whole corner's been rebuilt. And we're back on the lintel job. Because I make Malcolm's bean. Always nice to work in a proper scaffold. Let's go up and have a look. Just the M6 down there. Down in the dip. There's the speed cameras. Yeah this is great this. You can see where it's lifting. Not a problem. Wonderful customer done. Thank you Don. Let's tuck all the faces. Let's off it off. You see there's some block on the top. I think they'd go the width of the cavity so we'll have to slot in underneath those. But that's not a problem. There's all the dust from last week blown up here. So yeah. Today we're going to do these two. Tomorrow we're going to do this one. That lintel's at home. You see where the lifting is. So that's all going to be sorted. So we're going to pretty much going to redo the whole top of this section. And then we're going to put trays in as well. Even though there's only speakers off to the roof. We're still going to put new trays and everything in. So yeah. I was a bit disappointed that's solid. But we can get over that. That's not a problem. So yeah. We'll get ourselves organised. And get cracking. As we're obviously on this big scaffold today. We're going to do the thing that we used to do on site. Which is we have a rope with an old profile clip attached to it. And it's going to be our hook. So we're going to have a little pulley system to get all this stuff up. Instead of hoitering it up and down. We're just going to hook it on. So that'll be the plan.  So catch you in a minute. Although the block work is trapping our brick work in. A quick sweep through with the reciprocating saw. Gets everything nice and loose. These concrete lintels again. And here's our angle line. I don't know how it's got so rusty up here. Because it's like there's an eight inch overhang there. But anyway, it has got rusty. You've probably got just less than a hundred mil seat in there. Seemed there. No, it's about 70, 80. Even less. So yeah, that's going. I've took the brick work right back to the corner. Because our lintel's going to come to here. So our upstand has to come up here. We'll just fettle it around in the corner there somewhere. And then I think from what it's worth. Because it's only a two brick pillar. Two and a half brick pillar. We'll just go right through with the tray. But one solid tray.  Probably just tack it into those blocks actually. And there you go. That's all we're changing it with. Oh yeah. Thinner galvanised. Thinner galvanised. But a nice big upstand for the strength. Not that it's holding much. It's holding three coats of brick. So yeah, let's take this down with us and go and get a brick.  So this trim. I'll have to sort something out with this trim. Yeah, so there you go. I keep saying this but I think these are these boot lintels. Weird looking things because it's like hollow. But we put insulation in there as we go. There's our other lintels. That's as far as we've gone today. That's tomorrow's job, that side. I'm going to check this for plumb. If it's not very plumb I'm going to rebuild a little corner here. It's just straight. The bricks are actually horrendous. Back when this was built. I'm pretty sure the bricks weren't delivered in packs.  So they're all chipped and in the right state. They're not good. Yeah, let's just cut the cavity tray, which we're going to slot under here. What else? Oh yeah, I was going to show you the lintel. This one we just took out. Looks like they put a liquor paint on before they put the windows in just to protect it, which has worked. This is where it's seated. The seating here is about 120. Seating this in was probably about 90 with that. So why don't you just put it central? Have an equal seating. You can see the old red-leaf paint. It's done its duty on the upstand, but where the water's getting in on the face work. And that's the bottom of it. First lintel's in. Where's the house's level? We haven't done anything. We've set it right. It's like rough as anything, but it's nice level anyway. The beds could have been a bit too high or something. I'll have that. I'll have it. You won't, I will. So there you go. Two lintels are set. I wish all the jobs went like this. This has been one of the easiest little jobs we've ever done. Pardon? Yeah. But yeah, the bricks came out nicely. No need for propping. I didn't even clean off these bricks. Dad managed to do them with the hammer and chisel. They were that easy to come off. So yeah, it's been a nice change of pace from some of the usual... It's a done customer. Sorry? I thought it was a done customer. Yeah. Yeah, yeah. I forgot to mention that. So yeah, the customer took all the facial boards off for us. So big thank you to him. He's made our whole job here a lot easier. Just in general, he's been moving stuff for us. He's been getting stuff sorted. It's always nice when the customers are... Yeah. ...to get the form creased and then you can... Just trying to get this creased out. Oh, there's the lintel. There you go, and the tray sits out like Dad said, because it's so close together. Maybe we'll just have one continuous one. We'll get all our events in and whatever. You know the song and dance. Let's see. There you go, get a better look. There we have it. So we'll just use up that mix and have a quick lunch and then we'll get this plastered out for the end of the day. This whole corn has been re-built. Sorry about the wind. So there you go. Over, new lintels, new tray. All nicely re-pointed, nice and tight. The whole back of the house is wonky. So you just have to follow it where we could. And here's all the week events just for the doubts. Seven. Two separate lintels, one continuous tray. That's an upstand there. That brick hasn't been set. See the upstand there? Just there. So what we're going to do is, when this one comes along, I'm going to put another upstand against that. So there'll be two week events together. But the two trays will meet there. So rather than trying to do a whole straight right across the back. So that's it. I'm not very pretty to follow this brick work, as you can see. You can see they've got low enough to pick up. That's it for today. It's the end of our Sunday. We're back on the extension job tomorrow to get finished, to get the steels on, get the two course on the front of the pike up, and then back here Monday to get this one cracked off. Get it? And we'll catch you in the next one.


Hi, I'm Paul Fantomet, various internet mediums, and Prometheus operator and Cube Prometheus maintainer. Also, I'm a senior software engineer at timescale working on TOPS and promscape projects. And at some time, I got this problem that I had to solve that is familiar probably to everyone. I need to get some data from some service that I don't have really access to the code, and I need to get that data into Prometheus server.  Unfortunately, of course not. And 404, so I went to documentation and then found that there's, hey, there's this get monitors method that you can get some data, and it actually returns some JSON, some JSON file, JSON format. But the API in question here was actually the uptime robot. I don't know if you're familiar, this is the external service that you can point and do synthetic monitoring something about your services. And the API is actually requiring you to do post HTTP request to get some JSON data. But how do I get JSON into Prometheus? It's still not Prometheus format. It's a JSON format, and I don't know why it's extras. But I went to Twitter and, hey, it's Julian saying that there is this thing called JSON exporter that can turn your REST endpoints and REST data into Prometheus data, and there's an example for using Twitter followers, putting that into Prometheus and graphing on a Grafana dashboard. So I did exactly that, apart from the fact that there are already like two or three uptime robot exporters, I didn't want to use them, so I used JSON exporter. And JSON exporter is just one configuration file that can transform your JSON object into actual metrics using JSON path. And that file actually shows how do you extract the data? How do you ask? You can say, what are your headers for the request? What is the body content for the request? And how do you get that metrics from the JSON object? And in this case, we can define some name for the metric, it's actually the prefix. Then you say in JSON path format, how do you extract the data from the JSON itself? And you can put some dynamic labels on your metrics using the values from the JSON. And on top of that, it's like you can use the same object to create multiple metrics by defining values and where you can find them. However, not all APIs are perfect, and sometimes you want to probably normalize that data. In my case, I wanted to have a simple status, is it up or not? Is it down? And unfortunately, that API says up is two and down is nine or something like that. So we can use recording rules from infuse to normalize that data to zero or one. And yeah, it worked. It actually worked. I get my instances from monitoring from uptime robot. I got them into Prometheus. And I figured, yeah, that's not enough. I'm running everything in Kubernetes. How do I put that configuration into Kubernetes? It's easy to put JSON exporter into Kubernetes, but how did I get that data with Prometheus? And I'm using Prometheus operator, there are two CRs that you can use. First is service monitor, which you can use for gathering JSON exporter data from the JSON exporter itself. And second is a probe CR, which was primarily designed to use in black box exporter scenarios. However, it works with SNMP exporter, with black box exporter, with JSON exporter, and probably overs that are fitting into the same category. And you can do that transformation. You can get that data from Prometheus, using Prometheus operator. And what was the goal? The goal was to create that particular alert. This is an alert that is taking data from black box exporter running inside the cluster, joining the data from external sources, in this case, uptime robot, joining them together. And if it's down, well, I get a page, because it's critical. Probably at some point, it will be running on Pura, because, well, SLOs. And if you do have any questions, catch me at KubeCon, DM me on Slack on Twitter.  Thank you.