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