private static void ReverseSolveDirection( GameBoard gameBoard, IGameBlockDestination gameBlock, IGameBlockDestination destinationBlock, MovementDirection direction, int maxCountAtPlayer, int maxNumberOfSolutions, int variability, ref int stepsBack, BlockMovement nextMove, Dictionary <string, GameBoard> solutionGameBoards) { // Test the board to see if a reverse solve is even possible. If it's not, return early so it doesn't // waste time trying to solve something that can't be solved. if (CanTestReverseSolve && !gameBoard.TestCanReverseSolve()) { return; } // So it doesn't always choose the max List <int> counts = Enumerable.Range(1, maxCountAtPlayer).ToList(); for (int i = 0; i < counts.Count; i++) { int next = RandomGenerator.Next(0, counts.Count - 1); int index = counts[next]; counts.Remove(index); counts.Add(index); } for (int i = 0; i < counts.Count && solutionGameBoards.Count < maxNumberOfSolutions && !RequestAbort; i++) { BlockMovement temp; if (gameBlock.ApplyReverseMove(destinationBlock, direction, counts[i], out temp, null, null, new Queue <IGameBlock>())) { bool allMovesUsed = true; int originalAvailableMoves = temp.SourceBlock.AvailableMoves; List <List <IGameBlockDestination> > removedCombinations; if (nextMove != null) { temp.NextMove = nextMove; nextMove.PreviousMove = temp; } // Got more moves that we wanted, so take out a few randomly int max = counts[i]; if (temp.SourceBlock.PreferredMaxMoves != 0 && temp.SourceBlock.AvailableMoves > temp.SourceBlock.PreferredMaxMoves) { max = Math.Min(max, temp.SourceBlock.PreferredMaxMoves); } // More moves than can be used if (temp.SourceBlock.AvailableMoves > max) { int numberOfMovedNeedRemoving = temp.SourceBlock.AvailableMoves - max; int numberAvailableForRemoving = temp.IntermediateBlocks.Count(ib => ib.CanBeRemovedIntermediate()); // Not possible because it needs to remove more items than it could if (numberAvailableForRemoving < numberOfMovedNeedRemoving) { BlockMovement move = new BlockMovement(temp.SourceBlock, temp.Direction); temp.SourceBlock.ApplyMove(null, temp.Direction, move); continue; } allMovesUsed = false; List <IGameBlockDestination> removeableIntermediates = temp.IntermediateBlocks.Where(ib => ib.CanBeRemovedIntermediate()).ToList(); removedCombinations = new List <List <IGameBlockDestination> >(); IEnumerable <IEnumerable <IGameBlockDestination> > combos = removeableIntermediates.Combinations(numberOfMovedNeedRemoving).ToList(); int count = 0; foreach (IEnumerable <IGameBlockDestination> gameBlockDestinations in combos) { removedCombinations.Add(gameBlockDestinations.ToList()); if (MaxCombinations > 0 && ++count >= MaxCombinations) { break; } } } else { removedCombinations = new List <List <IGameBlockDestination> > { new List <IGameBlockDestination>() }; } List <IGameBlockDestination> originalIntermediateBlocks = new List <IGameBlockDestination>(); foreach (IGameBlockDestination gameBlockDestination in temp.IntermediateBlocks) { originalIntermediateBlocks.Add(gameBlockDestination.Clone() as IGameBlockDestination); } foreach (List <IGameBlockDestination> removedCombination in removedCombinations) { List <IGameBlockDestination> unusedBlocks = new List <IGameBlockDestination>(); List <List <IGameBlockDestination> > intermediateBlockSets = new List <List <IGameBlockDestination> >(); foreach (IGameBlockDestination gameBlockDestination in removedCombination) { temp.IntermediateBlocks.Remove(gameBlockDestination); temp.SourceBlock.AvailableMoves--; gameBlockDestination.SetAvailability(false); unusedBlocks.Add(gameBlockDestination); } foreach (IGameBlockDestination unusedBlock in unusedBlocks) { for (int j = 0; j < temp.IntermediateBlocks.Count; j++) { List <IGameBlockDestination> sets = new List <IGameBlockDestination>(temp.IntermediateBlocks); sets.RemoveAt(j); sets.Add(unusedBlock); intermediateBlockSets.Add(sets); } } do { if (gameBoard.IsUntouchedPuzzle()) { lock (solutionGameBoards) { // HACK: Test the solution in case it comes up with an invalid solution GameBoard testSolution = (GameBoard)gameBoard.Clone(); testSolution.SolutionStartMove = temp.CloneFromGameBoard(testSolution, null); testSolution.NullZeroPlayers(); string key = testSolution.ToString(); if (!solutionGameBoards.ContainsKey(key)) { if (testSolution.PlaySolution()) { GameBoard solution = (GameBoard)gameBoard.Clone(); solution.SolutionStartMove = temp.CloneFromGameBoard(solution, null); solution.NullZeroPlayers(); solutionGameBoards.Add(key, solution); NotifySolutionFoundEvent(); } } } } else if (!gameBoard.HasOrphan()) { List <IGameBlockDestination> destinations = new List <IGameBlockDestination>(); foreach (IGameBlock nextBlock in gameBoard.GameBlocks) { IGameBlockDestination nextDestination = nextBlock as IGameBlockDestination; if (nextDestination != null && !nextDestination.IsFullyAvailable) { for (int j = 0; j < nextDestination.NumberUsed; j++) { destinations.Add(nextDestination); } } } Shuffle(destinations, destinations.Count); foreach (IGameBlockDestination nextBlock in destinations) { ReverseSolveMovements( gameBoard, nextBlock, null, maxCountAtPlayer, maxNumberOfSolutions, variability, ref stepsBack, temp, solutionGameBoards); if (solutionGameBoards.Count >= maxNumberOfSolutions) { allMovesUsed = true; break; } if (RequestAbort) { break; } } } // Not all the moves were used, but no solution was found, so let's use one of the ones we took out if (!allMovesUsed) { if (intermediateBlockSets.Count == 0) { allMovesUsed = true; } else { // Replace the block with one of the unused ones temp.IntermediateBlocks.ForEach(g => g.SetAvailability(true)); List <IGameBlockDestination> intermediateBlockSet = intermediateBlockSets[0]; intermediateBlockSets.RemoveAt(0); intermediateBlockSet.ForEach(g => g.SetAvailability(false)); temp.IntermediateBlocks = intermediateBlockSet; } } else { allMovesUsed = true; NotifySolutionFailedEvent(); } }while (!allMovesUsed && !RequestAbort); if (removedCombination.Count > 0) { allMovesUsed = false; temp.IntermediateBlocks.Clear(); foreach (IGameBlockDestination gameBlockDestination in originalIntermediateBlocks) { IGameBlockDestination blockDestination = gameBoard.GameBlocks[gameBlockDestination.IndexRow, gameBlockDestination.IndexColumn] as IGameBlockDestination; blockDestination.Copy(gameBlockDestination); temp.IntermediateBlocks.Add(blockDestination); } temp.SourceBlock.AvailableMoves = originalAvailableMoves; } } BlockMovement blockMove = new BlockMovement(temp.SourceBlock, temp.Direction); temp.SourceBlock.ApplyMove(null, temp.Direction, blockMove); } } }
/// <summary> /// Reverses the solve. /// </summary> /// <param name="gameBoard">The game board.</param> /// <param name="maxCountAtPlayer">The max count at player.</param> /// <param name="maxNumberOfSolutions">The max number of solutions.</param> /// <param name="variability">The variability.</param> /// <param name="milliSecondsTimeout">The milli seconds timeout.</param> /// <param name="aborted">if set to <c>true</c> [aborted].</param> /// <returns></returns> public static List <GameBoard> ReverseSolve( GameBoard gameBoard, int maxCountAtPlayer, int maxNumberOfSolutions, int variability, int milliSecondsTimeout, out bool aborted) { RequestAbort = false; AlreadyTestedBoards = new HashSet <string>(); Dictionary <string, GameBoard> solutionGameBoards = new Dictionary <string, GameBoard>(); List <Thread> threads = new List <Thread>(); List <IGameBlockDestination> destinations = new List <IGameBlockDestination>(); foreach (IGameBlock gameBlock in gameBoard.GameBlocks) { IGameBlockDestination nextDestination = gameBlock as IGameBlockDestination; if (nextDestination != null) { destinations.Add(gameBlock as IGameBlockDestination); } } Shuffle(destinations, destinations.Count); foreach (IGameBlockDestination gameBlock in destinations) { GameBoard gameBoardClone = (GameBoard)gameBoard.Clone(); IGameBlockDestination gameBlockClone = gameBoardClone.GameBlocks[gameBlock.IndexRow, gameBlock.IndexColumn] as IGameBlockDestination; // Parallel process the solutions ThreadStart threadStart = () => { int stepsBack = 0; ReverseSolveMovements( gameBoardClone, gameBlockClone, null, maxCountAtPlayer, maxNumberOfSolutions, variability, ref stepsBack, null, solutionGameBoards); }; Thread threadFunction = new Thread(threadStart); threads.Add(threadFunction); threadFunction.Start(); } // Wait for the threads to finish foreach (Thread thread in threads) { thread.Join(milliSecondsTimeout); // Took too long so abort it if (thread.IsAlive) { thread.Abort(); } } NotifySolveSolutionStartedEvent(); // Clear it to take back memory AlreadyTestedBoards = new HashSet <string>(); GC.Collect(); if (AnalyzeImmediately) { foreach (KeyValuePair <string, GameBoard> solutionGameBoard in solutionGameBoards) { int fails; int successes; GameBoardSolver.Solve(solutionGameBoard.Value, milliSecondsTimeout * 4, out fails, out successes); solutionGameBoard.Value.Failures = fails; solutionGameBoard.Value.Successes = successes; NotifyAnalyzedSolutionEvent(); } } NotifySolveSolutionEndedEvent(); aborted = RequestAbort; RequestAbort = false; return(solutionGameBoards.Values.ToList()); }
public static void Solve(GameBoard gameBoard, int milliSecondsTimeout, out int failures, out int successes) { Failures = 0; Successes = 0; AlreadyTestedBoards = new HashSet <string>(); List <Thread> threads = new List <Thread>(); foreach (IGameBlockParent gameBlock in gameBoard.GameBlocks.OfType <IGameBlockParent>()) { GameBoard gameBoardClone = (GameBoard)gameBoard.Clone(); IGameBlockParent gameBlockClone = gameBoardClone.GameBlocks[gameBlock.IndexRow, gameBlock.IndexColumn] as IGameBlockParent; // Parallel process the solutions ThreadStart threadStart = () => SolveMovements(gameBoardClone, gameBlockClone, null); Thread threadFunction = new Thread(threadStart); threads.Add(threadFunction); } List <Thread> runningThreads = new List <Thread>(); while (threads.Count > 0) { // Limit running threads to 8 for (int i = 0; i < 8 - runningThreads.Count && i < threads.Count; i++) { threads[i].Start(); runningThreads.Add(threads[i]); threads.Remove(threads[i]); } List <Thread> finishedThreads = new List <Thread>(); // Remove any threads that finished foreach (Thread runningThread in runningThreads) { bool joined = runningThread.Join(1000); if (joined) { finishedThreads.Add(runningThread); } } foreach (Thread finishedThread in finishedThreads) { runningThreads.Remove(finishedThread); } } // Wait for the threads to finish foreach (Thread thread in runningThreads) { thread.Join(milliSecondsTimeout); // Took too long so abort it if (thread.IsAlive) { thread.Abort(); } } failures = Failures; successes = Successes; AlreadyTestedBoards = new HashSet <string>(); GC.Collect(); }