├── TwoPhaseSolver ├── tables │ ├── move │ │ ├── co_move │ │ ├── cp_move │ │ ├── ds_move │ │ ├── eo_move │ │ ├── ep2_move │ │ ├── ud_move │ │ ├── uds_move │ │ └── us_move │ ├── other │ │ ├── combi │ │ └── ep2_merge │ └── prune │ │ ├── co_ud_prun │ │ ├── cp_ud2_prun │ │ ├── eo_ud_prun │ │ └── ep2_ud2_prun ├── Constants.cs ├── PruneTable.cs ├── Properties │ └── AssemblyInfo.cs ├── BinLoad.cs ├── TwoPhaseSolver.csproj ├── Tools.cs ├── Move.cs ├── Coords.cs ├── Cube.cs └── Search.cs ├── SolverTest ├── App.config ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── SolverTest.csproj ├── LICENSE ├── TwoPhaseSolver.sln ├── README.md ├── .gitattributes └── .gitignore /TwoPhaseSolver/tables/move/co_move: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tremwil/TwoPhaseSolver/HEAD/TwoPhaseSolver/tables/move/co_move -------------------------------------------------------------------------------- /TwoPhaseSolver/tables/move/cp_move: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tremwil/TwoPhaseSolver/HEAD/TwoPhaseSolver/tables/move/cp_move -------------------------------------------------------------------------------- /TwoPhaseSolver/tables/move/ds_move: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tremwil/TwoPhaseSolver/HEAD/TwoPhaseSolver/tables/move/ds_move -------------------------------------------------------------------------------- /TwoPhaseSolver/tables/move/eo_move: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tremwil/TwoPhaseSolver/HEAD/TwoPhaseSolver/tables/move/eo_move -------------------------------------------------------------------------------- /TwoPhaseSolver/tables/move/ep2_move: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tremwil/TwoPhaseSolver/HEAD/TwoPhaseSolver/tables/move/ep2_move -------------------------------------------------------------------------------- /TwoPhaseSolver/tables/move/ud_move: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tremwil/TwoPhaseSolver/HEAD/TwoPhaseSolver/tables/move/ud_move -------------------------------------------------------------------------------- /TwoPhaseSolver/tables/move/uds_move: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tremwil/TwoPhaseSolver/HEAD/TwoPhaseSolver/tables/move/uds_move -------------------------------------------------------------------------------- /TwoPhaseSolver/tables/move/us_move: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tremwil/TwoPhaseSolver/HEAD/TwoPhaseSolver/tables/move/us_move -------------------------------------------------------------------------------- /TwoPhaseSolver/tables/other/combi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tremwil/TwoPhaseSolver/HEAD/TwoPhaseSolver/tables/other/combi -------------------------------------------------------------------------------- /TwoPhaseSolver/tables/other/ep2_merge: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tremwil/TwoPhaseSolver/HEAD/TwoPhaseSolver/tables/other/ep2_merge -------------------------------------------------------------------------------- /TwoPhaseSolver/tables/prune/co_ud_prun: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tremwil/TwoPhaseSolver/HEAD/TwoPhaseSolver/tables/prune/co_ud_prun -------------------------------------------------------------------------------- /TwoPhaseSolver/tables/prune/cp_ud2_prun: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tremwil/TwoPhaseSolver/HEAD/TwoPhaseSolver/tables/prune/cp_ud2_prun -------------------------------------------------------------------------------- /TwoPhaseSolver/tables/prune/eo_ud_prun: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tremwil/TwoPhaseSolver/HEAD/TwoPhaseSolver/tables/prune/eo_ud_prun -------------------------------------------------------------------------------- /TwoPhaseSolver/tables/prune/ep2_ud2_prun: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tremwil/TwoPhaseSolver/HEAD/TwoPhaseSolver/tables/prune/ep2_ud2_prun -------------------------------------------------------------------------------- /SolverTest/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /TwoPhaseSolver/Constants.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace TwoPhaseSolver 8 | { 9 | public struct Constants 10 | { 11 | public const int N_CO = 2187; // 3^7 12 | public const int N_EO = 2048; // 2^12 13 | public const int N_UD = 495; // 12! / (4! * 8!) 14 | 15 | public const int N_CP = 40320; // 8! 16 | public const int N_EP2 = 40320; // 8! 17 | public const int N_UDS = 11880; // 12! / 8! 18 | public const int N_UD2 = 24; // 4! 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 William Tremblay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SolverTest/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using TwoPhaseSolver; 3 | 4 | namespace SolverTest 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | // Just solve a random cube with some pattern. 11 | Cube c = Move.randmove(200).apply(new Cube()); 12 | Move pattern; 13 | 14 | // BEST. RANDOM. GEN. EVER. (actually not that bad, 15 | // since you'd have to time yourself with 100nanosecond precision 16 | if ((DateTime.Now.Ticks & 1) == 0) 17 | { 18 | pattern = Move.None; 19 | Console.WriteLine("No pattern this time..."); 20 | } 21 | else 22 | { 23 | pattern = Move.randmove(20); 24 | Console.WriteLine("Pattern is {0}", pattern); 25 | } 26 | 27 | // Do the actual solve while printing what is happening 28 | Search.patternSolve(c, pattern, 22, printInfo: true); 29 | 30 | // End 31 | Console.Write("Press any key to continue..."); 32 | Console.Read(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /TwoPhaseSolver/PruneTable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace TwoPhaseSolver 8 | { 9 | public class PruneTable 10 | { 11 | byte[] bytes; 12 | 13 | public PruneTable(int size) 14 | { 15 | bytes = new byte[size / 2]; 16 | } 17 | 18 | public PruneTable(byte[] bytes) 19 | { 20 | this.bytes = bytes; 21 | } 22 | 23 | public byte this[int index] 24 | { 25 | get 26 | { 27 | if ((index & 1) == 1) { return (byte)((bytes[index / 2] & 0xf0) >> 4); } 28 | else { return (byte)(bytes[index / 2] & 0x0f); } 29 | } 30 | set 31 | { 32 | if ((index & 1) == 1) { bytes[index / 2] &= (byte)(0x0f | (value << 4)); } 33 | else { bytes[index / 2] &= (byte)(value | 0xf0); } 34 | } 35 | } 36 | 37 | // Prune tables 38 | 39 | public static readonly PruneTable pruneCO = BinLoad.loadPruneTable("tables\\prune\\co_ud_prun"); 40 | public static readonly PruneTable pruneEO = BinLoad.loadPruneTable("tables\\prune\\eo_ud_prun"); 41 | 42 | public static readonly PruneTable pruneCP = BinLoad.loadPruneTable("tables\\prune\\cp_ud2_prun"); 43 | public static readonly PruneTable pruneEP2 = BinLoad.loadPruneTable("tables\\prune\\ep2_ud2_prun"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /SolverTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SolverTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SolverTest")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("ea6d133f-b24e-42b6-826e-c18e6a6bdd61")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TwoPhaseSolver.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25123.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TwoPhaseSolver", "TwoPhaseSolver\TwoPhaseSolver.csproj", "{C0259681-7E14-4939-B525-310B3536D8CF}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SolverTest", "SolverTest\SolverTest.csproj", "{EA6D133F-B24E-42B6-826E-C18E6A6BDD61}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {C0259681-7E14-4939-B525-310B3536D8CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {C0259681-7E14-4939-B525-310B3536D8CF}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {C0259681-7E14-4939-B525-310B3536D8CF}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {C0259681-7E14-4939-B525-310B3536D8CF}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {EA6D133F-B24E-42B6-826E-C18E6A6BDD61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {EA6D133F-B24E-42B6-826E-C18E6A6BDD61}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {EA6D133F-B24E-42B6-826E-C18E6A6BDD61}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {EA6D133F-B24E-42B6-826E-C18E6A6BDD61}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /TwoPhaseSolver/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TwoPhaseSolver")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TwoPhaseSolver")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("c0259681-7e14-4939-b525-310b3536d8cf")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TwoPhaseSolver 2 | Kociemba's algorithm implementation in C#. 3 | 4 | I originaly made it for a Mindstorms EV3 cube-solving robot, but it can be used in any situation. Note that I do not have any real experience in coding well, so the code is pretty much a mess without comments. I might add some documentation later on. 5 | 6 | # Building 7 | Simply open the solution in VisualStudio and hit "Build Solution". The "TwoPhaseSolver.dll" library and a test application named "SolverTest.exe" should be created. 8 | 9 | # Info 10 | Solves the rubik's cube using a two-phase algorithm. The code first maps different permuations of the cube as natural numbers. Then, it uses precomputed tables to apply moves to these numbers. The search is IDA*-like with four different prune tables as a heuristic. The code first tries to solve the cube in a position where all edges and corners have an orientation of 0 (phase 1), to then solve it as a whole (phase 2). Prune tables for phase 1 go up to depth 12 (they cover all of it) while phase 2 tables only cover depth 12 out of a maximum of 18. This means that the code will sometimes run very slow when asked to solve for a 20 or sometimes 21 move solution. It works great from 22 to 24 moves. 11 | 12 | # Notes 13 | The Search class is a C# port (with some modifications) of the one in Kociemba's Java implementation. All credit for this class goes to him. His website (http://kociemba.org/cube.htm) is the main source of information I used to program this two phase solver. I highly recommend checking it out if you want to know more about Rubik's cubes. 14 | 15 | Also note that the programs that generate the tables are NOT included in this solution. I might release them later. 16 | -------------------------------------------------------------------------------- /TwoPhaseSolver/BinLoad.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.IO; 7 | 8 | namespace TwoPhaseSolver 9 | { 10 | static class BinLoad 11 | { 12 | public static byte[][] getUdToPerm(string path) 13 | { 14 | var raw = File.OpenRead(path); 15 | byte[][] values = new byte[Constants.N_UD][]; 16 | byte[] c; 17 | 18 | for (var i = 0; i < Constants.N_UD; i++) 19 | { 20 | c = new byte[2]; 21 | raw.Read(c, 0, 2); 22 | 23 | values[i] = new byte[4] 24 | { 25 | (byte)((c[0] & 0xf0) >> 4), 26 | (byte)(c[0] & 0x0f), 27 | (byte)((c[1] & 0xf0) >> 4), 28 | (byte)(c[1] & 0x0f) 29 | }; 30 | } 31 | 32 | raw.Close(); 33 | return values; 34 | } 35 | 36 | public static ushort[,] loadShortTable2D(string path, int chunksize = 18) 37 | { 38 | var bytes = File.ReadAllBytes(path); 39 | int len1d = bytes.Length / chunksize / 2; 40 | ushort[,] values = new ushort[len1d, chunksize]; 41 | int i, j; 42 | 43 | for (i = 0; i < len1d; i++) 44 | { 45 | for (j = 0; j < chunksize; j++) 46 | { 47 | values[i, j] = (ushort)( 48 | (bytes[(chunksize * i + j) * 2] << 8) + 49 | bytes[(chunksize * i + j) * 2 + 1] 50 | ); 51 | } 52 | } 53 | 54 | return values; 55 | } 56 | 57 | public static PruneTable loadPruneTable(string path) 58 | { 59 | return new PruneTable(File.ReadAllBytes(path)); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /TwoPhaseSolver/TwoPhaseSolver.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C0259681-7E14-4939-B525-310B3536D8CF} 8 | Library 9 | Properties 10 | TwoPhaseSolver 11 | TwoPhaseSolver 12 | v4.5.2 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | mkdir "$(TargetDir)\tables" 56 | XCOPY "$(ProjectDir)tables" "$(TargetDir)\tables" /s /y 57 | 58 | 65 | -------------------------------------------------------------------------------- /SolverTest/SolverTest.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {EA6D133F-B24E-42B6-826E-C18E6A6BDD61} 8 | Exe 9 | Properties 10 | SolverTest 11 | SolverTest 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ..\TwoPhaseSolver\bin\Debug\TwoPhaseSolver.dll 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | mkdir "$(TargetDir)tables" 58 | XCOPY "$(SolutionDir)TwoPhaseSolver\tables" "$(TargetDir)tables" /y /s 59 | 60 | 67 | -------------------------------------------------------------------------------- /TwoPhaseSolver/Tools.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace TwoPhaseSolver 8 | { 9 | static class Tools 10 | { 11 | public static int factorial(int n) 12 | { 13 | int k = 1; 14 | for (int i = 2; i < n; i++) 15 | { 16 | k *= i; 17 | } 18 | return k * n; 19 | } 20 | 21 | public static int nChooseK(int n, int k) 22 | { 23 | int i, j, s; 24 | if (n < k) 25 | return 0; 26 | if (k > n / 2) 27 | k = n - k; 28 | for (s = 1, i = n, j = 1; i != n - k; i--, j++) 29 | { 30 | s *= i; 31 | s /= j; 32 | } 33 | return s; 34 | } 35 | 36 | public static T[] rotate(this IEnumerable me, int amount) 37 | { 38 | var newarr = new T[me.Count()]; 39 | int i = 0; 40 | 41 | foreach (T item in me) 42 | { 43 | newarr[(i + amount) % me.Count()] = item; 44 | i++; 45 | } 46 | 47 | return newarr; 48 | } 49 | 50 | public static int Index(this T[] me, T value, IEqualityComparer comparer) 51 | { 52 | for (int i = 0; i < me.Length; i++) 53 | { 54 | if (comparer.Equals(value, me[i])) { return i; } 55 | } 56 | 57 | return -1; 58 | } 59 | 60 | public static int Index(this T[] me, T value, Func comparer) 61 | { 62 | for (int i = 0; i < me.Length; i++) 63 | { 64 | if (comparer(value, me[i])) { return i; } 65 | } 66 | 67 | return -1; 68 | } 69 | 70 | public static int Index(this T[] me, T value) where T : IEquatable 71 | { 72 | for (int i = 0; i < me.Length; i++) 73 | { 74 | if (value.Equals(me[i])) { return i; } 75 | } 76 | 77 | return -1; 78 | } 79 | 80 | public static bool setEquals(this T[] a, T[] b) 81 | { 82 | var min = (a.Length >= b.Length) ? b : a; 83 | var max = (a.Length >= b.Length) ? a : b; 84 | 85 | foreach (T item in max) 86 | { 87 | if (!min.Contains(item)) { return false; } 88 | } 89 | 90 | return true; 91 | } 92 | 93 | static void ColorProcedure(object obj, Action printFunc, ConsoleColor fg, ConsoleColor bg) 94 | { 95 | ConsoleColor previousFg = Console.ForegroundColor; 96 | ConsoleColor previousBg = Console.BackgroundColor; 97 | 98 | Console.ForegroundColor = fg; 99 | Console.BackgroundColor = bg; 100 | 101 | printFunc(obj); 102 | 103 | Console.ForegroundColor = previousFg; 104 | Console.BackgroundColor = previousBg; 105 | } 106 | 107 | public static void ColorPrint(object obj, ConsoleColor fg, ConsoleColor bg = ConsoleColor.Black) 108 | { 109 | ColorProcedure(obj, Console.Write, fg, bg); 110 | } 111 | 112 | public static void ColorPrintLine(object obj, ConsoleColor fg, ConsoleColor bg = ConsoleColor.Black) 113 | { 114 | ColorProcedure(obj, Console.WriteLine, fg, bg); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Microsoft Azure ApplicationInsights config file 170 | ApplicationInsights.config 171 | 172 | # Windows Store app package directory 173 | AppPackages/ 174 | BundleArtifacts/ 175 | 176 | # Visual Studio cache files 177 | # files ending in .cache can be ignored 178 | *.[Cc]ache 179 | # but keep track of directories ending in .cache 180 | !*.[Cc]ache/ 181 | 182 | # Others 183 | ClientBin/ 184 | [Ss]tyle[Cc]op.* 185 | ~$* 186 | *~ 187 | *.dbmdl 188 | *.dbproj.schemaview 189 | *.pfx 190 | *.publishsettings 191 | node_modules/ 192 | orleans.codegen.cs 193 | 194 | # RIA/Silverlight projects 195 | Generated_Code/ 196 | 197 | # Backup & report files from converting an old project file 198 | # to a newer Visual Studio version. Backup files are not needed, 199 | # because we have git ;-) 200 | _UpgradeReport_Files/ 201 | Backup*/ 202 | UpgradeLog*.XML 203 | UpgradeLog*.htm 204 | 205 | # SQL Server files 206 | *.mdf 207 | *.ldf 208 | 209 | # Business Intelligence projects 210 | *.rdl.data 211 | *.bim.layout 212 | *.bim_*.settings 213 | 214 | # Microsoft Fakes 215 | FakesAssemblies/ 216 | 217 | # GhostDoc plugin setting file 218 | *.GhostDoc.xml 219 | 220 | # Node.js Tools for Visual Studio 221 | .ntvs_analysis.dat 222 | 223 | # Visual Studio 6 build log 224 | *.plg 225 | 226 | # Visual Studio 6 workspace options file 227 | *.opt 228 | 229 | # Visual Studio LightSwitch build output 230 | **/*.HTMLClient/GeneratedArtifacts 231 | **/*.DesktopClient/GeneratedArtifacts 232 | **/*.DesktopClient/ModelManifest.xml 233 | **/*.Server/GeneratedArtifacts 234 | **/*.Server/ModelManifest.xml 235 | _Pvt_Extensions 236 | 237 | # LightSwitch generated files 238 | GeneratedArtifacts/ 239 | ModelManifest.xml 240 | 241 | # Paket dependency manager 242 | .paket/paket.exe 243 | 244 | # FAKE - F# Make 245 | .fake/ -------------------------------------------------------------------------------- /TwoPhaseSolver/Move.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace TwoPhaseSolver 8 | { 9 | public class Move 10 | { 11 | private static byte[,,] edgeMoveTable = new byte[6, 12, 2] 12 | { 13 | {{3, 0},{0, 0},{1, 0},{2, 0},{4, 0},{5, 0}, //U 14 | {6, 0},{7, 0},{8, 0},{9, 0},{10, 0},{11, 0}}, 15 | {{8, 0},{1, 0},{2, 0},{3, 0},{11, 0},{5, 0}, //R 16 | {6, 0},{7, 0},{4, 0},{9, 0},{10, 0},{0, 0}}, 17 | {{0, 0},{9, 1},{2, 0},{3, 0},{4, 0},{8, 1}, //F 18 | {6, 0},{7, 0},{1, 1},{5, 1},{10, 0},{11, 0}}, 19 | {{0, 0},{1, 0},{2, 0},{3, 0},{5, 0},{6, 0}, //D 20 | {7, 0},{4, 0},{8, 0},{9, 0},{10, 0},{11, 0}}, 21 | {{0, 0},{1, 0},{10, 0},{3, 0},{4, 0},{5, 0}, //L 22 | {9, 0},{7, 0},{8, 0},{2, 0},{6, 0},{11, 0}}, 23 | {{0, 0},{1, 0},{2, 0},{11, 1},{4, 0},{5, 0}, //B 24 | {6, 0},{10, 1},{8, 0},{9, 0},{3, 1},{7, 1}} 25 | }; 26 | 27 | private static byte[,,] cornMoveTable = new byte[6, 8, 2] 28 | { 29 | {{3, 0},{0, 0},{1, 0},{2, 0},{4, 0},{5, 0},{6, 0},{7, 0}}, //U 30 | {{4, 2},{1, 0},{2, 0},{0, 1},{7, 1},{5, 0},{6, 0},{3, 2}}, //R 31 | {{1, 1},{5, 2},{2, 0},{3, 0},{0, 2},{4, 1},{6, 0},{7, 0}}, //F 32 | {{0, 0},{1, 0},{2, 0},{3, 0},{5, 0},{6, 0},{7, 0},{4, 0}}, //D 33 | {{0, 0},{2, 1},{6, 2},{3, 0},{4, 0},{1, 2},{5, 1},{7, 0}}, //L 34 | {{0, 0},{1, 0},{3, 1},{7, 2},{4, 0},{5, 0},{2, 2},{6, 1}} //B 35 | }; 36 | 37 | public static byte[] phase2Move = new byte[10] 38 | { 39 | 0, 5, 6, 7, 8, 9, 10, 11, 12, 17 40 | }; 41 | 42 | public static string[] strmove = 43 | "U U2 U' R R2 R' F F2 F' D D2 D' L L2 L' B B2 B'".Split(' '); 44 | 45 | public readonly byte[] moveList; 46 | 47 | public int Length 48 | { 49 | get { return moveList.Length; } 50 | } 51 | 52 | public Move(string movestr) 53 | { 54 | moveList = movestr.Split(' ').Select(x => (byte)strmove.Index(x)).ToArray(); 55 | } 56 | 57 | public Move(byte[] moveList) 58 | { 59 | this.moveList = moveList; 60 | } 61 | 62 | public static Cube apply(Cube cube, byte move, byte ap = 3) 63 | { 64 | int ax = move / 3; 65 | int po = move % 3; 66 | int x, i; 67 | Cubie c; 68 | Cubie[] edges, corns; 69 | 70 | for (x = 0; x <= po; x++) 71 | { 72 | corns = new Cubie[8]; 73 | edges = new Cubie[12]; 74 | 75 | if ((ap & 2) == 2) 76 | { 77 | for (i = 0; i < 8; i++) 78 | { 79 | c = cube.corners[cornMoveTable[ax, i, 0]]; 80 | corns[i] = new Cubie( 81 | c.pos, 82 | (byte)((cornMoveTable[ax, i, 1] + c.orient) % 3) 83 | ); 84 | } 85 | } 86 | else 87 | { 88 | corns = cube.corners; 89 | } 90 | 91 | if ((ap & 1) == 1) 92 | { 93 | for (i = 0; i < 12; i++) 94 | { 95 | c = cube.edges[edgeMoveTable[ax, i, 0]]; 96 | edges[i] = new Cubie( 97 | c.pos, 98 | (byte)((edgeMoveTable[ax, i, 1] + c.orient) % 2) 99 | ); 100 | } 101 | } 102 | else 103 | { 104 | edges = cube.edges; 105 | } 106 | 107 | cube = new Cube(corns, edges); 108 | } 109 | 110 | return cube; 111 | } 112 | 113 | public Cube apply(Cube cube) 114 | { 115 | foreach(byte m in moveList) 116 | { 117 | cube = Move.apply(cube, m); 118 | } 119 | 120 | return cube; 121 | } 122 | 123 | public Move inverse() 124 | { 125 | byte[] nmoves = new byte[moveList.Length]; 126 | int[] add = new int[3] { 2, 0, -2 }; 127 | byte m, inv; 128 | 129 | for (int i = 0; i < moveList.Length; i++) 130 | { 131 | m = moveList[moveList.Length - i - 1]; 132 | inv = (byte)(m + add[m % 3]); 133 | nmoves[i] = inv; 134 | } 135 | 136 | return new Move(nmoves); 137 | } 138 | 139 | public static Move randmove(int maneuverLen = 20) 140 | { 141 | int[] opface = new int[6] { 3, 4, 5, 0, 1, 2 }; 142 | Random generator = new Random(); 143 | byte[] moves = new byte[maneuverLen]; 144 | moves[0] = (byte)generator.Next(18); 145 | int face, m; 146 | 147 | for (int i = 1; i < maneuverLen; i++) 148 | { 149 | face = moves[i - 1] / 3; 150 | 151 | m = generator.Next(18); 152 | while (m / 3 == face || m / 3 == opface[face]) 153 | { 154 | m = generator.Next(18); 155 | } 156 | 157 | moves[i] = (byte)m; 158 | } 159 | 160 | return new Move(moves); 161 | } 162 | 163 | public override string ToString() 164 | { 165 | if (Length == 0) return "None"; 166 | return string.Join(" ", moveList.Select(x => strmove[x])); 167 | } 168 | 169 | public static Move operator +(Move a, Move b) 170 | { 171 | return new Move(a.moveList.Concat(b.moveList).ToArray()); 172 | } 173 | 174 | public static readonly Move None = new Move(new byte[0]); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /TwoPhaseSolver/Coords.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace TwoPhaseSolver 8 | { 9 | public partial class Cube 10 | { 11 | // Combinations 12 | 13 | public ushort UDSliceCoord() 14 | { 15 | int k = -1, i = 0, s = 0; 16 | 17 | foreach (Cubie e in edges) 18 | { 19 | if (UDSlice.Contains(e.pos)) { k += 1; } 20 | else if (k != -1) { s += Tools.nChooseK(i, k); } 21 | 22 | i += 1; 23 | } 24 | 25 | return (ushort)s; 26 | } 27 | 28 | public ushort USliceCoord() 29 | { 30 | int k = -1, i = 0, s = 0; 31 | 32 | foreach (Cubie e in edges.Reverse()) 33 | { 34 | if (USlice.Contains(e.pos)) { k += 1; } 35 | else if (k != -1) { s += Tools.nChooseK(i, k); } 36 | 37 | i += 1; 38 | } 39 | 40 | return (ushort)s; 41 | } 42 | 43 | public ushort DSliceCoord() 44 | { 45 | int k = -1, i = 0, s = 0; 46 | 47 | foreach (Cubie e in edges.rotate(4)) 48 | { 49 | if (DSlice.Contains(e.pos)) { k += 1; } 50 | else if (k != -1) { s += Tools.nChooseK(i, k); } 51 | 52 | i += 1; 53 | } 54 | 55 | return (ushort)s; 56 | } 57 | 58 | // Orients 59 | 60 | public ushort cornOrientCoord() 61 | { 62 | int i, s = 0; 63 | for (i = 0; i < 7; i++) 64 | { 65 | s = 3 * s + corners[i].orient; 66 | } 67 | 68 | return (ushort)s; 69 | } 70 | 71 | public ushort edgeOrientCoord() 72 | { 73 | int i, s = 0; 74 | for (i = 0; i < 11; i++) 75 | { 76 | s = 2 * s + edges[i].orient; 77 | } 78 | 79 | return (ushort)s; 80 | } 81 | 82 | // Permutations 83 | 84 | public ushort cornPermCoord() 85 | { 86 | int i, j, s, x = 0; 87 | 88 | for (i = 7; i > 0; i--) 89 | { 90 | s = 0; 91 | for (j = i; j >= 0; j--) 92 | { 93 | if (corners[j].pos > corners[i].pos) { s++; } 94 | } 95 | 96 | x = (x + s) * i; 97 | } 98 | 99 | return (ushort)x; 100 | } 101 | 102 | public int edgePermCoord() 103 | { 104 | int i, j, s, x = 0; 105 | 106 | for (i = 11; i > 0; i--) 107 | { 108 | s = 0; 109 | for (j = i; j >= 0; j--) 110 | { 111 | if (edges[j].pos > edges[i].pos) { s++; } 112 | } 113 | 114 | x = (x + s) * i; 115 | } 116 | 117 | return x; 118 | } 119 | 120 | public ushort edgePermCoord2() 121 | { 122 | int i, j, s, x = 0; 123 | 124 | for (i = 7; i > 0; i--) 125 | { 126 | s = 0; 127 | for (j = i; j >= 0; j--) 128 | { 129 | if (edges[j].pos > edges[i].pos) { s++; } 130 | } 131 | 132 | x = (x + s) * i; 133 | } 134 | 135 | return (ushort)x; 136 | } 137 | 138 | // Sorted combinations 139 | 140 | public ushort UDSliceCoordS() 141 | { 142 | List arr = new List(4); 143 | int i, j, s, x = 0; 144 | 145 | foreach (Cubie e in edges) 146 | { 147 | if (UDSlice.Contains(e.pos)) { arr.Add(e.pos); } 148 | } 149 | 150 | for (i = 3; i > 0; i--) 151 | { 152 | s = 0; 153 | for (j = i; j >= 0; j--) 154 | { 155 | if (arr[j] > arr[i]) { s++; } 156 | } 157 | 158 | x = (x + s) * i; 159 | } 160 | 161 | return (ushort)(UDSliceCoord() * 24 + x); 162 | } 163 | 164 | public ushort USliceCoordS() 165 | { 166 | List arr = new List(4); 167 | int i, j, s, x = 0; 168 | 169 | foreach (Cubie e in edges) 170 | { 171 | if (USlice.Contains(e.pos)) { arr.Add(e.pos); } 172 | } 173 | 174 | for (i = 3; i > 0; i--) 175 | { 176 | s = 0; 177 | for (j = i; j >= 0; j--) 178 | { 179 | if (arr[j] > arr[i]) { s++; } 180 | } 181 | 182 | x = (x + s) * i; 183 | } 184 | 185 | return (ushort)(USliceCoord() * 24 + x); 186 | } 187 | 188 | public ushort DSliceCoordS() 189 | { 190 | List arr = new List(4); 191 | int i, j, s, x = 0; 192 | 193 | foreach (Cubie e in edges) 194 | { 195 | if (DSlice.Contains(e.pos)) { arr.Add(e.pos); } 196 | } 197 | 198 | for (i = 3; i > 0; i--) 199 | { 200 | s = 0; 201 | for (j = i; j >= 0; j--) 202 | { 203 | if (arr[j] > arr[i]) { s++; } 204 | } 205 | 206 | x = (x + s) * i; 207 | } 208 | 209 | return (ushort)(DSliceCoord() * 24 + x); 210 | } 211 | } 212 | 213 | public static class MoveTables 214 | { 215 | // Move tables, max size 8! * 18 = 725760 216 | 217 | public static readonly ushort[,] moveCO = BinLoad.loadShortTable2D("tables\\move\\co_move"); 218 | public static readonly ushort[,] moveEO = BinLoad.loadShortTable2D("tables\\move\\eo_move"); 219 | public static readonly ushort[,] moveUD = BinLoad.loadShortTable2D("tables\\move\\ud_move"); 220 | 221 | public static readonly ushort[,] moveCP = BinLoad.loadShortTable2D("tables\\move\\cp_move"); 222 | public static readonly ushort[,] moveEP2 = BinLoad.loadShortTable2D("tables\\move\\ep2_move"); 223 | public static readonly ushort[,] moveUDS = BinLoad.loadShortTable2D("tables\\move\\uds_move"); 224 | 225 | public static readonly ushort[,] moveUS = BinLoad.loadShortTable2D("tables\\move\\us_move"); 226 | public static readonly ushort[,] moveDS = BinLoad.loadShortTable2D("tables\\move\\ds_move"); 227 | 228 | // Merge table (US-DS to EP2), size 11880 * 24 = 285120 229 | 230 | public static readonly ushort[,] mergeEP2 = BinLoad.loadShortTable2D("tables\\other\\ep2_merge", 24); 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /TwoPhaseSolver/Cube.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace TwoPhaseSolver 8 | { 9 | public struct Cubie 10 | { 11 | public byte pos, orient; 12 | 13 | public Cubie(byte pos, byte orient) 14 | { 15 | this.pos = pos; 16 | this.orient = orient; 17 | } 18 | 19 | public override string ToString() 20 | { 21 | return "Cubie(" + pos.ToString() + ", " + orient.ToString() + ")"; 22 | } 23 | } 24 | 25 | public partial class Cube 26 | { 27 | /* Definitions based on this diagram 28 | +---------+ 29 | | 00 - 07 | 30 | | U | 31 | | | 32 | +---------+---------+---------+---------+ 33 | | 24 - 31 | 16 - 23 | 08 - 17 | 32 - 39 | 34 | | L | F | R | B | 35 | | | | | | 36 | +---------+---------+---------+---------+ 37 | | 40 - 47 | 38 | | D | 39 | | | 40 | +---------+ 41 | */ 42 | 43 | private static byte[][] udToPerm = BinLoad.getUdToPerm("tables\\other\\combi"); 44 | 45 | private static byte[] Facelets = new byte[48] 46 | { 47 | 00, 01, 02, 03, 04, 05, 06, 07, 48 | 08, 09, 10, 11, 12, 13, 14, 15, 49 | 16, 17, 18, 19, 20, 21, 22, 23, 50 | 24, 25, 26, 27, 28, 29, 30, 31, 51 | 32, 33, 34, 35, 36, 37, 38, 39, 52 | 40, 41, 42, 43, 44, 45, 46, 47 53 | }; 54 | 55 | private static byte[][] CornerMap = new byte[8][] 56 | { 57 | new byte[3] {0, 1, 2}, 58 | new byte[3] {0, 2, 3}, 59 | new byte[3] {0, 3, 4}, 60 | new byte[3] {0, 4, 1}, 61 | new byte[3] {5, 2, 1}, 62 | new byte[3] {5, 3, 2}, 63 | new byte[3] {5, 4, 3}, 64 | new byte[3] {5, 1, 4} 65 | }; 66 | 67 | private static byte[][] EdgeMap = new byte[12][] 68 | { 69 | new byte[2] {0, 1}, 70 | new byte[2] {0, 2}, 71 | new byte[2] {0, 3}, 72 | new byte[2] {0, 4}, 73 | new byte[2] {5, 1}, 74 | new byte[2] {5, 2}, 75 | new byte[2] {5, 3}, 76 | new byte[2] {5, 4}, 77 | new byte[2] {2, 1}, 78 | new byte[2] {2, 3}, 79 | new byte[2] {4, 3}, 80 | new byte[2] {4, 1} 81 | }; 82 | 83 | private static byte[][] CornerFacelet = new byte[8][] 84 | { 85 | new byte[3] {0x00 + 4, 0x08 + 0, 0x10 + 2}, 86 | new byte[3] {0x00 + 6, 0x10 + 0, 0x18 + 2}, 87 | new byte[3] {0x00 + 0, 0x18 + 0, 0x20 + 2}, 88 | new byte[3] {0x00 + 2, 0x20 + 0, 0x08 + 2}, 89 | new byte[3] {0x28 + 2, 0x10 + 4, 0x08 + 6}, 90 | new byte[3] {0x28 + 0, 0x18 + 4, 0x10 + 6}, 91 | new byte[3] {0x28 + 6, 0x20 + 4, 0x18 + 6}, 92 | new byte[3] {0x28 + 4, 0x08 + 4, 0x20 + 6}, 93 | }; 94 | 95 | private static byte[][] EdgeFacelet = new byte[12][] 96 | { 97 | new byte[2] {0x00 + 3, 0x08 + 1}, 98 | new byte[2] {0x00 + 5, 0x10 + 1}, 99 | new byte[2] {0x00 + 7, 0x18 + 1}, 100 | new byte[2] {0x00 + 1, 0x20 + 1}, 101 | new byte[2] {0x28 + 3, 0x08 + 5}, 102 | new byte[2] {0x28 + 1, 0x10 + 5}, 103 | new byte[2] {0x28 + 7, 0x18 + 5}, 104 | new byte[2] {0x28 + 5, 0x20 + 5}, 105 | new byte[2] {0x10 + 3, 0x08 + 7}, 106 | new byte[2] {0x10 + 7, 0x18 + 3}, 107 | new byte[2] {0x20 + 3, 0x18 + 7}, 108 | new byte[2] {0x20 + 7, 0x08 + 3}, 109 | }; 110 | 111 | private static byte[] USlice = new byte[4] { 0, 1, 2, 3 }; 112 | private static byte[] DSlice = new byte[4] { 4, 5, 6, 7 }; 113 | private static byte[] UDSlice = new byte[4] { 8, 9, 10, 11 }; 114 | 115 | public Cubie[] corners; 116 | public Cubie[] edges; 117 | 118 | public Cube(Cubie[] corners = null, Cubie[] edges = null) 119 | { 120 | this.corners = corners ?? new Cubie[8] 121 | { 122 | new Cubie(0, 0), new Cubie(1, 0), new Cubie(2, 0), new Cubie(3, 0), 123 | new Cubie(4, 0), new Cubie(5, 0), new Cubie(6, 0), new Cubie(7, 0) 124 | }; 125 | 126 | this.edges = edges ?? new Cubie[12] 127 | { 128 | new Cubie(0, 0), new Cubie(1, 0), new Cubie(2, 0), new Cubie(3, 0), 129 | new Cubie(4, 0), new Cubie(5, 0), new Cubie(6, 0), new Cubie(7, 0), 130 | new Cubie(8, 0), new Cubie(9, 0), new Cubie(10, 0), new Cubie(11, 0) 131 | }; 132 | } 133 | 134 | public Cube(byte[] faceletColors) 135 | { 136 | corners = new Cubie[8]; 137 | edges = new Cubie[12]; 138 | byte[] tuple, cubie; 139 | int i, o, val; 140 | 141 | for (i = 0; i < 8; i++) 142 | { 143 | tuple = CornerFacelet[i]; 144 | cubie = tuple.Select(x => faceletColors[x]).ToArray(); 145 | val = CornerMap.Index(cubie, Tools.setEquals); 146 | o = cubie.Index(CornerMap[val][0]); 147 | corners[i] = new Cubie((byte)val, (byte)o); 148 | } 149 | 150 | for (i = 0; i < 12; i++) 151 | { 152 | tuple = EdgeFacelet[i]; 153 | cubie = tuple.Select(x => faceletColors[x]).ToArray(); 154 | val = EdgeMap.Index(cubie, Tools.setEquals); 155 | o = cubie.Index(EdgeMap[val][0]); 156 | edges[i] = new Cubie((byte)val, (byte)o); 157 | } 158 | } 159 | 160 | public Cube(Cube other) 161 | { 162 | corners = other.corners.Select(x => new Cubie(x.pos, x.orient)).ToArray(); 163 | edges = other.edges.Select(x => new Cubie(x.pos, x.orient)).ToArray(); 164 | } 165 | 166 | public byte[] getFacelets() 167 | { 168 | byte[] facelets = new byte[48]; 169 | int i, j; 170 | 171 | for (i = 0; i < 8; i++) 172 | { 173 | Cubie c = corners[i]; 174 | var ct = CornerFacelet[c.pos].rotate(c.orient); 175 | for (j = 0; j < 3; j++) { facelets[CornerFacelet[i][j]] = ct[j]; } 176 | } 177 | 178 | for (i = 0; i < 12; i++) 179 | { 180 | Cubie e = edges[i]; 181 | var et = EdgeFacelet[e.pos].rotate(e.orient); 182 | for (j = 0; j < 2; j++) { facelets[EdgeFacelet[i][j]] = et[j]; } 183 | } 184 | 185 | return facelets; 186 | } 187 | 188 | public byte[] getFaceletColors() 189 | { 190 | return getFacelets().Select(x => (byte)(x / 8)).ToArray(); 191 | } 192 | 193 | public void apply(Move m) 194 | { 195 | Cube c = m.apply(this); 196 | edges = c.edges; 197 | corners = c.corners; 198 | } 199 | 200 | public bool isSolved() 201 | { 202 | return ( 203 | cornOrientCoord() == 0 && 204 | edgeOrientCoord() == 0 && 205 | cornPermCoord() == 0 && 206 | edgePermCoord() == 0 207 | ); 208 | } 209 | 210 | public bool isPhase2() 211 | { 212 | return ( 213 | cornOrientCoord() == 0 && 214 | edgeOrientCoord() == 0 && 215 | UDSliceCoord() == 0 216 | ); 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /TwoPhaseSolver/Search.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Diagnostics; 7 | 8 | namespace TwoPhaseSolver 9 | { 10 | public static class Search 11 | { 12 | static byte[] ax = new byte[31]; 13 | static byte[] po = new byte[31]; 14 | 15 | static ushort[] flip = new ushort[31]; 16 | static ushort[] twist = new ushort[31]; 17 | static ushort[] udslice = new ushort[31]; 18 | 19 | static ushort[] edgeperm = new ushort[31]; 20 | static ushort[] cornperm = new ushort[31]; 21 | 22 | static ushort[] udsliceS = new ushort[31]; 23 | static ushort[] usliceS = new ushort[31]; 24 | static ushort[] dsliceS = new ushort[31]; 25 | 26 | static byte[] minDistPhase1 = new byte[31]; 27 | static byte[] minDistPhase2 = new byte[31]; 28 | 29 | static Move axPoToMove(int length, int depthPhase1 = -1) 30 | { 31 | byte[] vals = new byte[length]; 32 | for (int i = 0; i < length; i++) 33 | { 34 | vals[i] = (byte)(ax[i] * 3 + po[i] - 1); 35 | } 36 | 37 | if (depthPhase1 != -1) 38 | { 39 | var p1move = new Move(vals.Take(depthPhase1).ToArray()); 40 | var p2move = new Move(vals.Skip(depthPhase1).ToArray()); 41 | 42 | Tools.ColorPrint(string.Format("Phase 1 Move : {0} ({1})\n", p1move, depthPhase1), ConsoleColor.Cyan); 43 | Tools.ColorPrint(string.Format("Phase 2 Move : {0} ({1})\n", p2move, length - depthPhase1), ConsoleColor.Cyan); 44 | } 45 | 46 | return new Move(vals); 47 | } 48 | 49 | public static Move fullSolve(Cube cube, int maxDepth, int timeoutMS = 6000, bool printInfo = false) 50 | { 51 | if (printInfo) 52 | Tools.ColorPrint( 53 | string.Format("SOLVER START, MAX DEPTH {0}, TIME ALLOWED {1}MS\n\n", maxDepth, timeoutMS), 54 | ConsoleColor.Cyan 55 | ); 56 | 57 | if (cube.isSolved()) 58 | { 59 | if (printInfo) Tools.ColorPrint("CUBE ALREADY SOLVED... MAKE IT HARDER NEXT TIME\n", ConsoleColor.Cyan); 60 | return Move.None; 61 | } 62 | 63 | ax[0] = 0; 64 | po[0] = 0; 65 | 66 | flip[0] = cube.edgeOrientCoord(); 67 | twist[0] = cube.cornOrientCoord(); 68 | udslice[0] = cube.UDSliceCoord(); 69 | 70 | cornperm[0] = cube.cornPermCoord(); 71 | 72 | udsliceS[0] = cube.UDSliceCoordS(); 73 | usliceS[0] = cube.USliceCoordS(); 74 | dsliceS[0] = cube.DSliceCoordS(); 75 | 76 | minDistPhase1[1] = 1; 77 | int mv = 0, n = 0, depthPhase1 = 1; 78 | bool busy = false; 79 | 80 | Stopwatch watch = new Stopwatch(); 81 | watch.Start(); 82 | 83 | if (printInfo) Tools.ColorPrint(string.Format( 84 | "CUBE COORDINATES\n" + 85 | "\n" + 86 | " ORIENTATIONS\n" + 87 | " Corner : {0}\n" + 88 | " Edge : {1}\n" + 89 | "\n" + 90 | " PERMUTATIONS\n" + 91 | " Corner : {2}\n" + 92 | " Edge : {3}\n" + 93 | "\n" + 94 | " SORTED COMBINATIONS\n" + 95 | " U-Slice : {4}\n" + 96 | " D-Slice : {5}\n" + 97 | " UD-Slice : {6}\n" + 98 | "\n", 99 | twist[0], flip[0], cornperm[0], cube.edgePermCoord(), 100 | usliceS[0], dsliceS[0], udsliceS[0] 101 | ), ConsoleColor.DarkCyan); 102 | 103 | while (true) 104 | { 105 | do 106 | { 107 | if (depthPhase1 - n > minDistPhase1[n + 1] && !busy) 108 | { 109 | if (ax[n] == 0 || ax[n] == 3) { ax[++n] = 1; } 110 | else { ax[++n] = 0; } 111 | 112 | po[n] = 1; 113 | } 114 | else if (++po[n] > 3) 115 | { 116 | do 117 | { 118 | if (++ax[n] > 5) 119 | { 120 | if (watch.ElapsedMilliseconds > timeoutMS) 121 | { 122 | throw new TimeoutException("Not enough time"); 123 | } 124 | 125 | if (n == 0) 126 | { 127 | if (depthPhase1 > maxDepth) 128 | { 129 | throw new Exception("Max depth exceeded"); 130 | } 131 | else 132 | { 133 | depthPhase1++; 134 | ax[n] = 0; 135 | po[n] = 1; 136 | busy = false; 137 | if (printInfo) Tools.ColorPrint(string.Format("Depth {0} reached\n", depthPhase1), ConsoleColor.DarkCyan); 138 | break; 139 | } 140 | } 141 | else 142 | { 143 | n--; 144 | busy = true; 145 | break; 146 | } 147 | } 148 | else 149 | { 150 | po[n] = 1; 151 | busy = false; 152 | } 153 | } while (n != 0 && (ax[n - 1] == ax[n] || ax[n - 1] == ax[n] + 3)); 154 | } 155 | else { busy = false; } 156 | } while (busy); 157 | 158 | mv = ax[n] * 3 + po[n] - 1; 159 | 160 | flip[n + 1] = MoveTables.moveEO[flip[n], mv]; 161 | twist[n + 1] = MoveTables.moveCO[twist[n], mv]; 162 | udslice[n + 1] = MoveTables.moveUD[udslice[n], mv]; 163 | 164 | minDistPhase1[n + 1] = Math.Max( 165 | PruneTable.pruneEO[Constants.N_UD * flip[n + 1] + udslice[n + 1]], 166 | PruneTable.pruneCO[Constants.N_UD * twist[n + 1] + udslice[n + 1]] 167 | ); 168 | 169 | if (minDistPhase1[n + 1] == 0 && n >= depthPhase1 - 1) 170 | { 171 | minDistPhase1[n + 1] = 10; 172 | int s = phaseTwo(depthPhase1, maxDepth); 173 | if (n == depthPhase1 - 1 && s >= 0) 174 | { 175 | if (s == depthPhase1 || (ax[depthPhase1 - 1] != ax[depthPhase1] && ax[depthPhase1 - 1] != ax[depthPhase1] + 3)) 176 | { 177 | if (printInfo) Tools.ColorPrint(string.Format("\nSolution found, length {0}\n\n", s), ConsoleColor.Cyan); 178 | return axPoToMove(s, (printInfo) ? depthPhase1 : -1); 179 | } 180 | } 181 | } 182 | } 183 | } 184 | 185 | static int phaseTwo(int depthPhase1, int maxDepth) 186 | { 187 | int mv = 0; 188 | byte d1 = 0, d2 = 0; 189 | int maxDepthPhase2 = Math.Min(12, maxDepth - depthPhase1); 190 | 191 | for (int i = 0; i < depthPhase1; i++) 192 | { 193 | mv = 3 * ax[i] + po[i] - 1; 194 | udsliceS[i + 1] = MoveTables.moveUDS[udsliceS[i], mv]; 195 | usliceS[i + 1] = MoveTables.moveUS[usliceS[i], mv]; 196 | dsliceS[i + 1] = MoveTables.moveDS[dsliceS[i], mv]; 197 | cornperm[i + 1] = MoveTables.moveCP[cornperm[i], mv]; 198 | } 199 | 200 | edgeperm[depthPhase1] = MoveTables.mergeEP2[ 201 | usliceS[depthPhase1], 202 | dsliceS[depthPhase1] % 24 203 | ]; 204 | 205 | d1 = PruneTable.pruneCP[Constants.N_UD2 * cornperm[depthPhase1] + udsliceS[depthPhase1]]; 206 | d2 = PruneTable.pruneEP2[Constants.N_UD2 * edgeperm[depthPhase1] + udsliceS[depthPhase1]]; 207 | 208 | if (d1 > maxDepthPhase2 || d2 > maxDepthPhase2) { return -1; } 209 | 210 | minDistPhase2[depthPhase1] = Math.Max(d1, d2); 211 | if (minDistPhase2[depthPhase1] == 0) { return depthPhase1; } 212 | 213 | int depthPhase2 = 1, n = depthPhase1; 214 | bool busy = false; 215 | 216 | po[depthPhase1] = 0; 217 | ax[depthPhase1] = 0; 218 | minDistPhase2[n + 1] = 1; 219 | 220 | do 221 | { 222 | do 223 | { 224 | if (depthPhase1 + depthPhase2 - n > minDistPhase2[n + 1] && !busy) 225 | { 226 | if (ax[n] == 0 || ax[n] == 3) 227 | { 228 | ax[++n] = 1; 229 | po[n] = 2; 230 | } 231 | else 232 | { 233 | ax[++n] = 0; 234 | po[n] = 1; 235 | } 236 | 237 | break; 238 | } 239 | po[n] += (ax[n] == 0 || ax[n] == 3) ? (byte)1 : (byte)2; 240 | if (po[n] > 3) 241 | { 242 | do 243 | { 244 | if (++ax[n] > 5) 245 | { 246 | if (n == depthPhase1) 247 | { 248 | if (depthPhase2 >= maxDepthPhase2) { return -1; } 249 | else 250 | { 251 | depthPhase2++; 252 | ax[n] = 0; 253 | po[n] = 1; 254 | busy = false; 255 | break; 256 | } 257 | } 258 | else 259 | { 260 | n--; 261 | busy = true; 262 | break; 263 | } 264 | } 265 | else 266 | { 267 | po[n] = (ax[n] == 0 || ax[n] == 3) ? (byte)1 : (byte)2; 268 | busy = false; 269 | } 270 | } while (n != depthPhase1 && (ax[n - 1] == ax[n] || ax[n - 1] == ax[n] + 3)); 271 | } 272 | else { busy = false; } 273 | } while (busy); 274 | 275 | mv = 3 * ax[n] + po[n] - 1; 276 | 277 | cornperm[n + 1] = MoveTables.moveCP[cornperm[n], mv]; 278 | edgeperm[n + 1] = MoveTables.moveEP2[edgeperm[n], mv]; 279 | udsliceS[n + 1] = MoveTables.moveUDS[udsliceS[n], mv]; 280 | 281 | minDistPhase2[n + 1] = Math.Max( 282 | PruneTable.pruneCP[Constants.N_UD2 * cornperm[n + 1] + udsliceS[n + 1]], 283 | PruneTable.pruneEP2[Constants.N_UD2 * edgeperm[n + 1] + udsliceS[n + 1]] 284 | ); 285 | } while (minDistPhase2[n + 1] != 0); 286 | return depthPhase1 + depthPhase2; 287 | } 288 | 289 | public static Move patternSolve(Cube cube, Move pattern, int maxDepth = 24, int timeoutMS = 6000, bool printInfo = false) 290 | { 291 | // First solve normal cube 292 | if (printInfo) Tools.ColorPrint("FIRST SEARCH, TO SOLVED STATE\n\n", ConsoleColor.Cyan); 293 | Move toCube = fullSolve(cube, maxDepth, timeoutMS, printInfo); 294 | 295 | // Pattern is normal cube 296 | if (pattern.Length == 0) return toCube; 297 | // Pattern fits under maxDepth 298 | if (pattern.Length + toCube.Length <= maxDepth) return toCube + pattern; 299 | 300 | // Merge the inverse pattern and the inverse solve 301 | Move merged = pattern.inverse() + toCube.inverse(); 302 | // Apply merged to a solved cube and solve 303 | Cube cubePlusPattern = merged.apply(new Cube()); 304 | 305 | // Solve this cube 306 | if (printInfo) Tools.ColorPrint("\nSECOND SEARCH, TO PATTERN\n\n", ConsoleColor.Cyan); 307 | return fullSolve(cubePlusPattern, maxDepth, timeoutMS, printInfo); 308 | } 309 | } 310 | } 311 | --------------------------------------------------------------------------------