# WPF Rubik's Cube

By , 31 Jan 2012

Prize winner in Competition "Best VB.NET article of February 2012"

## Introduction

I find the Rubik's Cube to be a really captivating and fascinating puzzle and since WPF has 3-D capabilities, I decided to try making a Rubik's cube application that would closely replicate the real thing, both visually and interactively.

## Background

3-D support in WPF is not designed to provide full-featured game development capabilities but it is possible to emulate some simple games/toys, like the Rubik's cube. Please note that this article is not an introduction to 3-D graphics support in WPF, so I hope that you are at least conversant with the relevant details in this area. (I'm not exactly familiar with all the intricate details of 3-D support in WPF but I know enough to have made this application). If you are unfamiliar with the details of WPF 3-D support, or want to know more, then you can take a look at the various resources that are available online, like here on CodeProject.

Another thing that I hope you are also familiar with is some of the cubing terms. Just in case, this is how you denote the faces of a cube,

## Using the Application

To turn a layer of the cube hold down the left mouse button and swipe in the direction you intend to rotate the layer. Once you let go of the left-mouse button the layer will rotate as intended.

Rotating the U layer anti-clockwise

You can also rotate the whole cube using the right-mouse button. Just hold down the right-mouse button and swipe across the intended axis of rotation. The cube will rotate once you let go off the right-mouse button.

Rotating the cube around the Z-axis

Note that to rotate a layer, or the whole cube; you only interact with the F and R faces of the cube. Also note that rotations will only occur if the swipe is along a particular layer, horizontally or vertically, and crosses two or more cubies/cubelets.

To scramble the cube, just click on the Scramble button.

## Design & Layout

Designing the cube by hand-coding XAML would have been torture. Instead I opted to model the cube in Blender. After studying a few important Blender details; like moving, rotating, scaling, and applying materials to objects, I played around with the application for a while and ended up with a model that looks like a Rubik's cube.

Modelling of the cube in progress

The final result

After modeling of the cube was complete I exported the model as a Wavefront (.obj) file and then added the object (.obj) file and the material (.mtl) file that was generated to the WPF project, in Expression Blend. The object file and the material file can be found in the 3D_Cube folder.

The material and object files

Once the material and object file were added to the project it was just a matter of right-clicking the object file and selecting Insert, from the context-menu, to add the 3-D content to the artboard, in Expression Blend. The `Viewport3D` object that was created contained quite a number of `ModelVisual3D` objects, including a `PerspectiveCamera` and several lights.

In order for the cube not to appear like just a black mass with coloured stickers, I added a good number of lights for adequate illumination.

"All of the lights, all of the lights..."

The `Viewport3D` object is overlaid with 18 `Path` objects that are used to respond to mouse events. I could have opted to use `ModelUIElement3D` objects; that support input, focus, and events, but opted to stick with `ModelVisual3D` objects.

Each of the `ModelVisual3D` objects that represent cubies is named according to the colors of its 'stickers' e.g. `WGR_Cubie` is the White-Green-Red corner piece.

## The Code

The `Location` enumeration contains members representing the location of a cubie in 3-D space.

```Enum Location
' Layers viewed from U-to-D
' =========================
'
' First layer locations.
FUL
FU
FUR
RU
BUR
BU
BUL
LU
UC

' Second layer locations.
FL
FC
FR
RC
BR
BC
BL
LC

' Last layer locations.
FDL
FD
FDR
RD
BDR
BD
BDL
LD
DC
End Enum
```

Each cubie in the `Viewport3D` is associated with an object of class `Cubie`, which contains methods for rotating the cubelet around a particular axis,

