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); } } }