// Unused solution: yes, this is a SECOND failed solution. Left here as a testament // to... something. The actual solution uses CalculateRange from point.go package day10 import ( "sort" ) // GroupPoints creates groups of points, then sorts them and returns the sorted list // A 'group' is a series of points that are adjacent to each other. // If this function finds any non-adjacent points, it returns nil. func GroupPoints(points []*Point) [][]*Point { // this seems a bit weird, but it's mapping points that just contain an x,y value to // the actual point objects with velocity information. pointMap := make(map[int]map[int]*Point) for _, p := range points { if pointMap[p.X] == nil { pointMap[p.X] = make(map[int]*Point) } pointMap[p.X][p.Y] = p } // If any point is not adjacent to at least one other, we are not done moving. for _, p := range points { if !adjacencyInMap(pointMap, p) { return nil } } // now, for each point, check for presence in existing groups, // then check the map for adjacencies // This could be more efficient, but the readability of the range // is desirable. groups := [][]*Point{} for _, p := range points { if checkGroups(groups, p) { continue } // Find and place all adjacent points. group := buildGroup(pointMap, []*Point{}, p) groups = append(groups, group) } // sort the groups sort.Slice(groups[:], func(i, j int) bool { xMin1, _, _, _ := findBounds(groups[i]) xMin2, _, _, _ := findBounds(groups[j]) return xMin1 < xMin2 }) return groups } // returns true if point is already a member of one of groups func checkGroups(groups [][]*Point, point *Point) bool { for _, group := range groups { if checkGroup(group, point) { return true } } return false } func checkGroup(group []*Point, point *Point) bool { for _, member := range group { if point == member { return true } } return false } func arePointsAdjacent(p1, p2 *Point) bool { return (p1.X == p2.X-1 || p1.X == p2.X+1 || p1.X == p2.X) && (p1.Y == p2.Y-1 || p1.Y == p2.Y+1 || p1.Y == p2.Y) } func adjacencyInMap(pMap map[int]map[int]*Point, p *Point) bool { return (pMap[p.X-1] != nil && pMap[p.X-1][p.Y] != nil) || (pMap[p.X+1] != nil && pMap[p.X+1][p.Y] != nil) || pMap[p.X][p.Y-1] != nil || pMap[p.X][p.Y+1] != nil } // recursively walk all adjacent points, adding them to the group. func buildGroup(pMap map[int]map[int]*Point, group []*Point, p *Point) []*Point { // this both prevents duplicates and prevents infinite recursion if checkGroup(group, p) { return group } group = append(group, p) // look in each of 8 directions, if a point is found, recurse if pMap[p.X-1] != nil { if pMap[p.X-1][p.Y] != nil { group = buildGroup(pMap, group, pMap[p.X-1][p.Y]) } if pMap[p.X-1][p.Y-1] != nil { group = buildGroup(pMap, group, pMap[p.X-1][p.Y-1]) } if pMap[p.X-1][p.Y+1] != nil { group = buildGroup(pMap, group, pMap[p.X-1][p.Y+1]) } } if pMap[p.X+1] != nil { if pMap[p.X+1][p.Y] != nil { group = buildGroup(pMap, group, pMap[p.X+1][p.Y]) } if pMap[p.X+1][p.Y-1] != nil { group = buildGroup(pMap, group, pMap[p.X+1][p.Y-1]) } if pMap[p.X+1][p.Y+1] != nil { group = buildGroup(pMap, group, pMap[p.X+1][p.Y+1]) } } if pMap[p.X][p.Y-1] != nil { group = buildGroup(pMap, group, pMap[p.X][p.Y-1]) } if pMap[p.X][p.Y+1] != nil { group = buildGroup(pMap, group, pMap[p.X][p.Y+1]) } return group }