```Imports System.Windows.Media.Media3D
Imports System.Windows.Media.Animation

Public Class Cubie

Public cubelet As ModelVisual3D

Private _cubieLocation As Location

Private axisPoint As New Point3D(0, 0, 0)
Private axisAngleRtn3D As New AxisAngleRotation3D(New Vector3D(0, 0, 1), 0)

Private dblAnim As DoubleAnimation
Private rtnTrans3D As RotateTransform3D
Private transGroup As New Transform3DGroup

Private milliSec As Short = 280

''' <summary>
''' Location of the cubie in 3D space; in terms
''' of an enum Location value.
''' </summary>
Friend Property CubieLocation() As Location
Get
Return _cubieLocation
End Get
Set(ByVal value As Location)
_cubieLocation = value
End Set
End Property

''' <summary>
''' Rotate cubie around the X-axis.
''' </summary>
''' <param name="angle">The angle of rotation; -90 or 90.</param>
Public Sub RotateAround_X_Axis(ByVal angle As Double)
axisAngleRtn3D = New AxisAngleRotation3D(New Vector3D(1, 0, 0), 0)
rtnTrans3D = New RotateTransform3D(axisAngleRtn3D, axisPoint)
dblAnim = New DoubleAnimation(CDbl(angle), TimeSpan.FromMilliseconds(milliSec), FillBehavior.HoldEnd)

axisAngleRtn3D.BeginAnimation(AxisAngleRotation3D.AngleProperty, dblAnim)
cubelet.Transform = transGroup

' Change value of _cubeLocation accordingly.
ChangeLocationOn_X_AxisRtn(angle)
End Sub

''' <summary>
''' Rotate cubie around the Y-axis.
''' </summary>
''' <param name="angle">The angle of rotation; -90 or 90.</param>
Public Sub RotateAround_Y_Axis(ByVal angle As Double)
axisAngleRtn3D = New AxisAngleRotation3D(New Vector3D(0, 1, 0), 0)
rtnTrans3D = New RotateTransform3D(axisAngleRtn3D, axisPoint)
dblAnim = New DoubleAnimation(CDbl(angle), TimeSpan.FromMilliseconds(milliSec), FillBehavior.HoldEnd)

axisAngleRtn3D.BeginAnimation(AxisAngleRotation3D.AngleProperty, dblAnim)
cubelet.Transform = transGroup

' Change value of _cubeLocation accordingly.
ChangeLocationOn_Y_AxisRtn(angle)
End Sub

''' <summary>
''' Rotate cubie around the Z-axis.
''' </summary>
''' <param name="angle">The angle of rotation; -90 or 90.</param>
Public Sub RotateAround_Z_Axis(ByVal angle As Double)
axisAngleRtn3D = New AxisAngleRotation3D(New Vector3D(0, 0, 1), 0)
rtnTrans3D = New RotateTransform3D(axisAngleRtn3D, axisPoint)
dblAnim = New DoubleAnimation(CDbl(angle), TimeSpan.FromMilliseconds(milliSec), FillBehavior.HoldEnd)

axisAngleRtn3D.BeginAnimation(AxisAngleRotation3D.AngleProperty, dblAnim)
cubelet.Transform = transGroup

' Change value of _cubeLocation accordingly.
ChangeLocationOn_Z_AxisRtn(angle)
End Sub

Private Sub ChangeLocationOn_X_AxisRtn(ByVal angle As Integer)
' Looking from R-to-L
' ===================
' 90 degree angle (anti-clockwise rotation).
If angle > 0 Then
Select Case _cubieLocation
' First layer.
Case Location.FUR
_cubieLocation = Location.FDR
Case Location.RU
_cubieLocation = Location.FR
Case Location.BUR
_cubieLocation = Location.FUR
Case Location.BR
_cubieLocation = Location.RU
Case Location.BDR
_cubieLocation = Location.BUR
Case Location.RD
_cubieLocation = Location.BR
Case Location.FDR
_cubieLocation = Location.BDR
Case Location.FR
_cubieLocation = Location.RD
...
End Select
Else
' -90 degree angle (clockwise rotation).
Select Case _cubieLocation
' First layer.
Case Location.FUR
_cubieLocation = Location.BUR
Case Location.RU
_cubieLocation = Location.BR
Case Location.BUR
_cubieLocation = Location.BDR
Case Location.BR
_cubieLocation = Location.RD
Case Location.BDR
_cubieLocation = Location.FDR
Case Location.RD
_cubieLocation = Location.FR
Case Location.FDR
_cubieLocation = Location.FUR
Case Location.FR
_cubieLocation = Location.RU
...
End Select
End If
End Sub

Private Sub ChangeLocationOn_Y_AxisRtn(ByVal angle As Integer)
' Looking from U-to-D
' ===================
' 90 degree angle (anti-clockwise rotation).
If angle > 0 Then
Select Case _cubieLocation
...
' Second layer.
Case Location.FL
_cubieLocation = Location.FR
Case Location.FC
_cubieLocation = Location.RC
Case Location.FR
_cubieLocation = Location.BR
Case Location.RC
_cubieLocation = Location.BC
Case Location.BR
_cubieLocation = Location.BL
Case Location.BC
_cubieLocation = Location.LC
Case Location.BL
_cubieLocation = Location.FL
Case Location.LC
_cubieLocation = Location.FC
...
End Select
Else
' -90 degree angle (clockwise rotation).
Select Case _cubieLocation
...
' Second layer.
Case Location.FL
_cubieLocation = Location.BL
Case Location.FC
_cubieLocation = Location.LC
Case Location.FR
_cubieLocation = Location.FL
Case Location.RC
_cubieLocation = Location.FC
Case Location.BR
_cubieLocation = Location.FR
Case Location.BC
_cubieLocation = Location.RC
Case Location.BL
_cubieLocation = Location.BR
Case Location.LC
_cubieLocation = Location.BC
...
End Select
End If
End Sub

Private Sub ChangeLocationOn_Z_AxisRtn(ByVal angle As Integer)
' Looking from F-to-B
' ===================
' 90 degree angle (anti-clockwise rotation).
If angle > 0 Then
Select Case _cubieLocation
...
' Third layer.
Case Location.BUL
_cubieLocation = Location.BDL
Case Location.BU
_cubieLocation = Location.BL
Case Location.BUR
_cubieLocation = Location.BUL
Case Location.BR
_cubieLocation = Location.BU
Case Location.BDR
_cubieLocation = Location.BUR
Case Location.BD
_cubieLocation = Location.BR
Case Location.BDL
_cubieLocation = Location.BDR
Case Location.BL
_cubieLocation = Location.BD
End Select
Else
' -90 degree angle (clockwise rotation).
Select Case _cubieLocation
...
' Third layer.
Case Location.BUL
_cubieLocation = Location.BUR
Case Location.BU
_cubieLocation = Location.BR
Case Location.BUR
_cubieLocation = Location.BDR
Case Location.BR
_cubieLocation = Location.BD
Case Location.BDR
_cubieLocation = Location.BDL
Case Location.BD
_cubieLocation = Location.BL
Case Location.BDL
_cubieLocation = Location.BUL
Case Location.BL
_cubieLocation = Location.BU
End Select
End If
End Sub
End Class
```

