/// <summary> /// Marks the square in the matrix to indicate the direction we're moving through the /// square. This allows us to render the path simply using ASCII art. /// </summary> /// <param name="tracker">The current square in the maze that we're looking at</param> /// <param name="outDirection">The direction the path takes when leaving the current /// tracker square (up, right, down or left).</param> private static void MarkTravelDirection(Square tracker, Direction outDirection) { SquareState currentSquareTravel = SquareState.OnThePath; if (outDirection == Direction.UP) { if (tracker.CurrentSquare == startingSquare.CurrentSquare) { return; } else if (tracker.PreviousSquare.X == tracker.CurrentSquare.X) { currentSquareTravel = SquareState.TravelNS; } else if (tracker.PreviousSquare.X - 1 == tracker.CurrentSquare.X) { currentSquareTravel = SquareState.TravelNE; } else if (tracker.PreviousSquare.X + 1 == tracker.CurrentSquare.X) { currentSquareTravel = SquareState.TravelNW; } } else if (outDirection == Direction.RIGHT) { if (tracker.CurrentSquare == startingSquare.CurrentSquare) { return; } else if (tracker.PreviousSquare.Y == tracker.CurrentSquare.Y) { currentSquareTravel = SquareState.TravelWE; } else if (tracker.PreviousSquare.Y - 1 == tracker.CurrentSquare.Y) { currentSquareTravel = SquareState.TravelNE; } else if (tracker.PreviousSquare.Y + 1 == tracker.CurrentSquare.Y) { currentSquareTravel = SquareState.TravelSE; } } else if (outDirection == Direction.DOWN) { if (tracker.CurrentSquare == startingSquare.CurrentSquare) { return; } else if (tracker.PreviousSquare.X == tracker.CurrentSquare.X) { currentSquareTravel = SquareState.TravelNS; } else if (tracker.PreviousSquare.X - 1 == tracker.CurrentSquare.X) { currentSquareTravel = SquareState.TravelSE; } else if (tracker.PreviousSquare.X + 1 == tracker.CurrentSquare.X) { currentSquareTravel = SquareState.TravelSW; } } else if (outDirection == Direction.LEFT) { if (tracker.CurrentSquare == startingSquare.CurrentSquare) { return; } else if (tracker.PreviousSquare.Y == tracker.CurrentSquare.Y) { currentSquareTravel = SquareState.TravelWE; } else if (tracker.PreviousSquare.Y - 1 == tracker.CurrentSquare.Y) { currentSquareTravel = SquareState.TravelNW; } else if (tracker.PreviousSquare.Y + 1 == tracker.CurrentSquare.Y) { currentSquareTravel = SquareState.TravelSW; } } matrix[tracker.CurrentSquare.X, tracker.CurrentSquare.Y] = currentSquareTravel; }
/// <summary> /// Gets the state of a square adjacent to the specified one. /// </summary> /// <param name="direction">The direction to look in (left, right, up or down)</param> /// <param name="square">The reference square.</param> /// <returns>Whether the square is empty, the start or finish point, an obstacle or /// one that's on the current path</returns> private static SquareState GetAdjoiningSquareState(Direction direction, Square square) { int xOffset = 0; int yOffset = 0; if (direction == Direction.UP && square.CurrentSquare.Y + 1 < SquareSize) yOffset = 1; else if (direction == Direction.DOWN && square.CurrentSquare.Y - 1 >= 0) yOffset = -1; else if (direction == Direction.RIGHT && square.CurrentSquare.X + 1 < SquareSize) xOffset = 1; else if (direction == Direction.LEFT && square.CurrentSquare.X - 1 >= 0) xOffset = -1; else return SquareState.InvalidSquare; return matrix[square.CurrentSquare.X + xOffset, square.CurrentSquare.Y + yOffset]; }
/// <summary> /// All good things have to start somewhere. /// </summary> /// <param name="args">None, ignored.</param> static void Main(string[] args) { // Setup a matrix to test with. matrix = SetupMatrixTest(grid1); // Open a log file so we can write out the paths we find. string logFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "WPC35-solutions.txt"); sw = new StreamWriter(logFile); sw.WriteLine("Olimex Weekend Programming Challenge 35 - 30 Nov 2013"); sw.WriteLine("-----------------------------------------------------"); sw.WriteLine(); // Starting square startingSquare = new Square(0, 0); startingSquare.PreviousSquare = new Point(-1, -1); startingSquare.SquareCounter = 1; // Start recursing... CheckSquare(startingSquare); // Finished. Close the log file. string message = string.Format("Finished!! Number of paths found = {0}", solutionsFound); Console.WriteLine(); Console.WriteLine(message); sw.WriteLine(); sw.WriteLine(message); sw.Close(); // Wait for user to close the console. Console.ReadLine(); }
/// <summary> /// The main recursion routine that checks whether a square is valid for the current path and /// moves the path along to that square. Then we call ourself again to recurse further along /// the path. /// </summary> /// <param name="tracker">The current square we're looking at in the maze.</param> /// <returns>true when a valid solutin is found (all squares occupied); false otherwise</returns> private static bool CheckSquare(Square tracker) { Square clone; // Check if we've reached the start/finish point if (tracker.CurrentSquare == startingSquare.CurrentSquare) { // Check whether all the squares in the maze are occupied. If they are, we have // a valid solution. if (AllSquaresFilled()) { // It's a valid path! solutionsFound++; DrawSolutionPath(); return true; } } // We could have occupied all squares in the maze but hit a dead-end. This detects that // and quits the recursion to back-track to the previous square. if (tracker.SquareCounter >= targetLength) { // Reset the current square to an empty one matrix[tracker.CurrentSquare.X, tracker.CurrentSquare.Y] = SquareState.Empty; return false; } // Get the state of the squares adjoining the current one SquareState squareAboveState = GetAdjoiningSquareState(Direction.UP, tracker); SquareState squareRightState = GetAdjoiningSquareState(Direction.RIGHT, tracker); SquareState squareBelowState = GetAdjoiningSquareState(Direction.DOWN, tracker); SquareState squareLeftState = GetAdjoiningSquareState(Direction.LEFT, tracker); // If it's OK to move up, do so if (squareAboveState == SquareState.Empty || squareAboveState == SquareState.StartFinish) { // Mark the square as being on the path. if (squareAboveState == SquareState.Empty || squareLeftState == SquareState.StartFinish) MarkTravelDirection(tracker, Direction.UP); // Create a clone of the current square to pass into the next recursion clone = new Square(tracker); // Bump the counter that keeps track of the number of squares are along the path clone.SquareCounter++; // Move to the square above clone.PreviousSquare.X = clone.CurrentSquare.X; clone.PreviousSquare.Y = clone.CurrentSquare.Y; clone.CurrentSquare.Y++; // And check that. if (CheckSquare(clone)) { // Reset the current square matrix[tracker.CurrentSquare.X, tracker.CurrentSquare.Y] = SquareState.Empty; return false; } } // If it's OK to move right, do so if (squareRightState == SquareState.Empty || squareRightState == SquareState.StartFinish) { // Mark the square as being on the path. if (squareRightState == SquareState.Empty || squareLeftState == SquareState.StartFinish) MarkTravelDirection(tracker, Direction.RIGHT); // Create a clone of the current square to pass into the next recursion clone = new Square(tracker); // Bump the counter that keeps track of the number of squares are along the path clone.SquareCounter++; // Move to the square to the right clone.PreviousSquare.X = clone.CurrentSquare.X; clone.PreviousSquare.Y = clone.CurrentSquare.Y; clone.CurrentSquare.X++; // And check that. if (CheckSquare(clone)) { // Reset the current square matrix[tracker.CurrentSquare.X, tracker.CurrentSquare.Y] = SquareState.Empty; return false; } } // If it's OK to move down, do so if (squareBelowState == SquareState.Empty || squareBelowState == SquareState.StartFinish) { // Mark the square as being on the path. if (squareBelowState == SquareState.Empty || squareLeftState == SquareState.StartFinish) MarkTravelDirection(tracker, Direction.DOWN); // Create a clone of the current square to pass into the next recursion clone = new Square(tracker); // Bump the counter that keeps track of the number of squares are along the path clone.SquareCounter++; // Move to the square below clone.PreviousSquare.X = clone.CurrentSquare.X; clone.PreviousSquare.Y = clone.CurrentSquare.Y; clone.CurrentSquare.Y--; // And check that. if (CheckSquare(clone)) { // Reset the current square matrix[tracker.CurrentSquare.X, tracker.CurrentSquare.Y] = SquareState.Empty; return false; } } // If it's OK to move left, do so if (squareLeftState == SquareState.Empty || squareLeftState == SquareState.StartFinish) { // Mark the square as being on the path. if (squareLeftState == SquareState.Empty || squareLeftState == SquareState.StartFinish) MarkTravelDirection(tracker, Direction.LEFT); // Create a clone of the current square to pass into the next recursion clone = new Square(tracker); // Bump the counter that keeps track of the number of squares are along the path clone.SquareCounter++; // Move to the square to the left clone.PreviousSquare.X = clone.CurrentSquare.X; clone.PreviousSquare.Y = clone.CurrentSquare.Y; clone.CurrentSquare.X--; // And check that. if (CheckSquare(clone)) { // Reset the current square matrix[tracker.CurrentSquare.X, tracker.CurrentSquare.Y] = SquareState.Empty; return false; } } // No more routes available from the current square so it's not viable. // As long as the current square is not the start/finish, clear it and go back to // the caller to try another route from the previous square along the path. if (matrix[tracker.CurrentSquare.X, tracker.CurrentSquare.Y] != SquareState.StartFinish) { matrix[tracker.CurrentSquare.X, tracker.CurrentSquare.Y] = SquareState.Empty; } return false; }