main.go (4146B)
1 package main 2 3 import ( 4 "bufio" 5 "fmt" 6 "log" 7 "math" 8 "os" 9 ) 10 11 func abs(a int) int { 12 return int(math.Abs(float64(a))) 13 } 14 func copysign(x, s int) int { 15 return int(math.Copysign(float64(x), float64(s))) 16 } 17 18 type Pos struct { 19 x, y int 20 } 21 22 func (p Pos) Diff(o Pos) Pos { 23 var diff Pos 24 diff.x = p.x - o.x 25 diff.y = p.y - o.y 26 return diff 27 } 28 func (p Pos) Dist(o Pos) int { 29 // returns maximum of distances by X or Y axis 30 diff := p.Diff(o) 31 dx := abs(diff.x) 32 dy := abs(diff.y) 33 if dx > dy { 34 return dx 35 } else { 36 return dy 37 } 38 } 39 40 type Rope struct { 41 id int 42 head, tail Pos 43 visited []Pos 44 attach *Rope 45 } 46 47 func (r *Rope) AddVisited(p Pos) { 48 add := true 49 for _, v := range r.visited { 50 if v.x == p.x && v.y == p.y { 51 add = false 52 break 53 } 54 } 55 if add { 56 r.visited = append(r.visited, p) 57 } 58 } 59 func (r *Rope) MoveTail() { 60 if r.head.Dist(r.tail) > 1 { 61 d := r.head.Diff(r.tail) 62 if abs(d.x) > 1 { 63 d.x /= 2 64 } 65 if abs(d.y) > 1 { 66 d.y /= 2 67 } 68 r.tail.x += d.x 69 r.tail.y += d.y 70 71 r.AddVisited(r.tail) 72 } 73 } 74 func (r *Rope) Step(dx, dy int) { 75 r.head.x += dx 76 r.head.y += dy 77 r.MoveTail() 78 79 if r.attach != nil { 80 d := r.tail.Diff(r.attach.head) 81 if d.x != 0 || d.y != 0 { 82 r.attach.MoveBy(d) 83 } 84 } 85 } 86 func (r *Rope) MoveBy(p Pos) { 87 ax, ay := abs(p.x), abs(p.y) 88 if ax != 0 && ay != 0 { 89 // diagonal move 90 for { 91 if ax == 0 || ay == 0 { 92 break 93 } 94 r.Step(copysign(1, p.x), copysign(1, p.y)) 95 ax-- 96 ay-- 97 } 98 } 99 if ax != 0 { 100 for i := 0; i < ax; i++ { 101 r.Step(copysign(1, p.x), 0) 102 } 103 } else if ay != 0 { 104 for i := 0; i < ay; i++ { 105 r.Step(0, copysign(1, p.y)) 106 } 107 } 108 } 109 func (r *Rope) Move(dir string, dist int) { 110 switch dir { 111 case "L": 112 r.MoveBy(Pos{x: -dist}) 113 case "R": 114 r.MoveBy(Pos{x: dist}) 115 case "U": 116 r.MoveBy(Pos{y: dist}) 117 case "D": 118 r.MoveBy(Pos{y: -dist}) 119 default: 120 log.Fatal("Invalid direction", dir) 121 } 122 } 123 124 type Chain struct { 125 first *Rope 126 last *Rope 127 size int 128 } 129 130 func (c *Chain) Move(dir string, dist int) { 131 c.first.Move(dir, dist) 132 // c.Print() 133 } 134 func (c *Chain) Print() { 135 var points []Pos 136 for rope := c.first; rope != nil; { 137 points = append(points, rope.head) 138 points = append(points, rope.tail) 139 rope = rope.attach 140 } 141 PrintPoints(points) 142 } 143 func makeChain(size int) Chain { 144 var c Chain 145 c.size = size 146 c.first = &Rope{} 147 c.first.AddVisited(c.first.tail) 148 149 prev := c.first 150 for i := 1; i < size; i++ { 151 r := &Rope{id: i} 152 r.AddVisited(r.tail) 153 prev.attach = r 154 prev = r 155 } 156 c.last = prev 157 return c 158 } 159 160 func main() { 161 if len(os.Args) > 1 { 162 day09(os.Args[1]) 163 } else if len(os.Args) == 1 { 164 fmt.Printf("usage: %s inputfile.txt\n", os.Args[0]) 165 } else { 166 fmt.Println("No input data") 167 } 168 } 169 170 func day09(input_file string) { 171 fmt.Printf("day 09 input filename: %s\n", input_file) 172 input, err := os.Open(input_file) 173 if err != nil { 174 log.Fatal(err) 175 } 176 defer input.Close() 177 scanner := bufio.NewScanner(input) 178 var rope Rope 179 rope.AddVisited(rope.tail) 180 chain := makeChain(9) 181 for scanner.Scan() { 182 in := scanner.Text() 183 var dir string 184 var dist int 185 found, err := fmt.Sscanf(in, "%s %d", &dir, &dist) 186 if err != nil || found != 2 { 187 log.Fatal("Invalid input string", in, err) 188 } 189 rope.Move(dir, dist) 190 chain.Move(dir, dist) 191 } 192 if err = scanner.Err(); err != nil { 193 log.Fatal(err) 194 } 195 196 fmt.Println("Part 1:", len(rope.visited)) 197 fmt.Println("Part 2:", len(chain.last.visited)) 198 199 // PrintPoints(chain.last.visited) 200 } 201 202 func PrintPoints(points []Pos) { 203 var min, max Pos 204 for _, p := range points { 205 if p.x < min.x { 206 min.x = p.x 207 } 208 if p.y < min.y { 209 min.y = p.y 210 } 211 if p.x > max.x { 212 max.x = p.x 213 } 214 if p.y > max.y { 215 max.y = p.y 216 } 217 } 218 if min.x > 0 { 219 min.x = 0 220 } 221 if min.y > 0 { 222 min.y = 0 223 } 224 dx, dy := -min.x, -min.y 225 mx, my := max.x+dx+3, max.y+dy+3 226 grid := make([][]bool, my) 227 for i := 0; i < my; i++ { 228 grid[i] = make([]bool, mx) 229 } 230 for _, p := range points { 231 p.x += dx 232 p.y += dy 233 grid[p.y][p.x] = true 234 } 235 for y := my - 1; y >= 0; y-- { 236 for x := 0; x < mx; x++ { 237 if grid[y][x] { 238 fmt.Print("#") 239 } else if x == dx && y == dy { 240 fmt.Print("X") 241 } else { 242 fmt.Print(".") 243 } 244 } 245 fmt.Print("\n") 246 } 247 }