The `ModelVisual3D` objects that make up a particular cubie are associated with a `Cubie` object when the `PopulateList()` method in class `Rotater` is called by its constructor.

```    Private Sub PopulateList()
' Layers viewed from U-to-D
' =========================
' CubieLocations are the initial locations of cubies
' in 3D space with White-U, Green-F, & Red-R.
' ==================================================
'
' First layer.
cubiesList.Add(New Cubie With {.CubieLocation = Location.FUL, .cubelet = mainWin.WGO_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.FU, .cubelet = mainWin.WG_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.FUR, .cubelet = mainWin.WGR_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.RU, .cubelet = mainWin.WR_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.BUR, .cubelet = mainWin.WRB_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.BU, .cubelet = mainWin.WB_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.BUL, .cubelet = mainWin.WBO_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.LU, .cubelet = mainWin.WO_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.UC, .cubelet = mainWin.WC_Cubie})

' Second layer.
cubiesList.Add(New Cubie With {.CubieLocation = Location.FL, .cubelet = mainWin.GO_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.FC, .cubelet = mainWin.GC_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.FR, .cubelet = mainWin.GR_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.RC, .cubelet = mainWin.RC_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.BR, .cubelet = mainWin.RB_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.BC, .cubelet = mainWin.BC_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.BL, .cubelet = mainWin.BO_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.LC, .cubelet = mainWin.OC_Cubie})

' Third layer.
cubiesList.Add(New Cubie With {.CubieLocation = Location.FDL, .cubelet = mainWin.YGO_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.FD, .cubelet = mainWin.YG_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.FDR, .cubelet = mainWin.YGR_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.RD, .cubelet = mainWin.YR_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.BDR, .cubelet = mainWin.YRB_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.BD, .cubelet = mainWin.YB_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.BDL, .cubelet = mainWin.YBO_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.LD, .cubelet = mainWin.YO_Cubie})
cubiesList.Add(New Cubie With {.CubieLocation = Location.DC, .cubelet = mainWin.YC_Cubie})
End Sub
```

As I mentioned earlier, the `Path` objects that overlay the `Viewport3D` are used to detect mouse events in order to carry out the required rotation.

