Exemple #1
0
        //Not used
        /// <summary>
        /// Create a weighted pruning table using U- and D-edge order and
        /// equator edge order. Every table entry contains the minimum cost
        /// required to solve the position represented by said coordinates.
        /// </summary>
        /// <param name="weights">The weights to use.</param>
        /// <returns>
        /// A weighted pruning table using U- and D-edge order and equator edge
        /// order.
        /// </returns>
        /// <exception cref="InvalidWeightsException">
        /// Thrown if <paramref name="weights"/> is invalid.
        /// </exception>
        public static float[] CreateWeightedPhase2UdEquatorTable(float[] weights)
        {
            MoveWeightsUtils.ValidateWeights(weights);

            float invalid = float.NaN;

            float[] pruningTable = Enumerable
                                   .Repeat(invalid, PruningTableConstants.UdEquatorPruningTableSizePhase2)
                                   .ToArray();

            TableController.InitializeUdEdgeOrderMoveTable();
            TableController.InitializeEquatorOrderMoveTable();

            pruningTable[0] = 0f;
            int numChanged = -1;

            while (numChanged != 0)
            {
                numChanged = 0;
                for (int udEdgeOrder = 0; udEdgeOrder < NumUdEdgeOrders; udEdgeOrder++)
                {
                    for (int equatorOrder = 0; equatorOrder < NumEquatorOrders; equatorOrder++)
                    {
                        int index = NumEquatorOrders * udEdgeOrder + equatorOrder;
                        if (pruningTable[index] != invalid)
                        {
                            foreach (int move in TwoPhaseConstants.Phase2Moves)
                            {
                                int newUdEdgeOrder  = TableController.UdEdgeOrderMoveTable[udEdgeOrder, MoveTables.Phase1IndexToPhase2Index[move]];
                                int newEquatorOrder = TableController.EquatorOrderMoveTable[equatorOrder, move];
                                int newIndex        = NumEquatorOrders * newUdEdgeOrder + newEquatorOrder;

                                float newPruningValue = pruningTable[index] + weights[move];

                                if (pruningTable[newIndex] == invalid || pruningTable[newIndex] > newPruningValue)
                                {
                                    pruningTable[newIndex] = newPruningValue;
                                    numChanged++;
                                }
                            }
                        }
                    }
                }
            }

            return(pruningTable);
        }
        private void StartSearch()
        {
            #region rotate cube
            CubieCube rotatedCube = CubieCube.CreateSolved();

            for (int i = 0; i < _rotation; i++)
            {
                rotatedCube.Rotate(Rotation.y3);
                rotatedCube.Rotate(Rotation.x3);
            }

            rotatedCube.Multiply(_notRotatedCube);

            for (int i = 0; i < _rotation; i++)
            {
                rotatedCube.Rotate(Rotation.x1);
                rotatedCube.Rotate(Rotation.y1);
            }

            if (_inversed)
            {
                rotatedCube.Inverse();
            }
            #endregion rotate cube

            #region rotate weights
            _rotatedWeights = new float[NumMoves];

            for (int oldIndex = 0; oldIndex < NumMoves; oldIndex++)
            {
                int newIndex = oldIndex;
                for (int i = 0; i < _rotation; i++)
                {
                    newIndex = (int)((Move)newIndex).Rotate(Rotation.x1).Rotate(Rotation.y1);
                }
                _rotatedWeights[newIndex] = _nonRotatedWeights[oldIndex];
            }

            if (_inversed)
            {
                for (int face = 0; face < NumFaces; face++)
                {
                    //face * 3 = 90° cw, face * 3 + 2 = 90° ccw
                    float temp = _rotatedWeights[face * 3];
                    _rotatedWeights[face * 3]     = _rotatedWeights[face * 3 + 2];
                    _rotatedWeights[face * 3 + 2] = temp;
                }
            }
            #endregion rotate weights

            _phase1MoveOrder = MoveWeightsUtils.OrderedMoves((Move[])Enum.GetValues(typeof(Move)), _rotatedWeights);
            _phase2MoveOrder = MoveWeightsUtils.OrderedMoves(TwoPhaseConstants.Phase2Moves, _rotatedWeights);

            //calculate coordinates
            int co      = Coordinates.GetCornerOrientation(rotatedCube);
            int cp      = Coordinates.GetCornerPermutation(rotatedCube);
            int eo      = Coordinates.GetEdgeOrientation(rotatedCube);
            int equator = Coordinates.GetEquatorPermutation(rotatedCube);
            int uEdges  = Coordinates.GetUEdgePermutation(rotatedCube);
            int dEdges  = Coordinates.GetDEdgePermutation(rotatedCube);

            //store coordinates used in phase 2
            _cp     = cp;
            _uEdges = uEdges;
            _dEdges = dEdges;

            int pruningIndex    = PruningTables.GetPhase1PruningIndex(co, eo, equator / Coordinates.NumEquatorOrders);
            int minPhase1Length = TableController.Phase1PruningTable[pruningIndex];

            _currentPhase1Solution = new int[MaxPhase1Length];
            _currentPhase2Solution = new int[MaxPhase2Length];

            for (int phase1Length = minPhase1Length; phase1Length < MaxPhase1Length; phase1Length++)
            {
                SearchPhase1(eo, co, equator, depth: 0, remainingMoves: phase1Length, minPhase1Length);
            }
        }
        /// <summary>
        /// Find a near-optimal solution for a cube in respect to the cost of
        /// the moves. If no matching solution is found, the return value is
        /// null.
        /// </summary>
        /// <param name="cubeToSolve">The cube to solve.</param>
        /// <param name="timeout">
        /// Interrupt the search after that much time has passed.
        /// </param>
        /// <param name="returnCost">
        /// The search stops as soon as a solution of the specified cost is
        /// found. Be careful with setting <paramref name="returnCost"/> to a
        /// value too small, because there might not be a solution cheaper than
        /// or matching that cost. In which case the algorithm will run for a
        /// long time.
        /// </param>
        /// <param name="requiredCost">
        /// Ignore the timeout until a solution of a cost less than or equal to
        /// <paramref name="requiredCost"/> is found. If the value of
        /// <paramref name="requiredCost"/> is negative, this parameter is
        /// ignored. If <paramref name="requiredCost"/> is positive, it must be
        /// larger than or equal to <paramref name="returnCost"/>. Setting the
        /// value to a large value will cause the first solution found after
        /// timeout to be returned.
        /// </param>
        /// <param name="weights">The weights to use.</param>
        /// <param name="weightedCornerEquatorPruningTable">
        /// The weighted corner permutation and equator order pruning table
        /// created by
        /// <see cref="WeightedPruningTables.CreateWeightedPhase2CornerEquatorTable(float[])"/>.
        /// The weights used to create this table must be equatl to
        /// <paramref name="weights"/>.
        /// </param>
        /// <param name="deltaMin">
        /// The minimum difference a newly found solution has to exceed the
        /// cheapest solution found. Only used to experiment. Set to 0 for
        /// optimal solutions.
        /// </param>
        /// <param name="searchDifferentOrientations">
        /// Start three threads each solving a 120° offset of the cube.
        /// </param>
        /// <param name="searchInverse">
        /// For every orientation solved, start another thread that solves its
        /// inverse.
        /// </param>
        /// <returns>
        /// A near-optimal solution for the specified cube in respect to its
        /// cost. Null, if no matching solution is found.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// Thrown if <paramref name="cubeToSolve"/> or
        /// <paramref name="weightedCornerEquatorPruningTable"/> is null.
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Thrown if <paramref name="timeout"/>,
        /// <paramref name="returnCost"/> or <paramref name="deltaMin"/> is
        /// negative. Also thrown if <paramref name="requiredCost"/> is
        /// positive and less than <paramref name="returnCost"/>.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// Thrown if <paramref name="weightedCornerEquatorPruningTable"/>.Length
        /// is not of the expected size.
        /// </exception>
        /// <exception cref="InvalidWeightsException">
        /// Thrown if <paramref name="weights"/> is invalid.
        /// </exception>
        public static Alg FindSolution(CubieCube cubeToSolve, TimeSpan timeout, float returnCost, float requiredCost, float[] weights, float[] weightedCornerEquatorPruningTable, float deltaMin = 0f, bool searchDifferentOrientations = true, bool searchInverse = true)
        {
            #region parameter checks
            if (cubeToSolve is null)
            {
                throw new ArgumentNullException(nameof(cubeToSolve) + " is null.");
            }
            if (timeout < TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException(nameof(timeout) + " cannot be negative: " + timeout);
            }
            if (returnCost < 0d)
            {
                throw new ArgumentOutOfRangeException(nameof(returnCost) + " cannot be negative: " + returnCost);
            }
            if (requiredCost > 0d && requiredCost < returnCost)
            {
                throw new ArgumentOutOfRangeException(nameof(requiredCost) + " must either be negative or >= " + nameof(returnCost) + ": " + requiredCost + " (" + nameof(requiredCost) + ") < " + returnCost + " (" + nameof(returnCost) + ")");
            }
            MoveWeightsUtils.ValidateWeights(weights);
            if (weightedCornerEquatorPruningTable is null)
            {
                throw new ArgumentNullException(nameof(weightedCornerEquatorPruningTable) + " is null.");
            }
            if (weightedCornerEquatorPruningTable.Length != PruningTableConstants.CornerEquatorPruningTableSizePhase2)
            {
                throw new ArgumentException(nameof(weightedCornerEquatorPruningTable) + " must be of size " + PruningTableConstants.CornerEquatorPruningTableSizePhase2);
            }
            if (deltaMin < 0d)
            {
                throw new ArgumentOutOfRangeException(nameof(deltaMin) + " cannot be negative: " + deltaMin);
            }
            #endregion paramter checks

            //initialize move tables
            TableController.InitializeCornerOrientationMoveTable();
            TableController.InitializeCornerPermutationMoveTable();
            TableController.InitializeDEdgePermutationMoveTable();
            TableController.InitializeEdgeOrientationMoveTable();
            TableController.InitializeEquatorPermutationMoveTable();
            TableController.InitializeUdEdgeOrderMoveTable();
            TableController.InitializeUEdgePermutationMoveTable();

            //initialize pruning tables
            TableController.InitializePhase1PruningTable();
            TableController.InitializePhase2CornerEquatorPruningTable();
            TableController.InitializePhase2CornerUdPruningTable();

            Stopwatch timePassed = new Stopwatch();
            timePassed.Start();

            SyncedBoolWrapper isTerminated = new SyncedBoolWrapper()
            {
                Value = false
            };
            SyncedFloatWrapper bestSolutionCost = new SyncedFloatWrapper()
            {
                Value = float.MaxValue
            };
            SyncedIntWrapper bestSolutionIndex = new SyncedIntWrapper()
            {
                Value = -1
            };
            List <Alg> solutions = new List <Alg>();

            int numThreads = (searchDifferentOrientations ? 3 : 1) * (searchInverse ? 2 : 1);
            int increment  = searchDifferentOrientations ? 1 : 2;

            Thread[] solvers = new Thread[numThreads];

            for (int orientation = 0; orientation < numThreads; orientation += increment)
            {
                WeightedTwoPhaseSolver solver = new WeightedTwoPhaseSolver()
                {
                    _notRotatedCube    = cubeToSolve.Clone(), //create a copy to avoid issues caused by multithreading
                    _timePassed        = timePassed,
                    _timeout           = timeout,
                    _returnCost        = returnCost,
                    _requiredCost      = requiredCost,
                    IsTerminated       = isTerminated,
                    _solutions         = solutions,
                    _bestSolutionCost  = bestSolutionCost,
                    _bestSolutionIndex = bestSolutionIndex,
                    _rotation          = orientation % 3,
                    _inversed          = orientation / 3 == 1,
                    _nonRotatedWeights = (float[])weights.Clone(), //create a copy to avoid issues caused by multithreading
                    _weightedCornerEquatorPruningTable = weightedCornerEquatorPruningTable,
                    _deltaMin = deltaMin
                };

                Thread solverThread = new Thread(new ThreadStart(solver.StartSearch));
                solvers[orientation] = solverThread;
                solverThread.Start();
            }

            foreach (Thread solverThread in solvers)
            {
                solverThread.Join();
            }

            timePassed.Stop(); //not required, only for the sake of completeness

            if (solutions.Count > 0)
            {
                return(solutions[bestSolutionIndex.Value]);
            }
            else
            {
                return(null);
            }
        }