Click here to Skip to main content
16,018,529 members
Articles / Desktop Programming / WPF

WPF: Rubik's Cube

Rate me:
Please Sign up or sign in to vote.
4.97/5 (112 votes)
24 Sep 2014CPOL3 min read 187.4K   10.3K   154   101
A WPF 3D Rubik's Cube application

Image 1

Introduction

I'm a huge fan of the Rubik's Cube and I solve it several times, almost daily. Ever since I got my first Rubik's Cube it has captivated me, and frustrated me in my attempts at getting faster at solving - I currently average 17 sec. It was only natural then that I took a shot at making a WPF version.

Background

The 3D APIs in WPF are not designed to provide full-featured game development capabilities but it is possible to develop something as 'simple' as a Rubik's Cube app. In this article I'll not delve into the finer details of WPF's 3D features so I will expect that you are conversant with that part of WPF. Another thing that you should be familiar with is basic cubing terms. Just in case, this is how you denote the faces of a cube,

322872/Cube_Faces.png

Using the App

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.

Image 3

Rotating the U layer anti-clockwise

Image 4

The same principle also applies to whole cube rotations but instead of using the left-mouse button you use the right-mouse button.

Note that to make rotations you only interact with the F and R faces of the cube. To scramble the cube click the Scramble button.

Design & Layout

Designing the 3D model of the cube by hand-coding XAML would have been a tedious affair so I instead opted to model it in Blender.

322872/Blender_Screenshot_1.png

Modelling of the cube in progress

322872/Blender_Screenshot_2.png

The final result

After modeling the cube in Blender I exported the 3D model as a Wavefront (.obj) file which I then added to my WPF project in Expression Blend, together with the corresponding material (.mtl) file.

Image 7

The material and object files

With the material and object file in the project, adding the 3D model to a WPF container is just a matter of right-clicking the object file and selecting Insert from the context-menu.

For this project I added more lights for adequate illumination of the cube so the Viewport3D contains several PointLights, a DirectionalLight and an AmbientLight.

The Viewport3D is masked by several Paths that are used to deal with mouse events. I could have opted to use ModelUIElement3D - that support input, focus, and events - but opted to stick with ModelVisual3D objects.

Image 8

Each ModelVisual3D object that represents a cube piece is named according to the colors of its 'stickers' e.g. WGR_Cubie is the White-Green-Red corner piece.

Code

The project contains three enumerations. The PieceLocations enumerations represent the location in 3D space of a cube piece.

VB.NET
Public Enum PieceLocations
    ' U layer locations
    FUL
    FU
    FUR
    RU
    BUR
    BU
    BUL
    LU
    UC
    ' E layer locations
    FL
    FC
    FR
    RC
    BR
    BC
    BL
    LC
    ' D layer locations
    FDL
    FD
    FDR
    RD
    BDR
    BD
    BDL
    LD
    DC
End Enum

The Notations enumerations represent the notations of a Rubik's Cube.

VB.NET
Public Enum Notations
    F
    F_prime
    F2
    B
    B_prime
    B2
    U
    U_prime
    U2
    D
    D_prime
    D2
    R
    R_prime
    R2
    L
    L_prime
    L2
End Enum

The FaceNotations enumerations represent the location of a Path on the faces of the cube. Remember that only two faces of the cube are masked by Paths.

VB.NET
Public Enum FaceLocations
    FUL
    FU
    FUR
    RUF
    RU
    RUB
    FL
    FC
    FR
    RF
    RC
    RB
    FDL
    FD
    FDR
    RDF
    RD
    RDB
End Enum

Class FacePath defines an attached property that is set on the masking Paths.

VB.NET
Public Class FacePath
    Public Shared ReadOnly LocationProperty As DependencyProperty =
        DependencyProperty.RegisterAttached("Location", GetType(FaceLocations), GetType(FacePath),
                                            New FrameworkPropertyMetadata(FaceLocations.FC,
                                                                          FrameworkPropertyMetadataOptions.None))

    Public Shared Sub SetLocation(ByVal element As Path, ByVal value As FaceLocations)
        element.SetValue(LocationProperty, value)
    End Sub

    Public Shared Function GetLocation(ByVal element As Path) As FaceLocations
        Return CType(element.GetValue(LocationProperty), FaceLocations)
    End Function
End Class

Each cube piece in the Viewport3D is associated with an object of class CubePiece which contains methods for rotating the associated ModelVisual3D around a particular axis.