```    Private Sub Paths_PreviewMouseLeftButtonDown(ByVal sender As Object, _
ByVal e As System.Windows.Input.MouseButtonEventArgs) _
Handles FU_1.PreviewMouseLeftButtonDown, FU_2.PreviewMouseLeftButtonDown, FU_3.PreviewMouseLeftButtonDown, _
FM_1.PreviewMouseLeftButtonDown, FM_2.PreviewMouseLeftButtonDown, FM_3.PreviewMouseLeftButtonDown, _
FD_1.PreviewMouseLeftButtonDown, FD_2.PreviewMouseLeftButtonDown, FD_3.PreviewMouseLeftButtonDown, _
RU_1.PreviewMouseLeftButtonDown, RU_2.PreviewMouseLeftButtonDown, RU_3.PreviewMouseLeftButtonDown, _
RM_1.PreviewMouseLeftButtonDown, RM_2.PreviewMouseLeftButtonDown, RM_3.PreviewMouseLeftButtonDown, _
RD_1.PreviewMouseLeftButtonDown, RD_2.PreviewMouseLeftButtonDown, RD_3.PreviewMouseLeftButtonDown

cPath_1 = CType(sender, Path)

End Sub

Private Sub Paths_PreviewMouseLeftButtonUp(ByVal sender As Object, _
ByVal e As System.Windows.Input.MouseButtonEventArgs) _
Handles FU_1.PreviewMouseLeftButtonUp, FU_2.PreviewMouseLeftButtonUp, FU_3.PreviewMouseLeftButtonUp, _
FM_1.PreviewMouseLeftButtonUp, FM_2.PreviewMouseLeftButtonUp, FM_3.PreviewMouseLeftButtonUp, _
FD_1.PreviewMouseLeftButtonUp, FD_2.PreviewMouseLeftButtonUp, FD_3.PreviewMouseLeftButtonUp, _
RU_1.PreviewMouseLeftButtonUp, RU_2.PreviewMouseLeftButtonUp, RU_3.PreviewMouseLeftButtonUp, _
RM_1.PreviewMouseLeftButtonUp, RM_2.PreviewMouseLeftButtonUp, RM_3.PreviewMouseLeftButtonUp, _
RD_1.PreviewMouseLeftButtonUp, RD_2.PreviewMouseLeftButtonUp, RD_3.PreviewMouseLeftButtonUp

cPath_2 = CType(sender, Path)

If (cPath_1 IsNot cPath_2) Then
rt.RotateLayer(cPath_1, cPath_2)
End If

End Sub

Private Sub Paths_PreviewMouseRightButtonDown(ByVal sender As Object, _
ByVal e As System.Windows.Input.MouseButtonEventArgs) _
Handles FU_1.PreviewMouseRightButtonDown, FU_2.PreviewMouseRightButtonDown, FU_3.PreviewMouseRightButtonDown, _
FM_1.PreviewMouseRightButtonDown, FM_2.PreviewMouseRightButtonDown, FM_3.PreviewMouseRightButtonDown, _
FD_1.PreviewMouseRightButtonDown, FD_2.PreviewMouseRightButtonDown, FD_3.PreviewMouseRightButtonDown, _
RU_1.PreviewMouseRightButtonDown, RU_2.PreviewMouseRightButtonDown, RU_3.PreviewMouseRightButtonDown, _
RM_1.PreviewMouseRightButtonDown, RM_2.PreviewMouseRightButtonDown, RM_3.PreviewMouseRightButtonDown, _
RD_1.PreviewMouseRightButtonDown, RD_2.PreviewMouseRightButtonDown, RD_3.PreviewMouseRightButtonDown

cPath_1 = CType(sender, Path)

End Sub

Private Sub Paths_PreviewMouseRightButtonUp(ByVal sender As Object, _
ByVal e As System.Windows.Input.MouseButtonEventArgs) _
Handles FU_1.PreviewMouseRightButtonUp, FU_2.PreviewMouseRightButtonUp, FU_3.PreviewMouseRightButtonUp, _
FM_1.PreviewMouseRightButtonUp, FM_2.PreviewMouseRightButtonUp, FM_3.PreviewMouseRightButtonUp, _
FD_1.PreviewMouseRightButtonUp, FD_2.PreviewMouseRightButtonUp, FD_3.PreviewMouseRightButtonUp, _
RU_1.PreviewMouseRightButtonUp, RU_2.PreviewMouseRightButtonUp, RU_3.PreviewMouseRightButtonUp, _
RM_1.PreviewMouseRightButtonUp, RM_2.PreviewMouseRightButtonUp, RM_3.PreviewMouseRightButtonUp, _
RD_1.PreviewMouseRightButtonUp, RD_2.PreviewMouseRightButtonUp, RD_3.PreviewMouseRightButtonUp

cPath_2 = CType(sender, Path)

If (cPath_1 IsNot cPath_2) Then
rt.RotateCube(cPath_1, cPath_2)
End If

End Sub
```

