Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- Strategy:
- 1- Parse the data to get two sets of instructions containing directions and distances of each movement
- 2- Turn the instructions into a set of segments that the wire covers
- 3- Compare the vertical and horizontal segments of the two wires since those are the only ones that will cross, build list of coordinates for intersection
- ** Question: Could vertical/horizontal segments of the two wires be superposed?
- 4- Compare the manhattan distances for each intersection
- */
- package day3
- import (
- "errors"
- "fmt"
- "io/ioutil"
- "log"
- "strconv"
- "strings"
- "gonum.org/v1/plot"
- "gonum.org/v1/plot/plotter"
- "gonum.org/v1/plot/plotutil"
- "gonum.org/v1/plot/vg"
- )
- const (
- fileName = "day3/input.txt"
- shouldPlot = false
- plotOutput = "plot.png"
- )
- /* Custom types */
- type wire []instruction
- type instruction struct {
- dir rune
- dist int64
- }
- type coord struct {
- x, y int64
- }
- type segment struct {
- c1 coord
- c2 coord
- }
- /* Functions for parsing */
- func parseWire(wtext string) (wire, error) {
- var newWire wire
- for _, v := range strings.Split(wtext, ",") {
- dist, err := strconv.ParseInt(v[1:], 10, 64)
- if err != nil {
- return nil, err
- }
- dir := rune(v[0])
- if dir != 'U' && dir != 'D' && dir != 'L' && dir != 'R' {
- return nil, errors.New("Invalid instruction for wire")
- }
- newWire = append(newWire, instruction{dir: dir, dist: dist})
- }
- return newWire, nil
- }
- func parseInput(fileName string) (wire, wire, error) {
- allInput, err := ioutil.ReadFile(fileName)
- if err != nil {
- return nil, nil, err
- }
- split := strings.Split(string(allInput), "\n")
- if len(split) != 2 {
- return nil, nil, errors.New("Input did not provide information about two wires")
- }
- wtext1, wtext2 := split[0], split[1]
- w1, err := parseWire(wtext1)
- if err != nil {
- return nil, nil, err
- }
- w2, err := parseWire(wtext2)
- if err != nil {
- return nil, nil, err
- }
- return w1, w2, nil
- }
- /* Functions for getting segments from wire */
- func (w wire) toSegments() (hSegs []segment, vSegs []segment) {
- var x, y int64
- for _, inst := range w {
- isHor := false
- newX, newY := x, y
- if inst.dir == 'U' {
- newY += inst.dist
- } else if inst.dir == 'D' {
- newY -= inst.dist
- } else {
- isHor = true
- if inst.dir == 'L' {
- newX -= inst.dist
- } else {
- newX += inst.dist
- }
- }
- newSegment := segment{coord{x, y}, coord{newX, newY}}
- if isHor {
- hSegs = append(hSegs, newSegment)
- } else {
- vSegs = append(vSegs, newSegment)
- }
- x = newX
- y = newY
- }
- return
- }
- /* Helper for intersects */
- func intersects(hs segment, vs segment) bool {
- min := func(a, b int64) int64 {
- if a > b {
- return b
- }
- return a
- }
- max := func(a, b int64) int64 {
- if a < b {
- return b
- }
- return a
- }
- return min(hs.c1.x, hs.c2.x) <= vs.c1.x && vs.c1.x <= max(hs.c1.x, hs.c2.x) &&
- min(vs.c1.y, vs.c2.y) <= hs.c1.y && hs.c1.y <= max(vs.c1.y, vs.c2.y) &&
- !(hs.c1.x == 0 && vs.c1.y == 0)
- }
- /* Function for getting intersections of two lists of segments */
- func findIntersections(hSegs []segment, vSegs []segment) (intersections []coord) {
- for _, hs := range hSegs {
- for _, vs := range vSegs {
- if intersects(hs, vs) {
- // There's an intersection
- intersections = append(intersections, coord{x: vs.c1.x, y: hs.c1.y})
- }
- }
- }
- return
- }
- func manDist(c coord) int64 {
- abs := func(x int64) int64 {
- if x < 0 {
- return -x
- }
- return x
- }
- return abs(c.x) + abs(c.y)
- }
- /* Function for finding the coordinate with the smallest Manhattan distance */
- func closestCoord(coords []coord) (coord, error) {
- if len(coords) == 0 {
- return coord{}, errors.New("No coords provided to closestCords")
- }
- minDist := manDist(coords[0])
- minDistIndex := 0
- for i, c := range coords[1:] {
- curDist := manDist(c)
- if curDist < minDist {
- minDist = curDist
- minDistIndex = i + 1
- }
- }
- return coords[minDistIndex], nil
- }
- // Solution ...
- func Solution() {
- // Parse the data into nice structs
- w1, w2, err := parseInput(fileName)
- if err != nil {
- log.Fatalln(err.Error())
- }
- // Get the horizontal and vertical segments for the first wire
- w1HSegs, w1VSegs := w1.toSegments()
- w2HSegs, w2VSegs := w2.toSegments()
- // Find all the intersections
- intersections := findIntersections(w1HSegs, w2VSegs)
- intersections = append(intersections, findIntersections(w2HSegs, w1VSegs)...)
- // Find the closest point
- closest, err := closestCoord(intersections)
- if err != nil {
- log.Fatalln(err.Error())
- }
- fmt.Printf("Found solution, closest intersection coordinate is (x=%d, y=%d). Distance=%d\n",
- closest.x, closest.y, manDist(closest))
- if shouldPlot {
- p, err := plot.New()
- if err != nil {
- log.Fatalln(err.Error())
- }
- plotutil.AddLinePoints(p,
- "L1", segToXYs(w1VSegs), segToXYs(w1HSegs),
- "L2", segToXYs(w2VSegs), segToXYs(w2HSegs))
- p.Save(4*vg.Inch, 4*vg.Inch, plotOutput)
- fmt.Printf("Successfully saved plot at (%s)\n", plotOutput)
- }
- }
- /* Helper for converting segments -> plot points */
- func segToXYs(segs []segment) plotter.XYs {
- var xysPts []plotter.XY
- for _, val := range segs {
- xysPts = append(xysPts, plotter.XY{X: float64(val.c1.x), Y: float64(val.c1.y)})
- xysPts = append(xysPts, plotter.XY{X: float64(val.c2.x), Y: float64(val.c2.y)})
- }
- return xysPts
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement