Partial solution for 10.1. We've got grouping, but still need sorting and some printing cleanup.
This commit is contained in:
parent
06a0886b67
commit
e47b6c4a45
37
2018/day10-1.go
Normal file
37
2018/day10-1.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"internal/day10"
|
||||||
|
"internal/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
data := util.ReadInput()
|
||||||
|
points := day10.ParseInput(data)
|
||||||
|
|
||||||
|
i := 0 // debug
|
||||||
|
for {
|
||||||
|
for _, point := range points {
|
||||||
|
point.Move()
|
||||||
|
}
|
||||||
|
|
||||||
|
groups := day10.GroupPoints(points)
|
||||||
|
if groups != nil {
|
||||||
|
for _, group := range groups {
|
||||||
|
day10.DrawPoints(group)
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// debug
|
||||||
|
i++
|
||||||
|
if i > 100000 {
|
||||||
|
fmt.Println("oops")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// end debug
|
||||||
|
}
|
||||||
|
}
|
15
2018/internal/day10/debug.go
Normal file
15
2018/internal/day10/debug.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package day10
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *Point) DebugPrintPoint() {
|
||||||
|
fmt.Printf("p=<%d, %d>, v=<%d, %d>\n", p.X, p.Y, p.Xv, p.Yv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DebugPrintPoints(points []*Point) {
|
||||||
|
for _, point := range points {
|
||||||
|
point.DebugPrintPoint()
|
||||||
|
}
|
||||||
|
}
|
46
2018/internal/day10/grid.go
Normal file
46
2018/internal/day10/grid.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
// Unused solution: this works fine for the example, but doesn't work at all for the
|
||||||
|
// real input data.
|
||||||
|
package day10
|
||||||
|
|
||||||
|
import "fmt" // debug
|
||||||
|
|
||||||
|
// AnalyzePoints tries to determine whether the data looks sufficiently ordered.
|
||||||
|
// It returns 'true' if it thinks we've got letters.
|
||||||
|
func AnalyzePoints(points []*Point) bool {
|
||||||
|
pointArr := makeBuffer(points)
|
||||||
|
|
||||||
|
// count adjacencies
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
for y := 0; y < len(pointArr[0]); y++ {
|
||||||
|
for x := 0; x < len(pointArr); x++ {
|
||||||
|
if pointArr[x][y] != '#' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if checkPoint(pointArr, x-1, y) ||
|
||||||
|
checkPoint(pointArr, x+1, y) ||
|
||||||
|
checkPoint(pointArr, x, y-1) ||
|
||||||
|
checkPoint(pointArr, x, y+1) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(float64(count) / float64(len(points))) // debug
|
||||||
|
return float64(count) >= 0.8*float64(len(points))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks the point in the grid. If it is '#' returns true.
|
||||||
|
func checkPoint(grid [][]byte, x, y int) bool {
|
||||||
|
if x < 0 || x >= len(grid) ||
|
||||||
|
y < 0 || y >= len(grid[0]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if grid[x][y] == '#' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
114
2018/internal/day10/groups.go
Normal file
114
2018/internal/day10/groups.go
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
package day10
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: sort the groups
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
39
2018/internal/day10/input.go
Normal file
39
2018/internal/day10/input.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package day10
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseInput(data []string) []*Point {
|
||||||
|
points := []*Point{}
|
||||||
|
regex := regexp.MustCompile("position=<(.*)> velocity=<(.*)>")
|
||||||
|
|
||||||
|
for _, line := range data {
|
||||||
|
p := &Point{}
|
||||||
|
matches := regex.FindSubmatch([]byte(line))
|
||||||
|
p.X, p.Y = parseVector2(string(matches[1]))
|
||||||
|
p.Xv, p.Yv = parseVector2(string(matches[2]))
|
||||||
|
|
||||||
|
points = append(points, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return points
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseVector2(data string) (int, int) {
|
||||||
|
dataBuffer := strings.Split(data, ", ")
|
||||||
|
|
||||||
|
x, err := strconv.Atoi(strings.TrimSpace(dataBuffer[0]))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
y, err := strconv.Atoi(strings.TrimSpace(dataBuffer[1]))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return x, y
|
||||||
|
}
|
77
2018/internal/day10/point.go
Normal file
77
2018/internal/day10/point.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package day10
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Point struct {
|
||||||
|
X, Y int
|
||||||
|
Xv, Yv int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Point) Move() {
|
||||||
|
p.X += p.Xv
|
||||||
|
p.Y += p.Yv
|
||||||
|
}
|
||||||
|
|
||||||
|
func DrawPoints(points []*Point) {
|
||||||
|
pointArr := makeBuffer(points)
|
||||||
|
|
||||||
|
// print the buffer
|
||||||
|
for y := 0; y < len(pointArr[0]); y++ {
|
||||||
|
for x := 0; x < len(pointArr); x++ {
|
||||||
|
if pointArr[x][y] != 0 {
|
||||||
|
fmt.Printf("%c", pointArr[x][y])
|
||||||
|
} else {
|
||||||
|
fmt.Printf(".")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// findBounds returns xMin, xMax, yMin, and yMax from the provided points.
|
||||||
|
func findBounds(points []*Point) (int, int, int, int) {
|
||||||
|
// find min and max values
|
||||||
|
var xMin, xMax, yMin, yMax int
|
||||||
|
for _, point := range points {
|
||||||
|
if point.X < xMin {
|
||||||
|
xMin = point.X
|
||||||
|
}
|
||||||
|
if point.X > xMax {
|
||||||
|
xMax = point.X
|
||||||
|
}
|
||||||
|
if point.Y < yMin {
|
||||||
|
yMin = point.Y
|
||||||
|
}
|
||||||
|
if point.Y > yMax {
|
||||||
|
yMax = point.Y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return xMin, xMax, yMin, yMax
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeBuffer(points []*Point) [][]byte {
|
||||||
|
xMin, xMax, yMin, yMax := findBounds(points)
|
||||||
|
// get the total magnitude; we'll do adjustments later
|
||||||
|
xRange := xMax - xMin
|
||||||
|
yRange := yMax - yMin
|
||||||
|
|
||||||
|
// construct the buffer
|
||||||
|
pointArr := make([][]byte, xRange+1)
|
||||||
|
for i := 0; i < len(pointArr); i++ {
|
||||||
|
pointArr[i] = make([]byte, yRange+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// put the points into the buffer
|
||||||
|
for _, p := range points {
|
||||||
|
// the x and y values are adjusted by the mins
|
||||||
|
x := p.X - xMin
|
||||||
|
y := p.Y - yMin
|
||||||
|
|
||||||
|
pointArr[x][y] = '#'
|
||||||
|
}
|
||||||
|
|
||||||
|
return pointArr
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user