The `RotateLayer()` method in class `Rotater` calls several methods which determine which `Path` objects the user interacted with so as to respond accordingly.

```    Public Sub RotateLayer(ByVal path_1 As Path, ByVal path_2 As Path)
HorizontalSwipeAcross_F_Paths(path_1, path_2)
HorizontalSwipeAcross_R_Paths(path_1, path_2)
HorizontalSwipeAcross_FandR_Paths(path_1, path_2)

VerticalSwipeOn_F_Paths(path_1, path_2)
VerticalSwipeOn_R_Paths(path_1, path_2)
End Sub

''' <summary>
''' Check which horizontal layer the user intends to rotate when
''' the user swipes horizontally across the front face of the
''' cube, and rotate the layer.
''' </summary>
Private Sub HorizontalSwipeAcross_F_Paths(ByVal path_1 As Path, ByVal path_2 As Path)
' Horizontal swipe on FU... Paths; L-to-R mouse swipe.
If (path_1 Is mainWin.FU_1 AndAlso path_2 Is mainWin.FU_2) Then
RotateFU_RU_LayerAroundY_Axis(ANTICLOCKWISE_ANGLE)
ElseIf (path_1 Is mainWin.FU_1 AndAlso path_2 Is mainWin.FU_3) Then
RotateFU_RU_LayerAroundY_Axis(ANTICLOCKWISE_ANGLE)
ElseIf (path_1 Is mainWin.FU_2 AndAlso path_2 Is mainWin.FU_3) Then
RotateFU_RU_LayerAroundY_Axis(ANTICLOCKWISE_ANGLE)
End If
' Horizontal swipe on FU... Paths; R-to-L mouse swipe.
If (path_1 Is mainWin.FU_3 AndAlso path_2 Is mainWin.FU_2) Then
RotateFU_RU_LayerAroundY_Axis(CLOCKWISE_ANGLE)
ElseIf (path_1 Is mainWin.FU_3 AndAlso path_2 Is mainWin.FU_1) Then
RotateFU_RU_LayerAroundY_Axis(CLOCKWISE_ANGLE)
ElseIf (path_1 Is mainWin.FU_2 AndAlso path_2 Is mainWin.FU_1) Then
RotateFU_RU_LayerAroundY_Axis(CLOCKWISE_ANGLE)
End If
...
End Sub

''' <summary>
''' Check which horizontal layer the user intends to rotate when
''' the user swipes horizontally across the right face of the
''' cube, and rotate the layer.
''' </summary>
Private Sub HorizontalSwipeAcross_R_Paths(ByVal path_1 As Path, ByVal path_2 As Path)
...
' =======================================================
' Horizontal swipe on RM... Paths; L-to-R mouse swipe.
If (path_1 Is mainWin.RM_1 AndAlso path_2 Is mainWin.RM_2) Then
RotateFM_RM_LayerAroundY_Axis(ANTICLOCKWISE_ANGLE)
ElseIf (path_1 Is mainWin.RM_1 AndAlso path_2 Is mainWin.RM_3) Then
RotateFM_RM_LayerAroundY_Axis(ANTICLOCKWISE_ANGLE)
ElseIf (path_1 Is mainWin.RM_2 AndAlso path_2 Is mainWin.RM_3) Then
RotateFM_RM_LayerAroundY_Axis(ANTICLOCKWISE_ANGLE)
End If
' Horizontal swipe on RM... Paths; R-to-L mouse swipe.
If (path_1 Is mainWin.RM_3 AndAlso path_2 Is mainWin.RM_2) Then
RotateFM_RM_LayerAroundY_Axis(CLOCKWISE_ANGLE)
ElseIf (path_1 Is mainWin.RM_3 AndAlso path_2 Is mainWin.RM_1) Then
RotateFM_RM_LayerAroundY_Axis(CLOCKWISE_ANGLE)
ElseIf (path_1 Is mainWin.RM_2 AndAlso path_2 Is mainWin.RM_1) Then
RotateFM_RM_LayerAroundY_Axis(CLOCKWISE_ANGLE)
End If
...
End Sub

''' <summary>
''' Check for horizontal swipes that cross from the front face of
''' the cube to the right face, and vice-versa, and rotate the
''' appopriate layer.
''' </summary>
Private Sub HorizontalSwipeAcross_FandR_Paths(ByVal path_1 As Path, ByVal path_2 As Path)
' Horizontal swipe crossing from FU Paths to RU Paths
' and vice-versa.
If (path_1 Is mainWin.FU_2 AndAlso path_2 Is mainWin.RU_1) Then
RotateFU_RU_LayerAroundY_Axis(ANTICLOCKWISE_ANGLE)
ElseIf (path_1 Is mainWin.FU_3 AndAlso path_2 Is mainWin.RU_1) Then
RotateFU_RU_LayerAroundY_Axis(ANTICLOCKWISE_ANGLE)
ElseIf (path_1 Is mainWin.RU_2 AndAlso path_2 Is mainWin.FU_3) Then
RotateFU_RU_LayerAroundY_Axis(CLOCKWISE_ANGLE)
ElseIf (path_1 Is mainWin.RU_1 AndAlso path_2 Is mainWin.FU_3) Then
RotateFU_RU_LayerAroundY_Axis(CLOCKWISE_ANGLE)
End If
...
End Sub
```