VB.NET
Public Class CubePiece
    Public piece As ModelVisual3D

    Private axisPoint As New Point3D(0, 0, 0)
    Private axisAngleRtn3D As New AxisAngleRotation3D(New Vector3D(0, 0, 1), 0)
    Private dblAnimation As DoubleAnimation
    Private rotateTx3D As RotateTransform3D
    Private tx3dGroup As New Transform3DGroup

    Private Const ROTATION_TIME As Double = 200
    Friend Property PieceLocation() As PieceLocations

    Private Sub RotateAroundAxis(ByVal angle As Double)
        rotateTx3D = New RotateTransform3D(axisAngleRtn3D, axisPoint)
        dblAnimation = New DoubleAnimation(CDbl(angle), TimeSpan.FromMilliseconds(ROTATION_TIME),
                                           FillBehavior.HoldEnd)
        axisAngleRtn3D.BeginAnimation(AxisAngleRotation3D.AngleProperty, dblAnimation)
        tx3dGroup.Children.Add(rotateTx3D)
        piece.Transform = tx3dGroup
    End Sub

    ''' <summary>
    ''' Rotate cube piece 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)
        RotateAroundAxis(angle)
        ChangeLocationOnXaxisRotation(angle)
    End Sub

    ''' <summary>
    ''' Rotate cube piece 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)
        RotateAroundAxis(angle)
        ChangeLocationOnYaxisRotation(angle)
    End Sub

    ''' <summary>
    ''' Rotate cube piece 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)
        RotateAroundAxis(angle)
        ChangeLocationOnZaxisRotation(angle)
    End Sub
...
End Class

Class Scrambler contains methods for scramble generation and scrambling of the cube.

