using System; namespace NEA { public enum Color:byte { Y, W, B, G, R, O } public enum Move { U, rU, D, rD, R, rR, L, rL, F, rF, B, rB, None } /// SIDE public struct Side { public Color[,] sideArray; public Color topLeft; public Color topMiddle; public Color topRight; public Color middleLeft; public Color middleMiddle; public Color middleRight; public Color bottomLeft; public Color bottomMiddle; public Color bottomRight; public Side(string colour) { switch (colour) { case "Y": this.topLeft = this.topMiddle = this.topRight = this.middleLeft = this.middleMiddle = this.middleRight = this.bottomLeft = this.bottomMiddle = this.bottomRight = Color.Y; break; case "W": this.topLeft = this.topMiddle = this.topRight = this.middleLeft = this.middleMiddle = this.middleRight = this.bottomLeft = this.bottomMiddle = this.bottomRight = Color.W; break; case "B": this.topLeft = this.topMiddle = this.topRight = this.middleLeft = this.middleMiddle = this.middleRight = this.bottomLeft = this.bottomMiddle = this.bottomRight = Color.B; break; case "G": this.topLeft = this.topMiddle = this.topRight = this.middleLeft = this.middleMiddle = this.middleRight = this.bottomLeft = this.bottomMiddle = this.bottomRight = Color.G; break; case "R": this.topLeft = this.topMiddle = this.topRight = this.middleLeft = this.middleMiddle = this.middleRight = this.bottomLeft = this.bottomMiddle = this.bottomRight = Color.R; break; case "O": this.topLeft = this.topMiddle = this.topRight = this.middleLeft = this.middleMiddle = this.middleRight = this.bottomLeft = this.bottomMiddle = this.bottomRight = Color.O; break; } sideArray = new Color[3, 3] { { this.topLeft, this.topMiddle, this.topRight }, { this.middleLeft, this.middleMiddle, this.middleRight }, { this.bottomLeft, this.bottomMiddle, this.bottomRight } }; } public void ReassignPieceColor() { this.topLeft = sideArray[0, 0]; this.topMiddle = sideArray[0, 1]; this.topRight = sideArray[0, 2]; this.middleLeft = sideArray[1, 0]; this.middleMiddle = sideArray[1, 1]; this.middleRight = sideArray[1, 2]; this.bottomLeft = sideArray[2, 0]; this.bottomMiddle = sideArray[2, 1]; this.bottomRight = sideArray[2, 2]; } public Color[] GetRow(int row) { return new Color[3] {sideArray[row, 0] , sideArray[row, 1] , sideArray[row, 2]}; } public Color[] GetColumn(int column) { return new Color[3] { sideArray[0, column], sideArray[1, column], sideArray[2, column] }; } public void SetRow(int row, Color[] rowContent) { sideArray[row, 0] = rowContent[0]; sideArray[row, 1] = rowContent[1]; sideArray[row, 2] = rowContent[2]; } public void SetColumn(int column, Color[] columnContent) { sideArray[0, column] = columnContent[0]; sideArray[1, column] = columnContent[1]; sideArray[2, column] = columnContent[2]; } public override string ToString() { return $"{topLeft}\t{topMiddle}\t{topRight}\n\n{middleLeft}\t{middleMiddle}\t{middleRight}\n\n{bottomLeft}\t{bottomMiddle}\t{bottomRight}"; } public void RotateClockwise() { Color temp; //rotate corners temp = sideArray[0, 0]; sideArray[0, 0] = sideArray[2, 0]; sideArray[2, 0] = sideArray[2, 2]; sideArray[2, 2] = sideArray[0, 2]; sideArray[0, 2] = temp; //rotate side pieces temp = sideArray[0, 1]; sideArray[0, 1] = sideArray[1, 0]; sideArray[1, 0] = sideArray[2, 1]; sideArray[2, 1] = sideArray[1, 2]; sideArray[1, 2] = temp; ReassignPieceColor(); } public void RotateAntiClockwise() { Color temp; //rotate corners temp = sideArray[0,0]; sideArray[0, 0] = sideArray[0, 2]; sideArray[0, 2] = sideArray[2, 2]; sideArray[2, 2] = sideArray[2, 0]; sideArray[2, 0] = temp; //rotate side pieces temp = sideArray[0, 1]; sideArray[0, 1] = sideArray[1, 2]; sideArray[1, 2] = sideArray[2, 1]; sideArray[2, 1] = sideArray[1, 0]; sideArray[1, 0] = temp; ReassignPieceColor(); } public Color[] GetSideColors() { this.ReassignPieceColor(); return new Color[9] { this.topLeft, this.topMiddle, this.topRight, this.middleLeft, this.middleMiddle, this.middleRight, this.bottomLeft, this.bottomMiddle, this.bottomRight }; } } // NEED TO ADD METHOD TO RETURN ENTIRE CUBE STATE FOR ALL SIX SIDES // NEED TO ADD METHOD TO MAKE A CUBE FROM AN ARRAY OF COLOURS, UDLRFB /// CUBE public struct Cube { public Side upSide; public Side downSide; public Side leftSide; public Side rightSide; public Side frontSide; public Side backSide; public static Color[] solvedState = new Cube().GetCubeState(); public Cube() { this.upSide = new Side("Y"); this.downSide = new Side("W"); this.leftSide = new Side("B"); this.rightSide = new Side("G"); this.frontSide = new Side("R"); this.backSide = new Side("O"); } //public Cube(string stringState) //{ // List cubeState; // for (int i = 0; i < 54; i++) // { // switch(stringState[i].ToString()) // { // case "Y": cubeState.Add(Color.Y);break; // case "W": cubeState.Add(Color.W);break; // case "B": cubeState.Add(Color.B);break; // case "G": cubeState.Add(Color.G);break; // case "R": cubeState.Add(Color.R);break; // case "O": cubeState.Add(Color.O);break; // } // } // this.upSide.SetRow(0, cubeState.ToList().GetRange(0, 3).ToArray()); // this.upSide.SetRow(1, cubeState.ToList().GetRange(3, 3).ToArray()); // this.upSide.SetRow(2, cubeState.ToList().GetRange(6, 3).ToArray()); // this.downSide.SetRow(0, cubeState.ToList().GetRange(9, 3).ToArray()); // this.downSide.SetRow(1, cubeState.ToList().GetRange(12, 3).ToArray()); // this.downSide.SetRow(2, cubeState.ToList().GetRange(15, 3).ToArray()); // this.leftSide.SetRow(0, cubeState.ToList().GetRange(18, 3).ToArray()); // this.leftSide.SetRow(1, cubeState.ToList().GetRange(21, 3).ToArray()); // this.leftSide.SetRow(2, cubeState.ToList().GetRange(24, 3).ToArray()); // this.rightSide.SetRow(0, cubeState.ToList().GetRange(27, 3).ToArray()); // this.rightSide.SetRow(1, cubeState.ToList().GetRange(30, 3).ToArray()); // this.rightSide.SetRow(2, cubeState.ToList().GetRange(33, 3).ToArray()); // this.frontSide.SetRow(0, cubeState.ToList().GetRange(36, 3).ToArray()); // this.frontSide.SetRow(1, cubeState.ToList().GetRange(39, 3).ToArray()); // this.frontSide.SetRow(2, cubeState.ToList().GetRange(42, 3).ToArray()); // this.backSide.SetRow(0, cubeState.ToList().GetRange(45, 3).ToArray()); // this.backSide.SetRow(1, cubeState.ToList().GetRange(48, 3).ToArray()); // this.backSide.SetRow(2, cubeState.ToList().GetRange(51, 3).ToArray()); // RefreshCubeState(); //} public Cube(Color[] cubeState) { this.upSide = new Side("Y"); this.downSide = new Side("W"); this.leftSide = new Side("B"); this.rightSide = new Side("G"); this.frontSide = new Side("R"); this.backSide = new Side("O"); //Every 9 elements in cubeState array are a new side this.upSide.SetRow(0, cubeState.ToList().GetRange(0, 3).ToArray()); this.upSide.SetRow(1, cubeState.ToList().GetRange(3, 3).ToArray()); this.upSide.SetRow(2, cubeState.ToList().GetRange(6, 3).ToArray()); this.downSide.SetRow(0, cubeState.ToList().GetRange(9, 3).ToArray()); this.downSide.SetRow(1, cubeState.ToList().GetRange(12, 3).ToArray()); this.downSide.SetRow(2, cubeState.ToList().GetRange(15, 3).ToArray()); this.leftSide.SetRow(0, cubeState.ToList().GetRange(18, 3).ToArray()); this.leftSide.SetRow(1, cubeState.ToList().GetRange(21, 3).ToArray()); this.leftSide.SetRow(2, cubeState.ToList().GetRange(24, 3).ToArray()); this.rightSide.SetRow(0, cubeState.ToList().GetRange(27, 3).ToArray()); this.rightSide.SetRow(1, cubeState.ToList().GetRange(30, 3).ToArray()); this.rightSide.SetRow(2, cubeState.ToList().GetRange(33, 3).ToArray()); this.frontSide.SetRow(0, cubeState.ToList().GetRange(36, 3).ToArray()); this.frontSide.SetRow(1, cubeState.ToList().GetRange(39, 3).ToArray()); this.frontSide.SetRow(2, cubeState.ToList().GetRange(42, 3).ToArray()); this.backSide.SetRow(0, cubeState.ToList().GetRange(45, 3).ToArray()); this.backSide.SetRow(1, cubeState.ToList().GetRange(48, 3).ToArray()); this.backSide.SetRow(2, cubeState.ToList().GetRange(51, 3).ToArray()); RefreshCubeState(); } public override string ToString() { return $"UpSide:\n{upSide}\nDownSide:\n{downSide}\nLeftSide:\n{leftSide}\nRightSide:\n{rightSide}\nFrontSide:\n{frontSide}\nBackSide:\n{backSide}"; } //METHOD FOR CURRENT CUBESTATE RETURN public void MakeMove(Move move) { Color[] temp; switch (move) { case Move.U: temp = this.frontSide.GetRow(0); this.frontSide.SetRow(0, this.rightSide.GetRow(0)); this.rightSide.SetRow(0, this.backSide.GetRow(0)); this.backSide.SetRow(0, this.leftSide.GetRow(0)); this.leftSide.SetRow(0, temp); this.upSide.RotateClockwise(); RefreshCubeState(); break; case Move.rU: temp = this.frontSide.GetRow(0); this.frontSide.SetRow(0, this.leftSide.GetRow(0)); this.leftSide.SetRow(0, this.backSide.GetRow(0)); this.backSide.SetRow(0, this.rightSide.GetRow(0)); this.rightSide.SetRow(0, temp); this.upSide.RotateAntiClockwise(); RefreshCubeState(); break; case Move.D: temp = this.frontSide.GetRow(2); this.frontSide.SetRow(2, this.leftSide.GetRow(2)); this.leftSide.SetRow(2, this.backSide.GetRow(2)); this.backSide.SetRow(2, this.rightSide.GetRow(2)); this.rightSide.SetRow(2, temp); this.downSide.RotateClockwise(); RefreshCubeState(); break; case Move.rD: temp = this.frontSide.GetRow(2); this.frontSide.SetRow(2, this.rightSide.GetRow(2)); this.rightSide.SetRow(2, this.backSide.GetRow(2)); this.backSide.SetRow(2, this.leftSide.GetRow(2)); this.leftSide.SetRow(2, temp); this.downSide.RotateAntiClockwise(); RefreshCubeState(); break; case Move.R: temp = this.frontSide.GetColumn(2); this.frontSide.SetColumn(2, this.downSide.GetColumn(2)); this.downSide.SetColumn(2, this.backSide.GetColumn(0).Reverse().ToArray()); this.backSide.SetColumn(0, this.upSide.GetColumn(2).Reverse().ToArray()); this.upSide.SetColumn(2, temp); this.rightSide.RotateClockwise(); RefreshCubeState(); break; case Move.rR: temp = this.frontSide.GetColumn(2); this.frontSide.SetColumn(2, this.upSide.GetColumn(2)); this.upSide.SetColumn(2, this.backSide.GetColumn(0).Reverse().ToArray()); this.backSide.SetColumn(0, this.downSide.GetColumn(2).Reverse().ToArray()); this.downSide.SetColumn(2, temp); this.rightSide.RotateAntiClockwise(); RefreshCubeState(); break; case Move.L: temp = this.frontSide.GetColumn(0); this.frontSide.SetColumn(0, this.upSide.GetColumn(0)); this.upSide.SetColumn(0, this.backSide.GetColumn(2).Reverse().ToArray()); this.backSide.SetColumn(2, this.downSide.GetColumn(0).Reverse().ToArray()); this.downSide.SetColumn(0, temp); this.leftSide.RotateClockwise(); RefreshCubeState(); break; case Move.rL: temp = this.frontSide.GetColumn(0); this.frontSide.SetColumn(0, this.downSide.GetColumn(0)); this.downSide.SetColumn(0, this.backSide.GetColumn(2).Reverse().ToArray()); this.backSide.SetColumn(2, this.upSide.GetColumn(0).Reverse().ToArray()); this.upSide.SetColumn(0, temp); this.leftSide.RotateAntiClockwise(); RefreshCubeState(); break; case Move.F: temp = this.rightSide.GetColumn(0); this.rightSide.SetColumn(0, this.upSide.GetRow(2)); this.upSide.SetRow(2, this.leftSide.GetColumn(2).Reverse().ToArray()); this.leftSide.SetColumn(2, this.downSide.GetRow(0)); this.downSide.SetRow(0, temp.Reverse().ToArray()); this.frontSide.RotateClockwise(); RefreshCubeState(); break; case Move.rF: temp = this.rightSide.GetColumn(0); this.rightSide.SetColumn(0, this.downSide.GetRow(0).Reverse().ToArray()); this.downSide.SetRow(0, this.leftSide.GetColumn(2)); this.leftSide.SetColumn(2, this.upSide.GetRow(2).Reverse().ToArray()); this.upSide.SetRow(2, temp); this.frontSide.RotateAntiClockwise(); RefreshCubeState(); break; case Move.B: temp = this.rightSide.GetColumn(2); this.rightSide.SetColumn(2, this.downSide.GetRow(2).Reverse().ToArray()); this.downSide.SetRow(2, this.leftSide.GetColumn(0)); this.leftSide.SetColumn(0, this.upSide.GetRow(0).Reverse().ToArray()); this.upSide.SetRow(0, temp); this.backSide.RotateClockwise(); RefreshCubeState(); break; case Move.rB: temp = rightSide.GetColumn(2); this.rightSide.SetColumn(2, this.upSide.GetRow(0)); this.upSide.SetRow(0, this.leftSide.GetColumn(0).Reverse().ToArray()); this.leftSide.SetColumn(0, this.downSide.GetRow(2)); this.downSide.SetRow(2, temp.Reverse().ToArray()); this.backSide.RotateAntiClockwise(); RefreshCubeState(); break; } } public void RefreshCubeState() { this.frontSide.ReassignPieceColor(); this.leftSide.ReassignPieceColor(); this.backSide.ReassignPieceColor(); this.rightSide.ReassignPieceColor(); this.downSide.ReassignPieceColor(); this.upSide.ReassignPieceColor(); } public Color[] GetCubeState() { List cubeState = new(); cubeState.AddRange(this.upSide.GetSideColors()); cubeState.AddRange(this.downSide.GetSideColors()); cubeState.AddRange(this.leftSide.GetSideColors()); cubeState.AddRange(this.rightSide.GetSideColors()); cubeState.AddRange(this.frontSide.GetSideColors()); cubeState.AddRange(this.backSide.GetSideColors()); return cubeState.ToArray(); } public bool IsSolved() { Color[] currentState = this.GetCubeState(); if (currentState.Length != solvedState.Length) return false; for (int i = 0; i < currentState.Length; i++) { if (currentState[i] != solvedState[i]) return false; } return true; } } } using System; namespace NEA { public class TableNode { public int[] cubeState; public Move move; public byte level; public TableNode(int[] acubeState) { cubeState = acubeState; } } public class Node { public byte level = 0; public Move move; public Cube cube; public int lastIndex; //for user-inputted cube public Node(Cube acube, Move amove, int depth) { cube = acube; move = amove; level = (byte)depth; } public Node(Cube acube) { cube = acube; } } //public class Graph //{ // public List nodes = new(); // public Graph(Node rootNode) // { // this.nodes.Add(rootNode); // } // public void AddNode(Node node) // { // this.nodes.Add(node); // } // public void AddEdge(Node node, Node parentNode) // { // parentNode.lastIndex++; // parentNode.childrenNodes[parentNode.lastIndex-1] = node; // } //} } using System; namespace NEA { public static class Utilities { public static Color[] MakeMoveArray(Color[] cubeState, Move move) { Color[] temp = new Color[3]; Color temp2; /* INDEX RANGE IN ARRAY: * UpSide 0-8 * DownSide 9-17 * LeftSide 18-26 * RightSide 27-35 * FrontSide 36-44 * BackSide 45-53 */ switch (move) { case Move.U: //swaps the rows of colours temp = cubeState.Skip(36).Take(3).ToArray(); Array.Copy(cubeState, 27, cubeState, 36, 3); Array.Copy(cubeState, 45, cubeState, 27, 3); Array.Copy(cubeState, 18, cubeState, 45, 3); Array.Copy(temp, 0, cubeState, 18, 3); //rotate the face //rotate corners temp2 = cubeState[0]; cubeState[0] = cubeState[6]; cubeState[6] = cubeState[8]; cubeState[8] = cubeState[2]; cubeState[2] = temp2; //rotate edges temp2 = cubeState[1]; cubeState[1] = cubeState[3]; cubeState[3] = cubeState[7]; cubeState[7] = cubeState[5]; cubeState[5] = temp2; break; case Move.rU: temp = cubeState.Skip(36).Take(3).ToArray(); Array.Copy(cubeState, 18, cubeState, 36, 3); Array.Copy(cubeState, 45, cubeState, 18, 3); Array.Copy(cubeState, 27, cubeState, 45, 3); Array.Copy(temp, 0, cubeState, 27, 3); temp2 = cubeState[0]; cubeState[0] = cubeState[2]; cubeState[2] = cubeState[8]; cubeState[8] = cubeState[6]; cubeState[6] = temp2; temp2 = cubeState[1]; cubeState[1] = cubeState[5]; cubeState[5] = cubeState[7]; cubeState[7] = cubeState[3]; cubeState[3] = temp2; break; case Move.D: temp = cubeState.Skip(42).Take(3).ToArray(); Array.Copy(cubeState, 24, cubeState, 42, 3); Array.Copy(cubeState, 51, cubeState, 24, 3); Array.Copy(cubeState, 33, cubeState, 51, 3); Array.Copy(temp, 0, cubeState, 33, 3); temp2 = cubeState[9]; cubeState[9] = cubeState[15]; cubeState[15] = cubeState[17]; cubeState[17] = cubeState[11]; cubeState[11] = temp2; temp2 = cubeState[10]; cubeState[10] = cubeState[12]; cubeState[12] = cubeState[16]; cubeState[16] = cubeState[14]; cubeState[14] = temp2; break; case Move.rD: temp = cubeState.Skip(42).Take(3).ToArray(); Array.Copy(cubeState, 33, cubeState, 42, 3); Array.Copy(cubeState, 51, cubeState, 33, 3); Array.Copy(cubeState, 24, cubeState, 51, 3); Array.Copy(temp, 0, cubeState, 24, 3); temp2 = cubeState[9]; cubeState[9] = cubeState[11]; cubeState[11] = cubeState[17]; cubeState[17] = cubeState[15]; cubeState[15] = temp2; temp2 = cubeState[10]; cubeState[10] = cubeState[14]; cubeState[14] = cubeState[16]; cubeState[16] = cubeState[12]; cubeState[12] = temp2; break; case Move.R: temp[0] = cubeState[38]; temp[1] = cubeState[41]; temp[2] = cubeState[44]; cubeState[38] = cubeState[11]; cubeState[41] = cubeState[14]; cubeState[44] = cubeState[17]; cubeState[11] = cubeState[51]; cubeState[14] = cubeState[48]; cubeState[17] = cubeState[45]; cubeState[45] = cubeState[8]; cubeState[48] = cubeState[5]; cubeState[51] = cubeState[2]; cubeState[2] = temp[0]; cubeState[5] = temp[1]; cubeState[8] = temp[2]; temp2 = cubeState[27]; cubeState[27] = cubeState[33]; cubeState[33] = cubeState[35]; cubeState[35] = cubeState[29]; cubeState[29] = temp2; temp2 = cubeState[28]; cubeState[28] = cubeState[30]; cubeState[30] = cubeState[34]; cubeState[34] = cubeState[32]; cubeState[32] = temp2; break; case Move.rR: temp[0] = cubeState[38]; temp[1] = cubeState[41]; temp[2] = cubeState[44]; cubeState[38] = cubeState[2]; cubeState[41] = cubeState[5]; cubeState[44] = cubeState[8]; cubeState[2] = cubeState[51]; cubeState[5] = cubeState[48]; cubeState[8] = cubeState[45]; cubeState[45] = cubeState[17]; cubeState[48] = cubeState[14]; cubeState[51] = cubeState[11]; cubeState[11] = temp[0]; cubeState[14] = temp[1]; cubeState[17] = temp[2]; temp2 = cubeState[27]; cubeState[27] = cubeState[29]; cubeState[29] = cubeState[35]; cubeState[35] = cubeState[33]; cubeState[33] = temp2; temp2 = cubeState[28]; cubeState[28] = cubeState[32]; cubeState[32] = cubeState[34]; cubeState[34] = cubeState[30]; cubeState[30] = temp2; break; case Move.L: temp[0] = cubeState[36]; temp[1] = cubeState[39]; temp[2] = cubeState[42]; cubeState[36] = cubeState[0]; cubeState[39] = cubeState[3]; cubeState[42] = cubeState[6]; cubeState[0] = cubeState[53]; cubeState[3] = cubeState[50]; cubeState[6] = cubeState[47]; cubeState[47] = cubeState[15]; cubeState[50] = cubeState[12]; cubeState[53] = cubeState[9]; cubeState[9] = temp[0]; cubeState[12] = temp[1]; cubeState[15] = temp[2]; temp2 = cubeState[18]; cubeState[18] = cubeState[24]; cubeState[24] = cubeState[26]; cubeState[26] = cubeState[20]; cubeState[20] = temp2; temp2 = cubeState[19]; cubeState[19] = cubeState[21]; cubeState[21] = cubeState[25]; cubeState[25] = cubeState[23]; cubeState[23] = temp2; break; case Move.rL: temp[0] = cubeState[36]; temp[1] = cubeState[39]; temp[2] = cubeState[42]; cubeState[36] = cubeState[9]; cubeState[39] = cubeState[12]; cubeState[42] = cubeState[15]; cubeState[9] = cubeState[53]; cubeState[12] = cubeState[50]; cubeState[15] = cubeState[47]; cubeState[47] = cubeState[6]; cubeState[50] = cubeState[3]; cubeState[53] = cubeState[0]; cubeState[0] = temp[0]; cubeState[3] = temp[1]; cubeState[6] = temp[2]; temp2 = cubeState[18]; cubeState[18] = cubeState[20]; cubeState[20] = cubeState[26]; cubeState[26] = cubeState[24]; cubeState[24] = temp2; temp2 = cubeState[19]; cubeState[19] = cubeState[23]; cubeState[23] = cubeState[25]; cubeState[25] = cubeState[21]; cubeState[21] = temp2; break; case Move.F: temp = cubeState.Skip(6).Take(3).ToArray(); cubeState[6] = cubeState[26]; cubeState[7] = cubeState[23]; cubeState[8] = cubeState[20]; cubeState[20] = cubeState[9]; cubeState[23] = cubeState[10]; cubeState[26] = cubeState[11]; cubeState[9] = cubeState[33]; cubeState[10] = cubeState[30]; cubeState[11] = cubeState[27]; cubeState[27] = temp[0]; cubeState[30] = temp[1]; cubeState[33] = temp[2]; temp2 = cubeState[36]; cubeState[36] = cubeState[42]; cubeState[42] = cubeState[44]; cubeState[44] = cubeState[38]; cubeState[38] = temp2; temp2 = cubeState[37]; cubeState[37] = cubeState[39]; cubeState[39] = cubeState[43]; cubeState[43] = cubeState[41]; cubeState[41] = temp2; break; case Move.rF: temp = cubeState.Skip(6).Take(3).ToArray(); cubeState[6] = cubeState[27]; cubeState[7] = cubeState[30]; cubeState[8] = cubeState[33]; cubeState[27] = cubeState[11]; cubeState[30] = cubeState[10]; cubeState[33] = cubeState[9]; cubeState[9] = cubeState[20]; cubeState[10] = cubeState[23]; cubeState[11] = cubeState[26]; cubeState[20] = temp[2]; cubeState[23] = temp[1]; cubeState[26] = temp[0]; temp2 = cubeState[36]; cubeState[36] = cubeState[38]; cubeState[38] = cubeState[44]; cubeState[44] = cubeState[42]; cubeState[42] = temp2; temp2 = cubeState[37]; cubeState[37] = cubeState[41]; cubeState[41] = cubeState[43]; cubeState[43] = cubeState[39]; cubeState[39] = temp2; break; case Move.B: temp = cubeState.Skip(0).Take(3).ToArray(); cubeState[0] = cubeState[29]; cubeState[1] = cubeState[32]; cubeState[2] = cubeState[35]; cubeState[29] = cubeState[17]; cubeState[32] = cubeState[16]; cubeState[35] = cubeState[15]; cubeState[15] = cubeState[18]; cubeState[16] = cubeState[21]; cubeState[17] = cubeState[24]; cubeState[18] = temp[2]; cubeState[21] = temp[1]; cubeState[24] = temp[0]; temp2 = cubeState[45]; cubeState[45] = cubeState[51]; cubeState[51] = cubeState[53]; cubeState[53] = cubeState[47]; cubeState[47] = temp2; temp2 = cubeState[46]; cubeState[46] = cubeState[48]; cubeState[48] = cubeState[52]; cubeState[52] = cubeState[50]; cubeState[50] = temp2; break; case Move.rB: temp = cubeState.Skip(0).Take(3).ToArray(); cubeState[0] = cubeState[24]; cubeState[1] = cubeState[21]; cubeState[2] = cubeState[18]; cubeState[18] = cubeState[15]; cubeState[21] = cubeState[16]; cubeState[24] = cubeState[17]; cubeState[15] = cubeState[35]; cubeState[16] = cubeState[32]; cubeState[17] = cubeState[29]; cubeState[29] = temp[0]; cubeState[32] = temp[1]; cubeState[35] = temp[2]; temp2 = cubeState[45]; cubeState[45] = cubeState[47]; cubeState[47] = cubeState[53]; cubeState[53] = cubeState[51]; cubeState[51] = temp2; temp2 = cubeState[46]; cubeState[46] = cubeState[50]; cubeState[50] = cubeState[52]; cubeState[52] = cubeState[48]; cubeState[48] = temp2; break; } return cubeState; } public static void SerializeByteArrayToBinaryFile(byte[] byteArray, string filePath) { using (FileStream fs = new FileStream(filePath, FileMode.Create)) { using (BinaryWriter writer = new BinaryWriter(fs)) { writer.Write(byteArray); Console.WriteLine("Serialization successful."); } } } public static byte[] DeserializeByteArrayFromBinaryFile(string filePath) { using (FileStream fs = new FileStream(filePath, FileMode.Open)) { using (BinaryReader reader = new BinaryReader(fs)) { int length = (int)fs.Length; return reader.ReadBytes(length); } } } } } using System; using System.Collections; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Numerics; using System.Xml.Linq; using static System.Runtime.InteropServices.JavaScript.JSType; namespace NEA { public static class PDB { public static byte[] heuristicLookUp = new byte[88179840]; public static byte[] edgesLookUpOne = new byte[42577920]; public static byte[] edgesLookUpTwo = new byte[42577920]; public static Color[] solvedState = new Cube().GetCubeState(); public static List movesList = new List { Move.U, Move.rU, Move.D, Move.rD, Move.R, Move.rR, Move.L, Move.rL, Move.F, Move.rF, Move.B, Move.rB }; public static Color[,] possibleCorners = new Color[24, 3] { { Color.B,Color.Y,Color.R }, { Color.Y,Color.R,Color.B }, { Color.R,Color.B,Color.Y }, //0-2 { Color.R,Color.Y,Color.G }, { Color.Y,Color.G,Color.R }, { Color.G,Color.R,Color.Y }, //3-5 { Color.G,Color.Y,Color.O }, { Color.Y,Color.O,Color.G }, { Color.O,Color.G,Color.Y }, //6-8 { Color.O,Color.Y,Color.B }, { Color.Y,Color.B,Color.O }, { Color.B,Color.O,Color.Y }, //9-11 { Color.R,Color.W,Color.B }, { Color.W,Color.B,Color.R }, { Color.B,Color.R,Color.W }, //12-14 { Color.G,Color.W,Color.R }, { Color.W,Color.R,Color.G }, { Color.R,Color.G,Color.W }, //15-17 { Color.O,Color.W,Color.G }, { Color.W,Color.G,Color.O }, { Color.G,Color.O,Color.W }, //18-20 { Color.B,Color.W,Color.O }, { Color.W,Color.O,Color.B }, { Color.O,Color.B,Color.W } //21-23 }; public static Color[,] possibleEdges = new Color[24, 2] { { Color.R, Color.Y },{ Color.Y, Color.R }, { Color.R, Color.G },{ Color.G, Color.R }, { Color.R, Color.W },{ Color.W, Color.R }, { Color.G, Color.Y },{ Color.Y, Color.G }, { Color.G, Color.O },{ Color.O, Color.G }, { Color.G, Color.W },{ Color.W, Color.G }, { Color.O, Color.Y },{ Color.Y, Color.O }, { Color.O, Color.B },{ Color.B, Color.O }, { Color.O, Color.W },{ Color.W, Color.O }, { Color.B, Color.Y },{ Color.Y, Color.B }, { Color.B, Color.R },{ Color.R, Color.B }, { Color.B, Color.W },{ Color.W, Color.B } }; public static byte[] GetFirstEdgeState(Color[] cubeState) { Color[,] individualEdges = new Color[6, 2] { { cubeState[37], cubeState[7] }, { cubeState[41], cubeState[30] }, { cubeState[43], cubeState[10] }, { cubeState[46], cubeState[1] }, { cubeState[50], cubeState[21] }, { cubeState[52], cubeState[16] } }; byte[] edgeNumbers = new byte[6]; for (int i = 0; i < 6; i++) { for (int j = 0; j < 24; j++) { if (individualEdges[i, 0] == possibleEdges[j, 0] && individualEdges[i, 1] == possibleEdges[j, 1]) { edgeNumbers[i] = (byte)j; break; } } } return edgeNumbers; } public static byte[] GetSecondEdgeState(Color[] cubeState) { Color[,] individualEdges = new Color[6, 2] { { cubeState[28], cubeState[5] }, { cubeState[32], cubeState[48] }, { cubeState[34], cubeState[14] }, { cubeState[19], cubeState[3] }, { cubeState[23], cubeState[39] }, { cubeState[25], cubeState[12] } }; byte[] edgeNumbers = new byte[6]; for (int i = 0; i < 6; i++) { for (int j = 0; j < 24; j++) { if (individualEdges[i, 0] == possibleEdges[j, 0] && individualEdges[i, 1] == possibleEdges[j, 1]) { edgeNumbers[i] = (byte)j; break; } } } return edgeNumbers; } public static byte[] GetCornerState(Color[] cubeState) { Color[,] individualCorners = new Color[8, 3] { { cubeState[20],cubeState[6],cubeState[36] }, { cubeState[38],cubeState[8],cubeState[27] }, { cubeState[29],cubeState[2],cubeState[45] }, { cubeState[47],cubeState[0],cubeState[18] }, { cubeState[42],cubeState[9], cubeState[26] }, { cubeState[33],cubeState[11],cubeState[44] }, { cubeState[51],cubeState[17],cubeState[35] }, { cubeState[24],cubeState[15],cubeState[53] } }; byte[] cornerNumbers = new byte[8]; for (int i = 0; i < 8; i++) { for (int j = 0; j < 24; j++) { if (individualCorners[i, 0] == possibleCorners[j, 0] && individualCorners[i, 1] == possibleCorners[j, 1] && individualCorners[i, 2] == possibleCorners[j, 2]) { cornerNumbers[i] = (byte)j; break; } } } return cornerNumbers; } public static (byte, byte) PossibleEdge(Color[] edge) { switch (edge[0], edge[1]) { case (Color.R,Color.Y): return (0, 0); case (Color.Y, Color.R): return (0, 1); case (Color.R,Color.G): return (1, 0); case (Color.G,Color.R): return (1, 1); case (Color.R,Color.W): return (2, 0); case (Color.W,Color.R): return (2, 1); case (Color.G,Color.Y): return (3, 0); case (Color.Y,Color.G): return (3, 1); case (Color.G,Color.O): return (4, 0); case (Color.O,Color.G): return (4, 1); case (Color.G,Color.W): return (5, 0); case (Color.W,Color.G): return (5, 1); case (Color.O,Color.Y): return (6, 0); case (Color.Y,Color.O): return (6, 1); case (Color.O,Color.B): return (7, 0); case (Color.B,Color.O): return (7, 1); case (Color.O,Color.W): return (8, 0); case (Color.W,Color.O): return (8, 1); case (Color.B,Color.Y): return (9, 0); case (Color.Y,Color.B): return (9, 1); case (Color.B,Color.R): return (10, 0); case (Color.R,Color.B): return (10, 1); case (Color.B,Color.W): return (11, 0); case (Color.W,Color.B): return (11, 1); } return (0, 0); } public static (byte, byte) possibleCorner(Color[] corner) { switch (corner[0], corner[1], corner[2]) { case (Color.B, Color.Y, Color.R): return (0, 0); case (Color.Y, Color.R, Color.B): return (0, 1); case (Color.R, Color.B, Color.Y): return (0, 2); case (Color.R, Color.Y, Color.G): return (1, 0); case (Color.Y, Color.G, Color.R): return (1, 1); case (Color.G, Color.R, Color.Y): return (1, 2); case (Color.G, Color.Y, Color.O): return (2, 0); case (Color.Y, Color.O, Color.G): return (2, 1); case (Color.O, Color.G, Color.Y): return (2, 2); case (Color.O, Color.Y, Color.B): return (3, 0); case (Color.Y, Color.B, Color.O): return (3, 1); case (Color.B, Color.O, Color.Y): return (3, 2); case (Color.R, Color.W, Color.B): return (4, 0); case (Color.W, Color.B, Color.R): return (4, 1); case (Color.B, Color.R, Color.W): return (4, 2); case (Color.G, Color.W, Color.R): return (5, 0); case (Color.W, Color.R, Color.G): return (5, 1); case (Color.R, Color.G, Color.W): return (5, 2); case (Color.O, Color.W, Color.G): return (6, 0); case (Color.W, Color.G, Color.O): return (6, 1); case (Color.G, Color.O, Color.W): return (6, 2); case (Color.B, Color.W, Color.O): return (7, 0); case (Color.W, Color.O, Color.B): return (7, 1); case (Color.O, Color.B, Color.W): return (7, 2); default: break; } return (0, 0); } public static (byte, byte)[] GetCornerStateTuple(Color[] cubeState) { (byte, byte)[] corners = new[] { possibleCorner(new Color[]{ cubeState[20],cubeState[6],cubeState[36] }), possibleCorner(new Color[]{ cubeState[38],cubeState[8],cubeState[27] }), possibleCorner(new Color[]{ cubeState[29],cubeState[2],cubeState[45] }), possibleCorner(new Color[]{ cubeState[47],cubeState[0],cubeState[18] }), possibleCorner(new Color[]{ cubeState[42],cubeState[9], cubeState[26] }), possibleCorner(new Color[]{ cubeState[33],cubeState[11],cubeState[44] }), possibleCorner(new Color[]{ cubeState[51],cubeState[17],cubeState[35] }), possibleCorner(new Color[]{ cubeState[24],cubeState[15],cubeState[53] }) }; return corners; } public static (byte,byte)[] GetFirstEdgeStatesTuple(Color[] cubeState) { (byte, byte)[] individualEdges = new[] { PossibleEdge(new Color[]{ cubeState[37], cubeState[7] }), PossibleEdge(new Color[]{ cubeState[41], cubeState[30] }), PossibleEdge(new Color[]{ cubeState[43], cubeState[10] }), PossibleEdge(new Color[]{ cubeState[46], cubeState[1] }), PossibleEdge(new Color[]{ cubeState[50], cubeState[21] }), PossibleEdge(new Color[]{ cubeState[52], cubeState[16] }), }; return individualEdges; } public static (byte, byte)[] GetSecondEdgeStatesTuple(Color[] cubeState) { (byte, byte)[] individualEdges = new[] { PossibleEdge(new Color[]{ cubeState[28], cubeState[5] }), PossibleEdge(new Color[]{ cubeState[32], cubeState[48] }), PossibleEdge(new Color[]{ cubeState[34], cubeState[14] }), PossibleEdge(new Color[]{ cubeState[19], cubeState[3] }), PossibleEdge(new Color[]{ cubeState[23], cubeState[39] }), PossibleEdge(new Color[]{ cubeState[25], cubeState[12] }), }; return individualEdges; } public static Move GetReverseMove(Move move) { switch (move) { case Move.R: return Move.rR; case Move.rR: return Move.R; case Move.L: return Move.rL; case Move.rL: return Move.L; case Move.D: return Move.rD; case Move.rD: return Move.D; case Move.U: return Move.rU; case Move.rU: return Move.U; case Move.F: return Move.rF; case Move.rF: return Move.F; case Move.B: return Move.rB; case Move.rB: return Move.B; } return new Move(); } public static void GenerateCornerTable() { TableNode solvedNode = new TableNode(ConvertColorsToInts(solvedState)); Queue nodeQueue = new Queue(); HashSet visited = new HashSet(); HashSet searchedStates = new HashSet(); nodeQueue.Enqueue(solvedNode); visited.Add(CornerPack(GetCornerState(ConvertIntsToColors(solvedNode.cubeState)))); while (nodeQueue.Count > 0) { TableNode currentNode = nodeQueue.Dequeue(); ulong currentCornerState = CornerPack(GetCornerState(ConvertIntsToColors(currentNode.cubeState))); if (searchedStates.Contains(currentCornerState)) continue; TableNode[] nextStates; if (currentNode.level == 0) { nextStates = GenAllMoves(currentNode); } else nextStates = GenerateNextStates(currentNode); for (int i = 0; i < nextStates.Length; i++) { TableNode nextState = nextStates[i]; byte[] nextCornerState = GetCornerState(ConvertIntsToColors(nextState.cubeState)); ulong packed = CornerPack(nextCornerState); if (!visited.Contains(packed)) { visited.Add(packed); nodeQueue.Enqueue(nextState); int index2 = (40320 * PDB.Lehmer.CornerOrientationIndex(PDB.GetCornerStateTuple(ConvertIntsToColors(nextState.cubeState)))+(PDB.Lehmer.CornerPositionIndex(PDB.GetCornerStateTuple(ConvertIntsToColors(nextState.cubeState))))); if (heuristicLookUp[index2] == 0) { heuristicLookUp[index2] = nextState.level; } } } searchedStates.Add(currentCornerState); } int zeroes = 0; foreach (var a in heuristicLookUp) { if (a == 0) { zeroes++; } } Console.WriteLine("empty spots in table: " + zeroes); Console.WriteLine("added to table: " + (88179840 - zeroes)); } public static void GenerateFirstEdgeTable() { TableNode solvedNode = new TableNode(ConvertColorsToInts(solvedState)); Queue nodeQueue = new Queue(); HashSet visited = new HashSet(); HashSet searchedStates = new HashSet(); nodeQueue.Enqueue(solvedNode); visited.Add(EdgePack(GetFirstEdgeState(ConvertIntsToColors(solvedNode.cubeState)))); while (nodeQueue.Count > 0) { TableNode currentNode = nodeQueue.Dequeue(); ulong currentEdgeState = EdgePack(GetFirstEdgeState(ConvertIntsToColors(currentNode.cubeState))); if (searchedStates.Contains(currentEdgeState)) continue; TableNode[] nextStates; if (currentNode.level == 0) { nextStates = GenAllMoves(currentNode); } else nextStates = GenerateNextStates(currentNode); for (int i = 0; i < nextStates.Length; i++) { TableNode nextState = nextStates[i]; byte[] nextEdgeState = GetFirstEdgeState(ConvertIntsToColors(nextState.cubeState)); ulong packed = EdgePack(nextEdgeState); if (!visited.Contains(packed)) { visited.Add(packed); nodeQueue.Enqueue(nextState); int index2 = (665280 * PDB.Lehmer.EdgeOrientationIndex(PDB.GetFirstEdgeStatesTuple(ConvertIntsToColors(nextState.cubeState)))) + PDB.Lehmer.EdgePositionIndex(PDB.GetFirstEdgeStatesTuple(ConvertIntsToColors(nextState.cubeState))); if (edgesLookUpOne[index2] == 0) { edgesLookUpOne[index2] = nextState.level; } } } searchedStates.Add(currentEdgeState); } int zeroes = 0; foreach (var a in edgesLookUpOne) { if (a == 0) { zeroes++; } } Console.WriteLine("empty spots in table: " + zeroes); Console.WriteLine("added to table: " + (42577920 - zeroes)); } public static void GenerateSecondEdgeTable() { TableNode solvedNode = new TableNode(ConvertColorsToInts(solvedState)); Queue nodeQueue = new Queue(); HashSet visited = new HashSet(); HashSet searchedStates = new HashSet(); nodeQueue.Enqueue(solvedNode); while (nodeQueue.Count > 0) { TableNode currentNode = nodeQueue.Dequeue(); ulong currentEdgeState = EdgePack(GetSecondEdgeState(ConvertIntsToColors(currentNode.cubeState))); if (searchedStates.Contains(currentEdgeState)) continue; TableNode[] nextStates; if (currentNode.level == 0) { nextStates = GenAllMoves(currentNode); } else nextStates = GenerateNextStates(currentNode); for (int i = 0; i < nextStates.Length; i++) { TableNode nextState = nextStates[i]; byte[] nextEdgeState = GetSecondEdgeState(ConvertIntsToColors(nextState.cubeState)); ulong packed = EdgePack(nextEdgeState); if (!visited.Contains(packed)) { visited.Add(packed); nodeQueue.Enqueue(nextState); int index2 = (665280 * PDB.Lehmer.EdgeOrientationIndex(PDB.GetSecondEdgeStatesTuple(ConvertIntsToColors(nextState.cubeState)))) + PDB.Lehmer.EdgePositionIndex(PDB.GetSecondEdgeStatesTuple(ConvertIntsToColors(nextState.cubeState))); if (edgesLookUpTwo[index2] == 0) { edgesLookUpTwo[index2] = nextState.level; } } } searchedStates.Add(currentEdgeState); } int zeroes = 0; foreach (var a in edgesLookUpTwo) { if (a == 0) { zeroes++; } } Console.WriteLine("empty spots in table: " + zeroes); Console.WriteLine("added to table: " + (42577920 - zeroes)); } public static TableNode[] GenerateNextStates(TableNode parentNode) { TableNode[] children = new TableNode[11]; int[] cubeState = parentNode.cubeState.ToArray(); sbyte index = -1; foreach (Move move in movesList) { if (move == GetReverseMove(parentNode.move)) continue; index++; Color[] parent = ConvertIntsToColors(cubeState).ToArray(); children[index] = new TableNode(ConvertColorsToInts(Utilities.MakeMoveArray(parent, move))) { move = move, level = (byte)(parentNode.level + 1) }; } return children; } public static TableNode[] GenAllMoves(TableNode parentNode) { TableNode[] children = new TableNode[12]; int[] cubeState = parentNode.cubeState.ToArray(); for (int i = 0; i < 12; i++) { Move move = movesList[i]; Color[] parent = ConvertIntsToColors(cubeState).ToArray(); children[i] = new TableNode(ConvertColorsToInts(Utilities.MakeMoveArray(parent, move))) { move = move, level = (byte)(parentNode.level + 1) }; } return children; } public static ulong CornerPack(byte[] bytes) { if (bytes.Length != 8) { throw new ArgumentException("Input must be an array of 8 bytes."); } ulong packed = 0; for (int i = 0; i < 8; i++) { packed |= ((ulong)bytes[i] << (i * 8)); } return packed; } public static ulong EdgePack(byte[] bytes) { if (bytes.Length != 6) { throw new ArgumentException("Input must be an array of 8 bytes."); } ulong packed = 0; for (int i = 0; i < 6; i++) { packed |= ((ulong)bytes[i] << (i * 8)); } return packed; } public static int[] ConvertColorsToInts(Color[] colors) { if (colors.Length != 54) throw new ArgumentException("Invalid number of colors"); int[] sides = new int[6]; for (int i = 0; i < 6; i++) { int value = 0; for (int j = 0; j < 9; j++) { value |= ((int)colors[i * 9 + j]) << (3 * j); } sides[i] = value; } return sides; } public static Color[] ConvertIntsToColors(int[] sides) { if (sides.Length != 6) throw new ArgumentException("Invalid number of sides"); Color[] colors = new Color[54]; for (int i = 0; i < 6; i++) { int value = sides[i]; for (int j = 0; j < 9; j++) { colors[i * 9 + j] = (Color)((value >> (3 * j)) & 7); } } return colors; } public class Lehmer { public static int[] cornerPositionPlaceValues = new int[8] { 5040, 720, 120, 24, 6, 2, 1, 1 }; public static int[] edgePositionPlaceValues = new int[6] { 55440, 5040, 504, 56, 7, 1 }; public static int CornerPositionIndex((byte, byte)[] corners) { int[] lehmer = new int[8]; lehmer[0] = corners[0].Item1; for (byte i = 1; i < 8; i++) { byte lessThan = 0; for (byte j = 0; j < i; j++) { if (corners[j].Item1 < corners[i].Item1) { lessThan++; } } lehmer[i] = (byte)(corners[i].Item1 - lessThan); } int total = 0; for (int i = 0; i < 7; i++) { total += lehmer[i] * cornerPositionPlaceValues[i]; } return total; } public static int CornerOrientationIndex((byte, byte)[] corners) { byte[] orient = new byte[8]; for (int i = 0; i < 8; i++) { orient[i] = corners[i].Item2; } string base3 = string.Join("", orient.Select(b => b.ToString())); // convert the base 3 string to a base 10 integer int base10 = 0; for (int i = 0; i < base3.Length; i++) { // get the digit at the current position int digit = int.Parse(base3[i].ToString()); // check if the digit is valid for base 3 if (digit > 2) { throw new ArgumentException("Invalid input"); } // multiply the digit by the power of 3 corresponding to its position base10 += digit * (int)Math.Pow(3, base3.Length - i - 1); } // return the base 10 integer return base10 / 3; } public static int EdgePositionIndex((byte, byte)[] edges) { int[] lehmer = new int[6]; lehmer[0] = edges[0].Item1; for (byte i = 1; i < 6; i++) { byte lessThan = 0; for (byte j = 0; j < i; j++) { if (edges[j].Item1 < edges[i].Item1) { lessThan++; } } lehmer[i] = (byte)(edges[i].Item1 - lessThan); } int total = 0; for (int i = 0; i < 6; i++) { total += lehmer[i] * edgePositionPlaceValues[i]; } return total; } public static int EdgeOrientationIndex((byte, byte)[] corners) { byte[] orient = new byte[6]; for (int i = 0; i < 6; i++) { orient[i] = corners[i].Item2; } // Join the array elements into a string string binary = string.Join("", orient); // Convert the string from base 2 to base 10 int num = Convert.ToInt32(binary, 2); // Return the result return num; } } } }