The methods called by the various methods highlighted above determine which cubies to rotate to create the illusion of a layer rotation e.g. the following methods lead to layer rotation around the Y-axis,

```    Public Sub RotateFU_RU_LayerAroundY_Axis(ByVal angle As Double)
Dim cubiesToMove = cubiesList.Where _
(Function(c) c.CubieLocation = Location.FUL Or c.CubieLocation = Location.FU Or _
c.CubieLocation = Location.FUR Or c.CubieLocation = Location.RU Or _
c.CubieLocation = Location.BUR Or c.CubieLocation = Location.BU Or _
c.CubieLocation = Location.BUL Or c.CubieLocation = Location.LU Or _
c.CubieLocation = Location.UC)

For Each c In cubiesToMove
c.RotateAround_Y_Axis(angle)
Next
End Sub

Private Sub RotateFM_RM_LayerAroundY_Axis(ByVal angle As Double)
Dim cubiesToMove = cubiesList.Where _
(Function(c) c.CubieLocation = Location.FL Or c.CubieLocation = Location.FC Or _
c.CubieLocation = Location.FR Or c.CubieLocation = Location.RC Or _
c.CubieLocation = Location.BR Or c.CubieLocation = Location.BC Or _
c.CubieLocation = Location.BL Or c.CubieLocation = Location.LC)

For Each c In cubiesToMove
c.RotateAround_Y_Axis(angle)
Next
End Sub

Public Sub RotateFD_RD_LayerAroundY_Axis(ByVal angle As Double)
Dim cubiesToMove = cubiesList.Where _
(Function(c) c.CubieLocation = Location.FDL Or c.CubieLocation = Location.FD Or _
c.CubieLocation = Location.FDR Or c.CubieLocation = Location.RD Or _
c.CubieLocation = Location.BDR Or c.CubieLocation = Location.BD Or _
c.CubieLocation = Location.BDL Or c.CubieLocation = Location.LD Or _
c.CubieLocation = Location.DC)

For Each c In cubiesToMove
c.RotateAround_Y_Axis(angle)
Next
End Sub
```

The class `Scrambler` deals with the scrambling of the cube.

