Advertisement
Guest User

go day3 part 1

a guest
Dec 3rd, 2019
157
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Go 5.39 KB | None | 0 0
  1. /*
  2. Strategy:
  3. 1- Parse the data to get two sets of instructions containing directions and distances of each movement
  4. 2- Turn the instructions into a set of segments that the wire covers
  5. 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
  6. ** Question: Could vertical/horizontal segments of the two wires be superposed?
  7. 4- Compare the manhattan distances for each intersection
  8. */
  9.  
  10. package day3
  11.  
  12. import (
  13.     "errors"
  14.     "fmt"
  15.     "io/ioutil"
  16.     "log"
  17.     "strconv"
  18.     "strings"
  19.  
  20.     "gonum.org/v1/plot"
  21.     "gonum.org/v1/plot/plotter"
  22.     "gonum.org/v1/plot/plotutil"
  23.     "gonum.org/v1/plot/vg"
  24. )
  25.  
  26. const (
  27.     fileName   = "day3/input.txt"
  28.     shouldPlot = false
  29.     plotOutput = "plot.png"
  30. )
  31.  
  32. /* Custom types */
  33. type wire []instruction
  34.  
  35. type instruction struct {
  36.     dir  rune
  37.     dist int64
  38. }
  39.  
  40. type coord struct {
  41.     x, y int64
  42. }
  43.  
  44. type segment struct {
  45.     c1 coord
  46.     c2 coord
  47. }
  48.  
  49. /* Functions for parsing */
  50. func parseWire(wtext string) (wire, error) {
  51.     var newWire wire
  52.  
  53.     for _, v := range strings.Split(wtext, ",") {
  54.         dist, err := strconv.ParseInt(v[1:], 10, 64)
  55.  
  56.         if err != nil {
  57.             return nil, err
  58.         }
  59.  
  60.         dir := rune(v[0])
  61.  
  62.         if dir != 'U' && dir != 'D' && dir != 'L' && dir != 'R' {
  63.             return nil, errors.New("Invalid instruction for wire")
  64.         }
  65.  
  66.         newWire = append(newWire, instruction{dir: dir, dist: dist})
  67.     }
  68.  
  69.     return newWire, nil
  70. }
  71.  
  72. func parseInput(fileName string) (wire, wire, error) {
  73.     allInput, err := ioutil.ReadFile(fileName)
  74.  
  75.     if err != nil {
  76.         return nil, nil, err
  77.     }
  78.  
  79.     split := strings.Split(string(allInput), "\n")
  80.  
  81.     if len(split) != 2 {
  82.         return nil, nil, errors.New("Input did not provide information about two wires")
  83.     }
  84.  
  85.     wtext1, wtext2 := split[0], split[1]
  86.  
  87.     w1, err := parseWire(wtext1)
  88.  
  89.     if err != nil {
  90.         return nil, nil, err
  91.     }
  92.  
  93.     w2, err := parseWire(wtext2)
  94.  
  95.     if err != nil {
  96.         return nil, nil, err
  97.     }
  98.  
  99.     return w1, w2, nil
  100. }
  101.  
  102. /* Functions for getting segments from wire */
  103. func (w wire) toSegments() (hSegs []segment, vSegs []segment) {
  104.     var x, y int64
  105.  
  106.     for _, inst := range w {
  107.         isHor := false
  108.         newX, newY := x, y
  109.  
  110.         if inst.dir == 'U' {
  111.             newY += inst.dist
  112.         } else if inst.dir == 'D' {
  113.             newY -= inst.dist
  114.         } else {
  115.             isHor = true
  116.  
  117.             if inst.dir == 'L' {
  118.                 newX -= inst.dist
  119.             } else {
  120.                 newX += inst.dist
  121.             }
  122.         }
  123.  
  124.         newSegment := segment{coord{x, y}, coord{newX, newY}}
  125.  
  126.         if isHor {
  127.             hSegs = append(hSegs, newSegment)
  128.         } else {
  129.             vSegs = append(vSegs, newSegment)
  130.         }
  131.  
  132.         x = newX
  133.         y = newY
  134.     }
  135.  
  136.     return
  137. }
  138.  
  139. /* Helper for intersects */
  140. func intersects(hs segment, vs segment) bool {
  141.     min := func(a, b int64) int64 {
  142.         if a > b {
  143.             return b
  144.         }
  145.         return a
  146.     }
  147.  
  148.     max := func(a, b int64) int64 {
  149.         if a < b {
  150.             return b
  151.         }
  152.         return a
  153.     }
  154.  
  155.     return min(hs.c1.x, hs.c2.x) <= vs.c1.x && vs.c1.x <= max(hs.c1.x, hs.c2.x) &&
  156.         min(vs.c1.y, vs.c2.y) <= hs.c1.y && hs.c1.y <= max(vs.c1.y, vs.c2.y) &&
  157.         !(hs.c1.x == 0 && vs.c1.y == 0)
  158. }
  159.  
  160. /* Function for getting intersections of two lists of segments */
  161. func findIntersections(hSegs []segment, vSegs []segment) (intersections []coord) {
  162.     for _, hs := range hSegs {
  163.         for _, vs := range vSegs {
  164.             if intersects(hs, vs) {
  165.                 // There's an intersection
  166.                 intersections = append(intersections, coord{x: vs.c1.x, y: hs.c1.y})
  167.             }
  168.         }
  169.     }
  170.  
  171.     return
  172. }
  173.  
  174. func manDist(c coord) int64 {
  175.     abs := func(x int64) int64 {
  176.         if x < 0 {
  177.             return -x
  178.         }
  179.         return x
  180.     }
  181.  
  182.     return abs(c.x) + abs(c.y)
  183. }
  184.  
  185. /* Function for finding the coordinate with the smallest Manhattan distance */
  186. func closestCoord(coords []coord) (coord, error) {
  187.     if len(coords) == 0 {
  188.         return coord{}, errors.New("No coords provided to closestCords")
  189.     }
  190.  
  191.     minDist := manDist(coords[0])
  192.     minDistIndex := 0
  193.  
  194.     for i, c := range coords[1:] {
  195.         curDist := manDist(c)
  196.  
  197.         if curDist < minDist {
  198.             minDist = curDist
  199.             minDistIndex = i + 1
  200.         }
  201.     }
  202.  
  203.     return coords[minDistIndex], nil
  204. }
  205.  
  206. // Solution ...
  207. func Solution() {
  208.     // Parse the data into nice structs
  209.     w1, w2, err := parseInput(fileName)
  210.  
  211.     if err != nil {
  212.         log.Fatalln(err.Error())
  213.     }
  214.  
  215.     // Get the horizontal and vertical segments for the first wire
  216.     w1HSegs, w1VSegs := w1.toSegments()
  217.     w2HSegs, w2VSegs := w2.toSegments()
  218.  
  219.     // Find all the intersections
  220.     intersections := findIntersections(w1HSegs, w2VSegs)
  221.     intersections = append(intersections, findIntersections(w2HSegs, w1VSegs)...)
  222.  
  223.     // Find the closest point
  224.     closest, err := closestCoord(intersections)
  225.  
  226.     if err != nil {
  227.         log.Fatalln(err.Error())
  228.     }
  229.  
  230.     fmt.Printf("Found solution, closest intersection coordinate is (x=%d, y=%d). Distance=%d\n",
  231.         closest.x, closest.y, manDist(closest))
  232.  
  233.     if shouldPlot {
  234.         p, err := plot.New()
  235.  
  236.         if err != nil {
  237.             log.Fatalln(err.Error())
  238.         }
  239.  
  240.         plotutil.AddLinePoints(p,
  241.             "L1", segToXYs(w1VSegs), segToXYs(w1HSegs),
  242.             "L2", segToXYs(w2VSegs), segToXYs(w2HSegs))
  243.  
  244.         p.Save(4*vg.Inch, 4*vg.Inch, plotOutput)
  245.         fmt.Printf("Successfully saved plot at (%s)\n", plotOutput)
  246.     }
  247. }
  248.  
  249. /* Helper for converting segments -> plot points */
  250. func segToXYs(segs []segment) plotter.XYs {
  251.     var xysPts []plotter.XY
  252.  
  253.     for _, val := range segs {
  254.         xysPts = append(xysPts, plotter.XY{X: float64(val.c1.x), Y: float64(val.c1.y)})
  255.         xysPts = append(xysPts, plotter.XY{X: float64(val.c2.x), Y: float64(val.c2.y)})
  256.     }
  257.  
  258.     return xysPts
  259. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement