/// <summary> /// This will solve the puzzle by using a recursive Backtracking algorthim /// </summary> /// <remarks> /// This is a brute force routine /// </remarks> /// <param name="puzzle"></param> /// <param name="cells"></param> /// <param name="index"></param> private static void SolveRecursive(Puzzle puzzle, Puzzle.Cell[] cells, int index) { int minIndex = cells.GetLowerBound(0); int maxIndex = cells.GetUpperBound(0); // Check the index, and exit if the index is outside the boundries if (index < minIndex || index > maxIndex) { return; } // Much easier to reference a single cell in the beginning than array[index] the whole time Puzzle.Cell cell = cells[index]; // Need to bypass if this cell is Locked. if (cell.IsLocked) { // Call recursively sending the next index to process SolveRecursive(puzzle, cells, index + 1); } else { // Re-Initialize the cell's value cell.Value = null; // Check to see if the puzzle is solved, if so, break out of the routine all together // Iterate through all the possible values for this cell while (!Validator.IsSolved(puzzle)) { if (cell.Value.GetValueOrDefault() < Puzzle.MAX_VALUE) { // Increment current cell // Note: The Increment method has logic to determine if the cell is Locked already cell.Increment(); // Determine if we stay on the current cell (to obtain a valid value) or move to the next cell // Call recursively sending the next index to process if (!Validator.IsExistValue(puzzle, cell)) { SolveRecursive(puzzle, cells, index + 1); } } else { // Re-Initialize the cell's value if the current value is at the max value cell.Value = null; // By breaking out of the loop, we are forcing the method to return to the previous call break; } } } }
/// <summary> /// Checks to see if cell's value already exists in the cell's current row, column and quadrient /// </summary> /// <param name="cell"></param> /// <returns></returns> internal static bool IsExistValue(Puzzle puzzle, Puzzle.Cell cell) { bool ret = false; Puzzle.Cell[] cells = null; Puzzle.Cell[] filteredCells = null; // Check current row to determine if the cell's value already exists by iterating through all the columns in that row cells = Utils.Transpose <Puzzle.Cell>(puzzle.ToArray(), cell.Row, 1, 0, Puzzle.PUZZLE_GRID_SIZE); // Need to remove the cell that we are checking against filteredCells = cells.Where(x => x.Column != cell.Column) .ToArray(); ret = ret || IsExistValue(cell.Value.GetValueOrDefault(), filteredCells); // Check current column to determine if the cell's value already exists by iterating through all the rows in that column cells = Utils.Transpose <Puzzle.Cell>(puzzle.ToArray(), 0, Puzzle.PUZZLE_GRID_SIZE, cell.Column, 1); // Need to remove the cell that we are checking against filteredCells = cells.Where(x => x.Row != cell.Row) .ToArray(); ret = ret || IsExistValue(cell.Value.GetValueOrDefault(), filteredCells); // Check current quadrient to determine if the cell's value already exists by iterating through all the rows and columns in that quadrient // Determine quadrient int quadRow = (cell.Row / Puzzle.QUADRIENT_GRID_SIZE); int quadCol = (cell.Column / Puzzle.QUADRIENT_GRID_SIZE); cells = Utils.Transpose <Puzzle.Cell>(puzzle.ToArray(), (quadRow * Puzzle.QUADRIENT_GRID_SIZE), Puzzle.QUADRIENT_GRID_SIZE, (quadCol * Puzzle.QUADRIENT_GRID_SIZE), Puzzle.QUADRIENT_GRID_SIZE); // Need to remove the cell that we are checking against filteredCells = cells.Where(x => !((x.Row == cell.Row) && (x.Column == cell.Column))) .ToArray(); ret = ret || IsExistValue(cell.Value.GetValueOrDefault(), filteredCells); return(ret); }
/// <summary> /// This will randomly pick a cell from the given puzzle /// </summary> /// <remarks> /// If the picked cell has a value or is locked, then a new cell will be randomly chosen /// </remarks> /// <param name="puzzle"></param> /// <returns></returns> private Puzzle.Cell GetRandomCell(Puzzle puzzle) { int row = 0; int col = 0; Puzzle.Cell cell = null; Random random = new Random(); do { row = random.Next(0, Puzzle.PUZZLE_GRID_SIZE); col = random.Next(0, Puzzle.PUZZLE_GRID_SIZE); cell = puzzle.GetCell(row, col); } while (cell.IsLocked || cell.Value.HasValue); return(cell); }
/// <summary> /// Populate puzzle with random values /// </summary> /// <remarks> /// This will perform some validations to make sure the random value can be placed within the specified cell /// </remarks> /// <param name="puzzle"></param> private void Populate(Puzzle puzzle) { int value = 0; Random random = new Random(); // Loop for the number of random number values to generate for (int i = 0; i < NumberOfRandomValues; i++) { Puzzle.Cell cell = GetRandomCell(puzzle); // Loop until we have a valid random number in a random cell do { cell.Value = null; // Clear out previous value cell = GetRandomCell(puzzle); value = random.Next(Puzzle.MIN_VALUE, Puzzle.MAX_VALUE + 1); cell.Value = Convert.ToByte(value); } while (Validator.IsExistValue(puzzle, cell)); // Lock cell so that value cannot be changed cell.IsLocked = true; } }
/// <summary> /// This will solve the puzzle by using a non-recursive Backtracking algorthim /// </summary> /// <remarks> /// This is a brute force routine /// </remarks> /// <param name="puzzle"></param> /// <param name="cells"></param> private static void SolveNonRecursive(Puzzle puzzle) { // Much easier to work with a single dimensional array, so lets transpose it Puzzle.Cell[] cells = Utils.Transpose <Puzzle.Cell>(puzzle.ToArray(), 0, Puzzle.PUZZLE_GRID_SIZE, 0, Puzzle.PUZZLE_GRID_SIZE); int minIndex = cells.GetLowerBound(0); int maxIndex = cells.GetUpperBound(0); int index = minIndex; int directionFactor = 0; // 1 to move forward, -1 to move backward // Loop until we have either solved or exhusted our routine while (!Validator.IsSolved(puzzle) && index >= minIndex) { // Check to see if we have gone beyond the boundries, change the index factor if (index > maxIndex) { index = maxIndex; directionFactor = -1; } // Much easier to reference a single cell in the beginning than array[index] the whole time Puzzle.Cell cell = cells[index]; // Need to bypass if this cell is IsLocked. (if cell is locked keep the directionFactor the same) if (!cell.IsLocked) { // Iterate through all the possible values for this cell if (cell.Value.GetValueOrDefault() < Puzzle.MAX_VALUE) { // Increment current cell // Note: The Increment method has logic to determine if the cell is Locked already cell.Increment(); // Determine if we stay on the current cell (to obtain a valid value) or move to the next cell if (Validator.IsExistValue(puzzle, cell)) { directionFactor = 0; // Stay on current cell } else { directionFactor = 1; // Move to next cell } } else { // Re-Initialize the cell's value if the current value is at the max value cell.Value = null; // Set the direction factor to the previous cell directionFactor = -1; } } else { // The index will not change if the first cell is locked and the directionFactor is zero if (directionFactor == 0) { directionFactor = 1; } } // Change the index by the direction factor index += directionFactor; } }