```Imports System.Windows.Threading

Public Class Scrambler

Private rtr As Rotater
Private mainWin As MainWindow

Private rotations() As String = {"F", "F'", "F2", "R", "R'", "R2", _
"B", "B'", "B2", "L", "L'", "L2", _
"U", "U'", "U2", "D", "D'", "D2"}

Private rndIndex As Integer = -1
Private index As Integer
Private listIndex As Integer
Private isScrambling As Boolean

Private Const CLOCKWISE_ANGLE As Double = -90
Private Const ANTICLOCKWISE_ANGLE As Double = 90

Private rnd As New Random
Private scrambleTimer As DispatcherTimer
Private rotationsList As New List(Of String)

Public Sub New(ByRef rt As Rotater, ByRef win As MainWindow)
rtr = rt
mainWin = win

scrambleTimer = New DispatcherTimer
scrambleTimer.Interval = New TimeSpan(0, 0, 0, 0, 400)
End Sub

Public Sub ScrambleCube()
If (isScrambling = False) Then
rotationsList.Clear()

For i As Integer = 0 To 24
PopulateRotationsList()
Next
' Disable Grid containing the Viewport and Path
' elements for rotating layers.
mainWin.ViewportAndCanvasGrid.IsEnabled = False

isScrambling = True
scrambleTimer.Start()
Else
MessageBox.Show("Scrambling of cube already in progress", _
"WPF Rubiks", MessageBoxButton.OK, MessageBoxImage.Exclamation)
End If
End Sub

''' <summary>
''' Populate rotationsList with disimillar Strings following
''' each other, and the next String in the List is not an
''' inverse or double of the preceding String e.g. F' does
''' not come after F, or F2 after F.
''' </summary>
Private Sub PopulateRotationsList()
Dim n As Integer = rnd.Next(0, 18)
Dim lastString As String = String.Empty

If (rotationsList.Count > 0) Then
lastString = rotationsList(rotationsList.Count - 1)
End If

If (n <> rndIndex) Then
rndIndex = n
If (lastString = "F" AndAlso rotations(rndIndex) = "F'") Then
PopulateRotationsList()
ElseIf (lastString = "F'" AndAlso rotations(rndIndex) = "F") Then
PopulateRotationsList()
ElseIf (lastString = "F" AndAlso rotations(rndIndex) = "F2") Then
PopulateRotationsList()
ElseIf (lastString = "F'" AndAlso rotations(rndIndex) = "F2") Then
PopulateRotationsList()
ElseIf (lastString = "F2" AndAlso rotations(rndIndex) = "F") Then
PopulateRotationsList()
ElseIf (lastString = "F2" AndAlso rotations(rndIndex) = "F'") Then
PopulateRotationsList()
...
'======================
ElseIf (lastString = "L" AndAlso rotations(rndIndex) = "L'") Then
PopulateRotationsList()
ElseIf (lastString = "L'" AndAlso rotations(rndIndex) = "L") Then
PopulateRotationsList()
ElseIf (lastString = "L" AndAlso rotations(rndIndex) = "L2") Then
PopulateRotationsList()
ElseIf (lastString = "L'" AndAlso rotations(rndIndex) = "L2") Then
PopulateRotationsList()
ElseIf (lastString = "L2" AndAlso rotations(rndIndex) = "L") Then
PopulateRotationsList()
ElseIf (lastString = "L2" AndAlso rotations(rndIndex) = "L'") Then
PopulateRotationsList()
...
'======================
ElseIf (lastString = "D" AndAlso rotations(rndIndex) = "D'") Then
PopulateRotationsList()
ElseIf (lastString = "D'" AndAlso rotations(rndIndex) = "D") Then
PopulateRotationsList()
ElseIf (lastString = "D" AndAlso rotations(rndIndex) = "D2") Then
PopulateRotationsList()
ElseIf (lastString = "D'" AndAlso rotations(rndIndex) = "D2") Then
PopulateRotationsList()
ElseIf (lastString = "D2" AndAlso rotations(rndIndex) = "D") Then
PopulateRotationsList()
ElseIf (lastString = "D2" AndAlso rotations(rndIndex) = "D'") Then
PopulateRotationsList()
Else
End If
Else
PopulateRotationsList()
End If
End Sub

''' <summary>
''' DispatcherTimer Tick event handler.
''' </summary>
Private Sub Timer_Tick(ByVal sender As Object, ByVal e As EventArgs)
If (index <= 24) Then
index += 1
RotateCubies()
Else
isScrambling = False
scrambleTimer.Stop()

index = 0
listIndex = 0
rndIndex = -1

mainWin.ViewportAndCanvasGrid.IsEnabled = True
End If
End Sub

Private Sub RotateCubies()

Select Case rotationsList(listIndex)
Case "F"
rtr.RotateR_1PathsLayerAroundZ_Axis(CLOCKWISE_ANGLE)
Case "F'"
rtr.RotateR_1PathsLayerAroundZ_Axis(ANTICLOCKWISE_ANGLE)
Case "F2"
rtr.RotateR_1PathsLayerAroundZ_Axis(CLOCKWISE_ANGLE)
rtr.RotateR_1PathsLayerAroundZ_Axis(CLOCKWISE_ANGLE)
...
'===================================================
Case "L"
rtr.RotateF_1PathsLayerAroundX_Axis(ANTICLOCKWISE_ANGLE)
Case "L'"
rtr.RotateF_1PathsLayerAroundX_Axis(CLOCKWISE_ANGLE)
Case "L2"
rtr.RotateF_1PathsLayerAroundX_Axis(ANTICLOCKWISE_ANGLE)
rtr.RotateF_1PathsLayerAroundX_Axis(ANTICLOCKWISE_ANGLE)
...
'=================================================
Case "D"
rtr.RotateFD_RD_LayerAroundY_Axis(ANTICLOCKWISE_ANGLE)
Case "D'"
rtr.RotateFD_RD_LayerAroundY_Axis(CLOCKWISE_ANGLE)
Case "D2"
rtr.RotateFD_RD_LayerAroundY_Axis(ANTICLOCKWISE_ANGLE)
rtr.RotateFD_RD_LayerAroundY_Axis(ANTICLOCKWISE_ANGLE)
End Select

listIndex += 1
End Sub

End Class
```