VB.NET
Public Class Scrambler
    Private index As Integer
    Private isScrambling As Boolean

    Private Const CLOCKWISE As Double = -90
    Private Const ANTI_CLOCKWISE As Double = 90

    Private mainWin As MainWindow
    Private rnd As Random
    Private scrambleTimer As DispatcherTimer
    Private scramble As List(Of Notations)

    Public Sub New(ByRef win As MainWindow)
        mainWin = win
        rnd = New Random
        scramble = New List(Of Notations)
        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
            scramble.Clear()
            ' Create a scramble with 20 values
            For i As Integer = 0 To 19
                AddToScramble()
            Next
            isScrambling = True
            scrambleTimer.Start()
            mainWin.CubeGroupGrid.IsEnabled = False
        Else
            MessageBox.Show("Scrambling of cube already in progress", _
                            "WPF Rubiks", MessageBoxButton.OK, MessageBoxImage.Exclamation)
        End If
    End Sub

    ''' <summary>
    ''' Create scramble with dissimilar values following each other, and the new value 
    ''' in the List is not an inverse or double of the preceding value e.g.
    ''' F' does not come after F, or F2 after F.
    ''' </summary>
    Private Sub AddToScramble()
        Dim randomNum As Integer = rnd.Next(0, 18)
        Dim newNotation As String = [Enum].GetName(GetType(Notations), randomNum)
        Dim notation As Notations

        If (scramble.Count > 0) Then
            Dim lastItem As String = scramble(scramble.Count - 1).ToString

            If newNotation.Contains("F") AndAlso lastItem.Contains("F") Then
                AddToScramble()
            ElseIf newNotation.Contains("R") AndAlso lastItem.Contains("R") Then
                AddToScramble()
            ElseIf newNotation.Contains("B") AndAlso lastItem.Contains("B") Then
                AddToScramble()
            ElseIf newNotation.Contains("L") AndAlso lastItem.Contains("L") Then
                AddToScramble()
            ElseIf newNotation.Contains("U") AndAlso lastItem.Contains("U") Then
                AddToScramble()
            ElseIf newNotation.Contains("D") AndAlso lastItem.Contains("D") Then
                AddToScramble()
            Else
                notation = [Enum].Parse(GetType(Notations), newNotation)
                scramble.Add(notation)
            End If
        Else
            notation = [Enum].Parse(GetType(Notations), newNotation)
            scramble.Add(notation)
        End If
    End Sub

    Private Sub Timer_Tick(ByVal sender As Object, ByVal e As EventArgs)
        If (index < 19) Then
            RotateCubePieces(index)
            index += 1
        Else
            isScrambling = False
            scrambleTimer.Stop()
            index = 0
            mainWin.CubeGroupGrid.IsEnabled = True
        End If
    End Sub
...
End Class

The project contains a few more classes that deal mainly with determining which direction a layer or the cube should be rotated. You can take a look at these classes by downloading the project source from the download link at the top of the article page.

Conclusion

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,
  • 25th Sep, 2014: Updated article and source

License

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


Written By
Software Developer
Kenya Kenya
Experienced C# software developer with a passion for WPF.

Awards,
  • CodeProject MVP 2013
  • CodeProject MVP 2012
  • CodeProject MVP 2021

Comments and Discussions

 
GeneralMy vote of 5 Pin
Anurag Gandhi12-Mar-12 9:36
professionalAnurag Gandhi12-Mar-12 9:36 
GeneralRe: My vote of 5 Pin
Meshack Musundi12-Mar-12 20:50
professionalMeshack Musundi12-Mar-12 20:50 
GeneralMy vote of 5 Pin
ProEnggSoft7-Mar-12 20:49
ProEnggSoft7-Mar-12 20:49 
GeneralRe: My vote of 5 Pin
Meshack Musundi7-Mar-12 20:53
professionalMeshack Musundi7-Mar-12 20:53 
GeneralMy vote of 5 Pin
Anthony Daly16-Feb-12 3:06
Anthony Daly16-Feb-12 3:06 
GeneralRe: My vote of 5 Pin
Meshack Musundi17-Feb-12 5:22
professionalMeshack Musundi17-Feb-12 5:22 
QuestionVery fine Pin
BillW3314-Feb-12 4:28
professionalBillW3314-Feb-12 4:28 
GeneralRe: Very fine Pin
Meshack Musundi14-Feb-12 20:16
professionalMeshack Musundi14-Feb-12 20:16 
GeneralMy vote of 5 Pin
Pankaj Chamria10-Feb-12 20:38
Pankaj Chamria10-Feb-12 20:38 
GeneralRe: My vote of 5 Pin
Meshack Musundi12-Feb-12 23:22
professionalMeshack Musundi12-Feb-12 23:22 
GeneralMy vote of 5 Pin
Sanjay K. Gupta10-Feb-12 3:23
professionalSanjay K. Gupta10-Feb-12 3:23 
GeneralRe: My vote of 5 Pin
Meshack Musundi10-Feb-12 4:24
professionalMeshack Musundi10-Feb-12 4:24 
QuestionFantastic! Pin
Mehdi Gholam9-Feb-12 22:58
Mehdi Gholam9-Feb-12 22:58 
GeneralRe: Fantastic! Pin
Meshack Musundi10-Feb-12 4:23
professionalMeshack Musundi10-Feb-12 4:23 
GeneralMy vote of 5 Pin
Javier Jiménez9-Feb-12 22:50
professionalJavier Jiménez9-Feb-12 22:50 
GeneralRe: My vote of 5 Pin
Meshack Musundi10-Feb-12 4:21
professionalMeshack Musundi10-Feb-12 4:21 
GeneralMy vote of 5 Pin
linuxjr9-Feb-12 13:28
professionallinuxjr9-Feb-12 13:28 
GeneralRe: My vote of 5 Pin
Meshack Musundi9-Feb-12 19:52
professionalMeshack Musundi9-Feb-12 19:52 
GeneralFantastic work Pin
Marcelo Ricardo de Oliveira9-Feb-12 6:43
Marcelo Ricardo de Oliveira9-Feb-12 6:43 
GeneralRe: Fantastic work Pin
Meshack Musundi9-Feb-12 19:51
professionalMeshack Musundi9-Feb-12 19:51 
GeneralMy vote of 5 Pin
DaveAuld7-Feb-12 4:00
professionalDaveAuld7-Feb-12 4:00 
GeneralRe: My vote of 5 Pin
Meshack Musundi7-Feb-12 19:21
professionalMeshack Musundi7-Feb-12 19:21 
GeneralMy vote of 5 Pin
Dr. Song Li7-Feb-12 3:21
Dr. Song Li7-Feb-12 3:21 
GeneralRe: My vote of 5 Pin
Meshack Musundi7-Feb-12 19:20
professionalMeshack Musundi7-Feb-12 19:20 
GeneralMy vote of 5 Pin
Halil ibrahim Kalkan3-Feb-12 0:19
Halil ibrahim Kalkan3-Feb-12 0:19 

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

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