public static IEnumerable<Coordinate2D[]> GetAlternatingAxisSets(Coordinate2D[] set, int size) { // Initialize the state used to enumerate the snake set. AlternatingAxisState state = new AlternatingAxisState(); state.Set = set; state.SetMap = GetSetMap(set); state.N = set.Length; state.K = size; state.Coordinates = new Coordinate2D[size]; // Enumerate all the coordinate sets that consist of sequences // of alternating vertical and horizonal turns. foreach (Coordinate2D coord in state.Set) { state.Coordinates[0] = coord; state.Index = 1; // Enumerate sets that start with a vertical turn. foreach (Coordinate2D[] coords in GetAlternatingAxisSets(state, Axis.Vertical)) { yield return coords; } // Enumerate sets that start with a horizontal turn. foreach (Coordinate2D[] coords in GetAlternatingAxisSets(state, Axis.Horizontal)) { yield return coords; } } }
private static bool IsDuplicateAlternatingAxisCoordinate(AlternatingAxisState state) { // Check whether the last coordinate is // a duplicate of one of the others. // It takes at least four coordinates // before we can have a loop. Coordinate2D[] coords = state.Coordinates; Coordinate2D coord = coords[state.Index - 1]; int limit = state.Index - 4; for (int i = 0; i < limit; i++) { if (coords[i] == coord) { return true; } } return false; }
private static bool IsDuplicateAlternatingAxisSet(AlternatingAxisState state) { Coordinate2D[] coords = state.Coordinates; Coordinate2D firstCoord = coords[0]; Coordinate2D lastCoord = coords[state.K - 1]; // If the first coordinate is after the last, then it's a duplicate. if (firstCoord > lastCoord) { return true; } // If the first coordinate is adjacent to the last and // the axis between them is perpendicular to the first // and last turns, then the set is a loop. // If the set is a loop, and the first coordinate is not // lexicographically the lowest or the loop direction // is not to the right, then it's a duplicate. if (Coordinate2D.GetOrthogonalDistance(firstCoord, lastCoord) == 1) { Direction loopDirection = GetDirectionBetween(lastCoord, firstCoord); Direction firstDirection = GetDirectionBetween(firstCoord, coords[1]); Direction lastDirection = GetDirectionBetween(coords[state.K - 2], lastCoord); if (Direction.ArePerpendicular(loopDirection, firstDirection) && Direction.ArePerpendicular(loopDirection, lastDirection)) { // Keep the loop set that starts with the most upper-left coordinate and // advance clockwise. if (!IsFirstCoordinateLeast(coords) || firstDirection != Direction.Right) { return true; } } } return false; }
private static IEnumerable<Coordinate2D[]> GetAlternatingAxisSets(AlternatingAxisState state, Axis axis) { // Determine the next two coordinates on either side // of the specified axis. Coordinate2D coord = state.Coordinates[state.Index - 1]; Coordinate2D coord1; Coordinate2D coord2; Axis nextAxis; if (axis == Axis.Vertical) { // Check the coordinates above and below this one. coord1 = coord + Direction.Up; coord2 = coord + Direction.Down; nextAxis = Axis.Horizontal; } else { // Check the coordinates the the left and right of this one. coord1 = coord + Direction.Left; coord2 = coord + Direction.Right; nextAxis = Axis.Vertical; } // Pursue both choices. ++state.Index; for (int i = 0; i < 2; i++) { // Select the appropriate coordinate for the first or second pass. Coordinate2D newCoord = i == 0 ? coord1 : coord2; // Check whether this coordinate is in the set. if (state.SetMap[newCoord]) { // Store the new coordinate. state.Coordinates[state.Index - 1] = newCoord; // Ensure we haven't created a loop. if (IsDuplicateAlternatingAxisCoordinate(state)) { continue; } // Check whether we have a complete set. if (state.Index == state.K) { // Account for sets that can be // nagivated in a different order. if (!IsDuplicateAlternatingAxisSet(state)) { yield return state.Coordinates; } } else { // Otherwise recurse on the opposite axis. foreach (Coordinate2D[] coords in GetAlternatingAxisSets(state, nextAxis)) { yield return coords; } } } } --state.Index; }