## Conclusion

That's it. I hope you'll have a fun time solving the WPF Rubik's Cube. I have scrambled and solved it several times and it is quite as thrilling as the real thing.

## History

• 1st Feb, 2012: Initial post

## About the Author

 Meshack Musundi Software Developer Kenya Member
Meshack is an avid programmer with a bias towards WPF and VB.NET. He currently resides in a small town in Kiambu county, Kenya.

Awards;
• CodeProject MVP 2013
• CodeProject MVP 2012
• Best VB.NET article of February 2013
• Best VB.NET article of October 2012
• Best VB.NET article of July 2012
• Best VB.NET article of February 2012
• Best VB.NET article of January 2012
• Best VB.NET article of November 2011
• Best VB.NET article of June 2011
• Best VB.NET article of May 2011
• Best VB.NET article of March 2011
• Best VB.NET article of February 2011
• Best VB.NET article of January 2011
• Best VB.NET article of December 2010
• Best VB.NET article of November 2010

Add a reason or comment to your vote: x
Votes of 3 or less require a comment

## Comments and Discussions

Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
 Search this forum Profile popups    Spacing RelaxedCompactTight   Noise Very HighHighMediumLowVery Low   Layout Open AllThread ViewNo JavascriptPreview   Per page 102550
 First PrevNext
 My vote of 5 Michael Haephrati 15 Mar '13 - 2:49
 Re: My vote of 5 Meshack Musundi 15 Mar '13 - 4:47
 My vote of 5 Barry Lapthorn 25 Feb '13 - 1:57
 Re: My vote of 5 Meshack Musundi 26 Feb '13 - 2:42
 My vote of 5 Duncan Edwards Jones 30 Dec '12 - 9:40
 Re: My vote of 5 Meshack Musundi 30 Dec '12 - 19:37
 My vote of 5 Tarun Y Mangukiya 11 Aug '12 - 16:56
 Re: My vote of 5 Meshack Musundi 20 Aug '12 - 20:50
 My vote of 5 Farhan Ghumra 22 Jun '12 - 2:07
 Re: My vote of 5 Meshack Musundi 26 Jul '12 - 21:49
 My vote of 5 michael azzar 1 Jun '12 - 5:20
 You're very Distinctive [modified] michael azzar 1 Jun '12 - 5:18
 I note you are very Distinctive at visual Basic .. I wish to be just like you..?I love Visual Basic so much.-- modified 1 Jun '12 - 11:28. Sign In·View Thread·Permalink
 Re: You're very Distinctive Meshack Musundi 1 Jun '12 - 21:18
 Re: You're very Distinctive michael azzar 2 Jun '12 - 10:49
 Rubik's Cube VishalvPatil 17 Apr '12 - 5:30
 Re: Rubik's Cube Meshack Musundi 17 Apr '12 - 20:33
 My vote of 5 G-Tek 3 Apr '12 - 3:49
 Re: My vote of 5 Meshack Musundi 12 Apr '12 - 4:25
 My vote of 5 Brian Pendleton 2 Apr '12 - 7:39
 Re: My vote of 5 Meshack Musundi 12 Apr '12 - 4:22
 My vote of 5 Reza Ahmadi 2 Apr '12 - 5:45
 Re: My vote of 5 Meshack Musundi 12 Apr '12 - 4:21
 My vote of 5 manoj kumar choubey 19 Mar '12 - 19:35
 Re: My vote of 5 Meshack Musundi 20 Mar '12 - 23:09
 My vote of 5 Polinia 13 Mar '12 - 1:56
 Last Visit: 31 Dec '99 - 18:00     Last Update: 18 May '13 - 14:15 Refresh 1234 Next »

General    News    Suggestion    Question    Bug    Answer    Joke    Rant    Admin

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 1 Feb 2012
Article Copyright 2012 by Meshack Musundi