(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)