Click here to Skip to main content
Click here to Skip to main content

WPF Rubik's Cube

, 31 Jan 2012
Rate this:
Please Sign up or sign in to vote.
A WPF 3-D Rubik's Cube application
Prize winner in Competition "Best VB.NET article of February 2012"

Screenshot_1.png

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,

Cube_Faces.png

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.

Layer_Rotation_1.png

Rotating the U layer anti-clockwise

Layer_Rotation_2.png

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.

Cube_Rotation_1.png

Rotating the cube around the Z-axis

Cube_Rotation_2.png

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.

Blender_Screenshot_1.png

Modelling of the cube in progress

Blender_Screenshot_2.png

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.

Solution_Explorer.png

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.

Lights_In_Blend.png

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

Paths.png

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

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

    ''' <span class="code-SummaryComment"><summary>
</span>    ''' Rotate cubie around the X-axis.
    ''' <span class="code-SummaryComment"></summary>
</span>    ''' <span class="code-SummaryComment"><param name="angle">The angle of rotation; -90 or 90.</param>
</span>    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)
        transGroup.Children.Add(rtnTrans3D)
        cubelet.Transform = transGroup

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

    ''' <span class="code-SummaryComment"><summary>
</span>    ''' Rotate cubie around the Y-axis.
    ''' <span class="code-SummaryComment"></summary>
</span>    ''' <span class="code-SummaryComment"><param name="angle">The angle of rotation; -90 or 90.</param>
</span>    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)
        transGroup.Children.Add(rtnTrans3D)
        cubelet.Transform = transGroup

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

    ''' <span class="code-SummaryComment"><summary>
</span>    ''' Rotate cubie around the Z-axis.
    ''' <span class="code-SummaryComment"></summary>
</span>    ''' <span class="code-SummaryComment"><param name="angle">The angle of rotation; -90 or 90.</param>
</span>    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)
        transGroup.Children.Add(rtnTrans3D)
        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 >

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


    ''' <span class="code-SummaryComment"><summary>
</span>    ''' 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. 
    ''' <span class="code-SummaryComment"></summary>   
</span>    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
    
    ''' <span class="code-SummaryComment"><summary>
</span>    ''' 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.  
    ''' <span class="code-SummaryComment"></summary> 
</span>    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

    ''' <span class="code-SummaryComment"><summary>
</span>    ''' 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.
    ''' <span class="code-SummaryComment"></summary>   
</span>    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)
        AddHandler scrambleTimer.Tick, AddressOf Timer_Tick
    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

    ''' <span class="code-SummaryComment"><summary>
</span>    ''' 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.
    ''' <span class="code-SummaryComment"></summary>
</span>    Private Sub PopulateRotationsList()
        Dim n As Integer = rnd.Next(0, 18)
        Dim lastString As String = String.Empty

        If (rotationsList.Count >

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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Meshack Musundi
Software Developer
Kenya Kenya
Meshack is an avid programmer with a bias towards WPF and VB.NET. He has about 5 years of programming experience initially starting off with Java before shifting to .NET, thanks to the allure of WPF. He has developed several applications, and written several articles about them, which can be viewed here on CodeProject. He currently resides in a small town in Kiambu county, Kenya.
 
Awards;
  • CodeProject MVP 2013
  • CodeProject MVP 2012
  • Best VB.NET article of August 2013
  • 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

Comments and Discussions

 
Questionscramble algorithm Pinmemberjrobb22922-Nov-13 16:58 
GeneralMy vote of 5 PinmvpMichael Haephrati15-Mar-13 2:49 
GeneralRe: My vote of 5 PinmvpMeshack Musundi15-Mar-13 4:47 
GeneralMy vote of 5 PinprotectorBarry Lapthorn25-Feb-13 1:57 
GeneralRe: My vote of 5 PinmvpMeshack Musundi26-Feb-13 2:42 
GeneralMy vote of 5 PinmemberDuncan Edwards Jones30-Dec-12 9:40 
Excellent 3D WPF demo
GeneralRe: My vote of 5 PinmvpMeshack Musundi30-Dec-12 19:37 
GeneralMy vote of 5 PinmemberTarun Y Mangukiya11-Aug-12 16:56 
GeneralRe: My vote of 5 PinmvpMeshack Musundi20-Aug-12 20:50 
GeneralMy vote of 5 PinmemberFarhan Ghumra22-Jun-12 2:07 
GeneralRe: My vote of 5 PinmvpMeshack Musundi26-Jul-12 21:49 
GeneralMy vote of 5 Pinmembermichael azzar1-Jun-12 5:20 
QuestionYou're very Distinctive [modified] Pinmembermichael azzar1-Jun-12 5:18 
GeneralRe: You're very Distinctive PinmvpMeshack Musundi1-Jun-12 21:18 
GeneralRe: You're very Distinctive Pinmembermichael azzar2-Jun-12 10:49 
GeneralRubik's Cube PinmemberVishalvPatil17-Apr-12 5:30 
GeneralRe: Rubik's Cube PinmvpMeshack Musundi17-Apr-12 20:33 
GeneralMy vote of 5 PinmemberG-Tek3-Apr-12 3:49 
GeneralRe: My vote of 5 PinmvpMeshack Musundi12-Apr-12 4:25 
GeneralMy vote of 5 PinmemberBrian Pendleton2-Apr-12 7:39 
GeneralRe: My vote of 5 PinmvpMeshack Musundi12-Apr-12 4:22 
GeneralMy vote of 5 PinmemberReza Ahmadi2-Apr-12 5:45 
GeneralRe: My vote of 5 PinmvpMeshack Musundi12-Apr-12 4:21 
GeneralMy vote of 5 Pinmembermanoj kumar choubey19-Mar-12 19:35 
GeneralRe: My vote of 5 PinmvpMeshack Musundi20-Mar-12 23:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140718.1 | Last Updated 1 Feb 2012
Article Copyright 2012 by Meshack Musundi
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid