Coding Challenge #142.2: Rubik's Cube Part 2

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
(whistle whistles) - Hello and welcome to part two of the Rubik's Cube coding challenge. In this version of the coding challenge, I just want to take my existing Rubik's Cube simulation and be able to make some turns, I want to shift the faces of one side of the cubies, like the yellow faces, to turn maybe clockwise and be on the other side and everything follows suit. So I'm going to do that with matrix transformations. Here's the thing, I have to be honest with you, this is my second attempt at doing this. I did a previous livestream where I went down a different road and I was keeping track of arrays and different arrays and trying to shift things and have custom hard coded things for every possible way the Rubik's Cube could turn and it was a total disaster. In the end I actually got it to work, you can see it spinning around and working here right now. I had some really useful suggestions, most notably from Michael Raphael Panganiban, who suggested that I use matrix transformations. And I'm going to try this again, I'm going to try to really think about refactoring that gobbledygook code I wrote before and try to set myself up for success in the future. I also want to highlight this website, I Am the Cube, made by Stewart Smith and other collaborators from Google. This was made for the Google Doodle about the Rubik's Cube a bunch of years ago, and it's really phenomenal. The code is available. I really should just not bother doing what I'm doing and just play with this, but I have a mission. And my mission is to make this Rubik's Cube turn and move. And so I'm going to do that. This is what I left off with in Part One. And one of the things that I did in Part One that was a little bit weird is I really was thinking in terms of cube notation. And if you're a cuber, you look at the cube in a certain direction, you're thinking about what's up, what's down, what's left, what's right, what's clockwise, what's counter-clockwise. And that really led me in some strange directions. I think it's useful maybe to double back and add that in once I have the cube working. But the thing that I'm actually working with here in this code, x-axis, y-zxis, z-axis. And it's going to be much simpler for me I think to keep track of things and think in those terms. And the rotation might not be clockwise or counter-clockwise, it might be in the positive direction or in the negative direction. The first thing that I want to do to really figure this out and to have a more effective way is actually keep track of the cube's location not in a vector, but in a matrix. Dun dun dun. So I have done a lot of videos about matrices and matrix math and some of that's going to come up here. The ones that you might want to check out if that's a totally new concept to you is the video where I made my own 3D renderer using matrix transformations. And I made a little like bunch of functions to do to store matrices and to multiply them. I also used matrices in a bunch of videos I made about neural networks. The matrices pop up there in the math of machine learning with neural networks. But here, what I want to do is I want to have a transformation matrix for each one of the cubies to keep track of where it is in the world. And so, the way that I'm going to do that is I'm going to change the cubie to receive a matrix and for its position to be that matrix rather than a PVector. Luckily for me, Processing already has built into it matrix classes. So I don't have to write my own matrix 3D object, I don't have to write a matrix 2D object, I've got one in the Processing. So, it's going to be a PMatrix3D object. The other thing I want to do now, so this is how I made the cubes before, I made each cubie with an x, y, and z. But now I want to do this. I want to make each cubie with a matrix, and then what I want to do is translate that matrix. So in other words, each cube, it's a way each cube is going to keep track of its own 3D world, and it has the full matrix of what is the translation, what is the rotation, what is the scale. And so if I translate that matrix to its x, y, z location, then I'm initializing the cubie with that particular matrix. Some other things, while this nested loop is useful, it's going to be much simpler for me in the end I think if I actually keep all of the cubies in just a single array. The multidimensional array is interesting, but let me, so let me change that to, I'm just going to make it, no, I'm not going to make this i, I'm going to make this like index, oh actually, let me make this, I'll make this index. And I'm gnona say cube index equals new cubie with the matrix, index plus plus, so that I'm counting. And then, this will now be a single array that has dimensions times dimensions times dimensions. Three times three times three, or 27. Of course, there really isn't a cubie in the center, but eh, you can't see it anyway, so we can imagine it's there. Now the other thing that's really important here, and I'm going to, I think this will be helpful. I'm going to change this to x, y, and z. X, y, and z. I'm going to change this to x, y, and z. I'm not going to worry about this figuring out the actual position of the cube with its length. I just want its position in a three-dimensional world. And what are those positions? So if I just think about one face of the cube, for example, maybe this is a face of the cube where z equals one. If we're looking at the cube with green facing us, facing you, this could be the z-axis, this is z equals one. So we've got the x-axis along the horizontal and the y-axis along the vertical. So what I could think about this is really the location x, y, zero, zero, so it's zero, zero, one. So all of these have a z location of one. But this one is negative one, negative one. Sorry, zero, negative one, one, negative one. The y is negative one, the x is negative one, zero, one. So this is negative one, zero, and this is one, zero. This is negative one, one. This is zero, one, and this is one, one. So these are all the locations, and these can actually be the x, y, z values that I apply to translate each individual cubelet's matrix. So if I come back to the code, what I'm doing here is I'm saying, have the x goes from negative one all the way to one. Same thing for the y. Same thing for z. Create a matrix for each one. Create the cubie, I'm not going to worry about this leg thing right now. And then say index plus plus. The cubie then is just a thing with its matrix. And this should say PMatrix3D. I've got to specifically use, I'm going to be using a 2D matrix in a little bit, you'll see this is crazy. But right now I'm going to use the (mumbles). Alright, so, I still have this length variable here. The other thing I want to do is, while it was really useful that I had this whole way of doing these quads and coloring them, I'm going to get rid of that right now. Because I think that what I want to do is think of each of these cubies as a box. And then it's also going to have faces. And I think I'll draw the faces as a separate object with a quad. So right now to keep things very simple, I'm going to show you something kind of nuts. I am going to take out all of this. I'm going to get rid of this translate. I am actually going to call this function called apply matrix. So what am I doing here, I'm taking the matrix, and I probably shouldn't call it pos as in position. Let's actually call it matrix. Let's call it matrix, each cubie has a matrix which really contains all the information about where, its position and rotation in the world of the cube itself. And this is actually the same as just calling translate at this point. But because I'm storing it in a matrix instead of calling translate directly, it's going to give me some more possibilities later as I need to keep track of all these cubies separately or in smaller groups and that kind of thing. So if I do this, and then if I just say box, and I'm going to say box one, it's going to be a small box, pop matrix, then, and here, the nice thing about this also is I can change this to cube dot length and get rid of this nested loop. I can say cube index I dot show, and hope that we see the Rubik's Cube. Ah, where is it? Oh look, I think it's there. I think that's the Rubik's Cube. Oh, look, I visualized the black hole on the day of the, this is the second image of a black hole, no, okay. So the reason why it's so tiny is because scale. So let me say scale 100, and let's see what happens now. Where's that Rubik's Cube? So let's think about, ah, stroke weight eight. Now that's lunacy here. so let's make the stroke zero, let's make the stroke weight small because I'm scaling up. And let's also make sure we say fill to (mumbles) that's there already. Oh, that's a really big one, but there it is. So I don't want to scale that much. Let's scale it by 50 and let's just say stroke weight .1. And there we go. So now, I have exactly the same thing. I have exactly what I had before, but I just have a matrix for each one of the cubies. Now I need to figure out, how am I going to debug this? Right now if I move them all around, I could move them around, they're all white, they're all going to appear in the same location. So what I'm going to do is I am going to make a variable. I'll just call it Highlight false. And I'm going to say, if highlight fill zero. And then let me say, let me say cube index zero highlight. So there we go, we can see, ah, that one. See look, this one is now highlighted. I can address any individual cubie. It's not really highlighted 'cause it's black, it sort of looks like it's removed. Maybe I should make it like red or something just so it's a bit more obvious. 'Cause everything right now is just white, okay. But let me actually highlight the one that's in the front. (bell dings) Alright, the answer by the way, in case you were wondering, was two. I'm counting with z first, so it's going zero, one, two. So perfect. Boy, that was a lot of time I spent trying to figure out that it was two, you're lucky you didn't have to watch that. Okay. So now I can see that I'm highlighting a given cubie. The next thing I want to do is take one of these faces, each one has its own 3D matrix with an x, y, and z, and rotate It. I want to apply a 2D rotation to this particular face. So any face, whether it's the front, the back, the top, the bottom, if we look at it, it ends up becoming two-dimensional. So I just want to rotate two, I just want to rotate along one particular axis. Any given face can become suddenly a 2D world that I want to tdo a rotation to. So if I suddenly have this point here, which is at negative one, negative one, and I want to rotate that relative to the center 90 degrees, or half pi, then it's going to end up where? In one, comma negative one. It's going to move and rotate there. So how can I do that? I can apply a 2D rotation. Let's write a function called turn z. And I want to avoid using rotate z, because even though I'm doing a z-axis rotation, rotate z is the built-in function in Processing. So if I say turn z, first let me look at all of the cubes. So I always need this particular, this particular loop. Just realized something, I now, I sort of regret, I'm regretting taking out that nested loop because I just want all the faces that are in the z-axis. And if I had that nested loop, I would be able to keep it that way. But actually, I'm going to do something slightly redundant, which is that I am also going to keep track in a separate variable. I am going to keep track of, I'm going back, I'm going to keep track of its x, y, and z index values. I'm going to keep track of what those values are within what would be a three-dimensional array. And so let me actually add those as arguments here. And I'm going to say x equals x, y equals y. Z equals z. And then when I create each cube, I'm going to say x, y, z. Great. So I have those extra variables because now I can say if a cube dot z equals one, I want only the cubes that are in the z-axis one, negative, zero, one, those are the ones, and this is cube index i dot z, these are the ones that I want to rotate. So now I need to make a 2D matrix, this is what I was saying, 2D matrix out of that face. So the 2D matrix is just a new 2D matrix. I want to do a 90 degree or a half pi rotation of that matrix. And then, I want to translate out to the cubes x, Spongeman on Code Pen who made a version of this. I saw used actually a really cute variable name which I'm going to use right now. qb, like the letters qb for the cubie, equals cube index i. So I can actually just use qb, it's qb z equals one, translate by qb x and qb y. Essentially what I want to do here is I need to rotate before I translate. I have this thing here, I make this matrix, I rotate the matrix, and then I translate out to here and I have my new location where x and y should be. So, I think in order to make this more clear, I want to do a little bit of console logging. 'Cause with this matrix, what's actually in there? So let me first make, I'm going to call this matrix one and I'm going to call this matrix two. And this is just for debugging purposes. And I'm going to say matrix one dot translate qb x qb y. So I want to show you, what does it look like when I just apply this translation to x and y without a rotation, and what happens when I rotate and then apply that translation? So to show you that, I could do matrix one print and matrix two print. Processing has a nice print function associated with the, a nice print function associated with matrices. And let me also just do print line something like this, and let's also, just so we can see, do print line qb x qb y. So we can see what those values are. So let me run this. Oh, I need to call this function. I'm going to call turn z right here in setup. Look at this. When the xy location is negative one, negative one, you can see it as the third column in rows one and two of this matrix. This is the matrix that keeps track of translations and rotations for a 2D world in Processing. When I rotate it 90 degrees, where does it end up? One, negative one. Negative one, negative one ends up at one, negative one, ha ha. When it's negative one, zero, it ends up at zero, negative one. Negative one, zero, rotating 90 degrees, it ends up at, where did it end up? Zero, negative one, which is right here. So you can see this rotates to here. So actually, in those matrices are the new index values of where it is in my 3D array that I'm imagining. So here we go. So I, and these values are all stored in properties of the matrix object which are indexed by its row and column. So I can basically say, hey, qb, you have a new location. Update your location to matrix two dot M, it's row, column. So zero, two, matrix. Matrix two, M two. Basically I'm getting, and I could've just done the matrix math with sine and cosine probably, but I kind of like the idea that I'm using the built-in matrix math of Processing. So you can see here that these are these two values. Update, oh, sorry. M one, two. And then, qb dot z. 'Cause I'm not affecting the z. There's no update function yet. That's a new thing I have to write, but this is what I'm doing. I'm taking the x, y, actually, I want to get rid of the debugging stuff, because this makes it so much simpler to look at. And I'm just going to call this matrix. I don't need matrix one or matrix two. What I'm doing is I am taking, I'm making a 2D matrix out of the xy, then I am rotating it, and then I am getting the new xy and replacing it in theory in my qb object. And in my qb object then, I need an update function, which does something like, first of all, I'm just going to, there might be a more thoughtful way of doing this. But I'm just going to reset the matrix. And this needs an x, a y, and a z. And I'm going to, and somebody in the chat was telling me, and I should probably get in the habit of doing this, if I have the same variable names of things, I can make sure I'm referring to the object instance variables with this dot, which is of course the thing I like to use on this channel. Let's be consistent and do it here. So I can say matrix, I don't have to say reset matrix, matrix reset, matrix translate x, y, z. And then let me update, again, I've got a serious bit of redundancy here, but let me just update those x, y, z values as well. So now, this should update, why is update not working? Oh, you know what? These values in the matrix are floats. And qb wants an in, so I'm going to, I could just convert it to an in, but I'm going to use round, because I want to make sure, I ran into this in another project, just in case it for some reason gives me like 0.999999999, if I used casting it to an integer or floor, it would make it zero. So this should work. Let's get rid of turn z here. And I'm going to add a key pressed. And I'm just going to say if key equals, just the key one, turn z. So now, alright, ready? (drum roll pattering) When I press the key one, that cubie should move over to the right. (horns trumpeting) Anticlimactic, but it works. Now here's the interesting thing. I should now be able to say give this turn z an index, which would be here. So for example, now, if I were to say turn z zero, I know you want me to use a switch statement, everybody. Turn z one. The middle is technically, while I could program it with a rotation of the middle of the cube, that's not really a thing. So I'm actually just going to use one and two to rotate zero and two. Let's highlight both cube two and cube zero, and let's see what happens. So I should now be able to, if I press one, woops. Oh, what happened here? Oh, sorry everybody, the indices are negative one and one. Negative one, zero, and one, of course, ah. Alright, let's try this again. Right, oh, is it going around? Look there, it's going around, look at that. That one's going around. It's going around, look at that. It's going around, yay. (hands clap) Yes, okay, now look at this. Now there's got to be some sort of way I could make this function into any, just like keep this functiOn generic and do any given axis. But that's too hard for me, I'm going to, this will definitely. ("Refactor" by Espen Sande Larsen) ♪ I will refactor this later ♪ ♪ You know I will refactor ♪ - Right now I'm just going to copy, paste. And I'm going to say turn y. And so now if y equals index, then what I want is x and z qb y. I don't need to actually round that. So this should be exactly the same for a y rotation, right? And then, oh, and I need, mm, I'm going to add one more thing. And then x, I'm going to just check the x-axis. And this should be y and z. Then I need to keep qb x and adjust the y and z. So I'm turning any face, like this here with turn y. Now it was kind of obvious to us that when I have the x-axis that I turn this face into a 2D matrix. But when I'm using the y-axis, eh, let's just take this face and turn it into a 2D matrix. I need the y values and the z value, I mean sorry, the x values and the z values. That's what's changing, y is fixed at negative one, one. Alright, alright. I'm going to use a switch statement. Probably going to have to come back to my if statement, but let me, I know it's just too painful, I can't bear to deal with the comments. Switch key, I'm looking at it over here on my invisible computer. Case one. No, no, case one. Turn z negative one, break. Right, and then I just keep doing this, right? Case two, turn z, one. Right? And then, alright. Now, actually, here's the thing. Let's see if I can get clockwise and counter-clockwise. There's how many moves? I could rotate in this direction or the other direction. So I'm going to consider that to be a direction in my rotate. Let me just get this to work first, then I'll add direction. Too many things. Case one and two. Case three and four, which is turn y. Case five and six, which is turn x. Is that actually a switch statement? What's the chance that's actually a switch statement that works? So one is just turning the back row. That still works. Two is turning the front row. Oh my god. Three, that's not right. Is that? So let's do something a little different. Let's give these a color. And let's do the following. Let's set the color equal to red. And the color equal to, oh, this just C. The color equal to blue. And then in the cubie itself, let's just fill it with its color. That makes much more sense. So now, I think it'll look right to us now. So if I go and do a y rotation, right, that's correct. Now here's the thing. Will this all keep working, will this continue to work, if I do multiple combinations? So let's do my z rotation where now, that's there. Now let me also rotate that there, but let me put it down here. Or let's just leave that there. Now let's do a y rotation. Yes, that still works. Let's do x. Yep, that one's going around there. And then the red one is going around. The cubes move correctly! Now that I have all this working, I need to actually color their faces correctly. So if I put the actual colors of the cube on the faces, then I can start to move those as well. So in order to do that, I have an idea. What I would like to do is I would like to make a face class. The idea of how I'm going to keep track of a face, so every single cubie will have, well, in an actual Rubik's Cube, there are center pieces which just have one face and they never move, they spin, but they just stay permanent. There are these corner pieces which have three faces. And I don't know what these are called, these little like middle pieces, I'm sure there's a tech name for them, that have two faces. I'm going to be simple about this for now and I'm just going to give every single cubie six faces. And there's going to be a lot of redundancy, like you will never see the fact that this cubie does not actually have a white face on the side, but I'm going to give it that. And the way that I'm going to define each face, each face is going to have a color. Like what is its actual color. And it is going to have a normal. So its normal is a vector that points perpendicularly, perpendicular from the face. So if it's the front-facing face, it's normal will be zero, zero, one. It's pointing forward in the z-axis. If it's the backwards-facing face, zero, zero, negative one, to the right, one, zero, zero, so on and so forth. So here in the face class, we're going to have a Pvector called normal. And then we're going to have a color called C for color. And when I make the face, I will give it a normal and a color. And I will say this dot normal equals normal. And this dot C equals C. Then every single cubie, in addition to having all this information, will have an array of faces with six faces, again, a thing that I would want to do later, and maybe you can do this in your version, is to have the correct number of faces for each cubie. But let's just do them all with six. And then in that case, I'm also just going to hard code this. So each face, faces index zero would be, so I'm going to consider the z-axis as green. Forward, so the back is blue. Negative one of is zero. So that's going to be a new face with a new Pvector that is zero, zero, negative one. And the color is zero, 0255. So this is the blue face. This is the, green face. This is (laughs) wait, up, up would be zero one zero. This would be white. Yellow, how do I do yellow? It's a little red and a little green, something like that. No, no, a lot of red and red and green, green, yeah, but this is zero. It's yellow. Yellow, so then left and right, positive right is orange. One, zero, zero is, I think that should be orangeish. Somebody in the chat will give me some better colors. And then the other side is red. So this is now me making all of the faces, great. And then, guess what I could do here in show. After I apply that matrix, I can draw the faces. For face f in faces, f dot show. And now, all I need is a function here that knows how to draw a rectangle pointed perpendicular to the normal with a particular color. So the first thing I know I need to do is fill the color. Then I need to draw a quad or a rectangle. Let's just use rectangle, let's just try, I'm just drawing a 2D rectangle. And I need to rotate it according to the normal. Let's just draw the rectangle. Rectangle at zero, zero, one comma one. Actually it's a square, I can use Processing square function. I think its size is just one. So what if I do this? Oh. Okay, first of all, let me go back to my cubie. And let's just say, no fill. There's the red one. It's in the wrong place. Oh, they're all just on top of each other. Right, of course. Oh, I do need to translate out, I do need to translate, I can translate by the normal. Oh, of course. Okay, so I can translate by the normal dot x, normal dot y, normal dot z. And I'm going to want to add push matrix and pop matrix. And let's do no stroke here. So now, that should be, there they are. There're all the faces, sort of. Are they in the right place? They kind of are, right? White is back there, yellow's there, then blue, then red, but they're all, the problem is they're all, I need a rotation. Can I rotate along the normal or something? Can I do something like say rotate normal dot x, normal dot y, normal dot z, half pi? Is that like going to work? No. Oh, maybe it's the other way, maybe half pi goes first. That's the angle and then this is the axis of rotation. Oh, oh look at this. Something's right sort of. First of all, they're, (laughs) I should be translating by normal x, half of that. So let's multiply all these by .5. Okay. (laughs) Green and blue, are those on opposite sides? Yes. White and yellow are in, the right orientation, everything's in the right orientation, but they're in the wrong place. Ah. (bell dings) Rrrrr. (laughs) negative one, that's a negative one, right? Ah. Okay, now I'm really close. Alright, well let me, I know there's a way I could do this. I was trying to avoid this. But just so I get it right. If I were to say, if a normal dot x, if the absolute value of normal dot z is greater than zero, right, if the normal is along the z-axis, I need to rotate, I actually don't need to rotate at all. The rectangle is drawn correctly. So I could say like rotate just, I could say rotate z, by half pi. Else if the absolute value of normal dot x is greater than zero, then I could rotate, I think it's by x. Else if absolute value of normal dot y is greater than zero, there's got to be a better way, but this will work, pretty sure. Then I can draw the face. (laughs) That's the same thing. (bell dings) Oops. Okay, so of course, of course, if I'm, blegh, if I'm along the x-axis, I want to do, I'm drawing the quad like, I'm always drawing the quad face forward. So if this is my quad, if I'm along here, along the x-axis, I need to actually do a y rotation. If I'm along the, yes, of course, so that's it. So what I needed to change here was not, and this doesn't, since I'm drawing it forward, I can just basically skip the whole z thing. So I only need to do a rotation if I'm on the x-axis of a y, if I'm on, if the normal is along the y-axis, rotate by x. And then, here we go, now, (bell dings) we have the Rubik's Cube. And, guess what, I can turn it. Wait, huh? So now I'm turning it. And nothing's happening. Guess what. Now that I have these faces, what happens when I actually turn a Rubik's Cube? The faces also rotate. The normals need to rotate. This is the last piece. I want to animate it turning, but this is the last piece to at least have it so that I can do all the moves. Actually I need to add directions and maybe make the keyboard controls make a little bit more sense. But. So, now what I need is I need inside the face, I need a turn x. I need a turn y. And I need a turn z. So I need to be able to rotate any given face. Once again I can use matrix transformations. Only this time, and I've done this before. I did this in my video about doing 3D rendering without the 3D renderer, with our own matrix math. And so what I need to do here, let me pull up the Wikipedia page. So here they are, these are the three rotation matrices for doing an x rotation, a y rotation, and a z rotation. And all I need to do is apply those rotations to the normal vector. So, I'm sure I could use PMatrix for this, but for whatever reason, I think I might have ane asier time just writing this out. Because ultimately what I'm saying is, let me make a new Pvector. I'll just call this V two. And the V two dot x equals the normal dot x times sine. So I need an angle here. Normal dot x times cosine of the angle minus normal dot y times sine of the angle. And then v two dot y equals normal dot x times sine of the angle plus normal dot y times cosine of the angle. And then v two dot z equals z. And then I could say normal equals v two. So I have just rotated the normal according to the x-axis. Sorry, this should be normal dot z. Now here's the thing, I'm a little worried we're going to run into some rounding weirdness. So what I probably should do is say, let's just put round around all of these. So this should be turning the face. And this math I sort of did by looking at the formula a little bit by memory, but I go through this particular formula in detail in the other matrix transformation video. So now, and somebody in the chat will point out if I may wrong. So now I should be able to take exactly this same thing and put it in turn Y. And the difference is, with turn y, I am changing x and z. So this is x times cosine, this should have also a float angle. This would be x, and this will be z. This is z, this is x, and this is z. And this is y. So that's just the matrix take and applied to x and z, because y, if I'm turning along the y-axis, y stays the same. Oh wait, this was z, by the way. I've got them in the wrong place. This was z, because z is fixed. This is y because y is fixed. And now, let me copy this into here and this will be x. I think this should turn all the normals. And if it's turning the normals, they'll display correctly. So what's a rotation that I'm doing? So how do I have this working? When I say turn z, for example, I say qb update. So I should also say here probably qb turn faces z. Let's just do that, this is, yeah, I mean this is awkward naming, but I need to, yeah, I need to turn the faces. I'll do that afterwards, it doesn't really matter which. And so then I would add a function here, turn faces z. And I would say for every face F, faces, and faces. F dot turn z half pi. What's the chance this works? Very little. Oh, wait a sec. Wait a sec. That's right. Orange goes to there goes to there goes to there goes to there, yes. Oh, that one's working too. Right, one, two, one two, one, two. One, two. (bell dings) So close, alright. Turn faces y. And again, I think, there's probably a way to refactor this so that these functions turn faces x. Okay, let's do y, which was, oh, wait wait wait wait. I need to call those. Turn faces z. Turn faces y. Turn faces x. And I forget what my key commands are. By the way, guess what, we can get rid of this thing. All this nonsense that I had before, I'm not using this anymore. My code is so much nicer. So now, where's my key commands? Let's just put this in its own tab. So right now if I do y, this should be the top four. Oh, that's the bottom, that's fine. Five is x. Three is the top. And then, can I start combining these? Yeah. I'm shuffling the cube. I think this is good. I got to do some like real testing of this to make sure this is really working. One thing I need to do is I need to add direction. So I need to be able to add a direction for turning here. So in these turn functions, I should also have a direction. So each one should get a direction. Then, the direction is half pi, direction times half pi. And turned face is z, with that direction. So rotate according to the direction. And then rotate according to the direction. So now in turn faces, these should all also have a direction. And then now, alright, so let's think about this. So z is forward. So let's use F. Hm. What's a good set of key commands for all the moves? So all the moves that I have are forward, back, up, down, right, left. So I'll use those keys. And lower case will be in a positive direction, upper case will mean negative direction. So the case f is turn z one, one. The case capital F is turn z negative one, negative one. That's forward. Then, case b would be also turn z, but one. One. And B would be one, negative one. Alright, I'm going to do this silently and this could get fast-forwarded if it needed to. (laid-back music) (bell dings) Alright, I have now put in every possible move. Front is turn z with a z of one either direction. Back is a z of negative one, either direction. Then y, then x, so then up then down is y. Then left and right is turn x. It does occur to me that I could just have a generic turn function and I give it the normal vector of the axis. Or the vector of the axis itself. But anyway, this I believe should work. Let's see. So if I want to do a front, I should just see that front face, which is the green face, turn. Which I am seeing. Back should be the back face. Up, oh, the up, is down. So up is down, whatever, 'cause the y points in the other direfction. Down is the top, ah, you know, spin it around. Up is the top, as long as I'm consistent. Down goes this way. Right, okay, now this is right. And left. Now are my clockwise and counter-clockwise things working? So front, which was the green face, goes this way, and capital F should go the other way. Yep, so now let's see. If I turn front, and then now let me do the left, which is there, that's the right. Doesn't matter, long as it's one side. It's working. I think this is good. Here's a way that I could test this. What I'm going to do now to finish off this video is run a set of moves shuffling it and then run those moves backwards. So to do that, let me get, let me make a char array. All moves is just f, front, back, up, down, left, right. And I could deal with the capitals, you know what, I should make these individual strings. So what I'm going to do, and I'm going to make a string called sequence. And what I'm going to do is in setup, I'm going to say for int i equals zero, i is less than, let's just do 10 moves. A sequence index i equals int. So let me get an index. Which is a random number between all, that's an index into the all moves array. Sequence plus equals, oh, I'm already using index. So let me just say r. Sequence plus equals all moves r. So if I do that, all movies, all moves, this would be, you can see, there should be a sequence here. You can't really see that. This is my sequence. Up, forward, right right right, left, up, right, down, back. And just for plausible deniability here, let's just say if random, I'm going to, random is less than .5. We'll do this. Otherwise we'll do that to upper case. So now I should have my sequence. And there you go, there's my sequence, down, up, right, up, down, forward, up, back, left, left. Okay. So now what I want to do is I want to run through that sequence. So I'm going to use, I'm actually going to use that int, I'm just going to use counter equals zero. And I'm going to say in draw, char move equals sequence char at counter, counter plus plus. And I'm just going to do this, if counter is less than sequence dot length. Okay. Counter is less than sequence dot length, I'm going to get the move, and then I'm going to do apply move, move. And now in controls, this should really be apply move char move. And then switch move. So I could if I want to control it by key pressed, I could still say key, apply move key. But I don't want to control it by key press, I want to control it by these moves, so let's run this. (laughs) It did it really fast. Because see, normally the rendering engine takes a minute to spin up. So I'm going to do this. Boolean started equals false. And I'm going to say in key pressed, if key equals the space, then started equals true. So if I press the space bar, start things going. And then I'm going to say here in draw, if started, so it won't do this shuffling now. It won't do the shuffling right now until I press the space bar. See, it shuffled it. But let's slow that down just so we can see it. If frame count modulus, you know, 20 equals zero, that's going to only do that every 20 frames. So I hit the space bar. And you can see it should be, I mean, it should be doing this sequence right now. It should be doing that sequence. So when it gets to the end of the sequence, I should go backwards through the sequence. So sequence is less than, so what actually, here's what I'm going to do. After I make this sequence, now, I'm going to say for int i equals zero, i is less than sequence dot length. I plus plus. Now what I want to do is say, so now I need to say that move, but the other direction. So char move, or actually, so, string next move equals sequence char at i. Oh, but I want to go backwards. So I want to go from sequence dot length minus one all the way down to zero, i minus minus. And then I want to get each one of those characters what's wrong with char at i, it's not a string. So I'll just convert it into a string. (laughs) Well, that. Can I do this to, oh, wait. Oh, how do I flip the case? Rrr. Thank you to MC Seem in the chat who just gave me a really nice suggestion. I'm going to say flip case. Sequence char at. So I'm going to write my own flip case function. It's going to be kind of ridiculous. Flip case of char, any given character c. And what I'm going to do is if, so first I need to make it a string. Sure I could just do that. If s dot low two, I know it's lower case. If it equals itself. Right? If the lower case version of a string is the same as the string, and I think I want to say it this way, if s equals s, this is just a little clearer, if s equals s to lower case, then return s to upper case. Otherwise, return s to lower case. And people were giving good suggestion about using x or flipping bits or adding the ASCII values. That would be a nice way of doing it. Apparently that's not a way you can make a string. I'll just do this, think that works. That's one way to make a string. Plus equals flip case. So, oh no, next move, yes. Next move equals, flip the case, and then sequence plus equals next move, alright? Let's have it shuffle pretty fast. Like every five frames. And then let's see what happens. (drum roll pattering) Ah, it got an error. Oh, plus equals, (laughs) we don't need press two there. Shuffle, shuffle, shuffle, shuffle, shuffle. Reverse. Ah. (horns trumpeting) Let's do, let's finish this off with just doing like 200 moves. Let's forget about slowing it down. And let's just enjoy this beauty. Let's make it full screen. Thank you for watching Part Two of my Rubik's Cube coding challenge, where I am now shuffling the cube, moving all the pieces around, then unshuffling it backwards to make it appear as if it's solving, of course it's not actually solving itself, it's just doing a sequence and then turning it back, and there we go. So the next step that I need to do is I need to be, I want to see it animate, I think it would be much more interesting to actually watch the faces themselves turn. So that's just a little animation thing, that'll come in part three. Once I have that, then I can start to think about, are there different techniques that I can try to apply to have the cube solve itself automatically? Thanks for watching this very long Part Two. If you make your own version of this, if you've figured out clever ways to refactor my code, to visualize it in interesting different ways, to make it more generic with different scales, maybe you could make one of those pyramid, it's not a cube, those Rubik's pyramids. That would be great. And I'll see you in a future video, goodbye. (bell dings) (upbeat music)
Info
Channel: The Coding Train
Views: 112,762
Rating: undefined out of 5
Keywords: daniel shiffman, coding, the coding train, coding challenge, creative coding, code challenge, programming challenge, rubiks cube, rubik's cube, rubiks cube algorithms, rubiks cube animation, rubiks cube processing, processing coding, processing coding challenge, rubiks cube patterns, rubiks cube simulation, rubiks cube 3x3x3, rubik cube, rubiks cube code, rubiks cube java, matrix rubik's, matrix math, matrix rotation, 2d matrix math, rubik's cube matrix
Id: EGmVulED_4M
Channel Id: undefined
Length: 52min 55sec (3175 seconds)
Published: Mon Apr 15 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.