main.go (4716B)
1 package main 2 3 import ( 4 "bufio" 5 "fmt" 6 "log" 7 "math" 8 "os" 9 "strconv" 10 ) 11 12 type Pos interface { 13 GetX() int 14 GetY() int 15 } 16 17 func Distance(p1 Pos, p2 Pos) int { 18 return int(math.Abs(float64(p1.GetX()-p2.GetX())) + math.Abs(float64(p1.GetY()-p2.GetY()))) 19 } 20 21 type Sensor struct { 22 x, y, r int 23 } 24 25 func (s Sensor) GetX() int { return s.x } 26 func (s Sensor) GetY() int { return s.y } 27 28 type Vec2 struct { 29 x, y int 30 } 31 32 func (v Vec2) GetX() int { return v.x } 33 func (v Vec2) GetY() int { return v.y } 34 35 func max(a, b int) int { 36 if a > b { 37 return a 38 } 39 return b 40 } 41 func min(a, b int) int { 42 if a < b { 43 return a 44 } 45 return b 46 } 47 48 func abs(a int) int { 49 return int(math.Abs(float64(a))) 50 } 51 52 type Line [2]Vec2 53 54 func (l1 Line) Intersection(l2 Line) (Vec2, bool) { 55 var v Vec2 56 x1, x2, x3, x4 := l1[0].x, l1[1].x, l2[0].x, l2[1].x 57 y1, y2, y3, y4 := l1[0].y, l1[1].y, l2[0].y, l2[1].y 58 59 d := (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4) 60 if d == 0 { 61 return v, false 62 } 63 64 pre := (x1*y2 - y1*x2) 65 post := (x3*y4 - y3*x4) 66 x := (pre*(x3-x4) - (x1-x2)*post) / d 67 y := (pre*(y3-y4) - (y1-y2)*post) / d 68 69 if x < min(x1, x2) || x > max(x1, x2) || 70 x < min(x3, x4) || x > max(x3, x4) { 71 return v, false 72 } 73 if y < min(y1, y2) || y > max(y1, y2) || 74 y < min(y3, y4) || y > max(y3, y4) { 75 return v, false 76 } 77 78 v.x = x 79 v.y = y 80 81 return v, true 82 } 83 84 type Sensors []Sensor 85 86 func (ss Sensors) Have(s1 Sensor) bool { 87 for _, s := range ss { 88 if s1.x == s.x && s1.y == s.y { 89 return true 90 } 91 } 92 return false 93 } 94 95 type Beacons []Vec2 96 97 func (bs Beacons) Have(b1 Vec2) bool { 98 for _, b := range bs { 99 if b1.x == b.x && b1.y == b.y { 100 return true 101 } 102 } 103 return false 104 } 105 106 func main() { 107 if len(os.Args) > 2 { 108 y, err := strconv.Atoi(os.Args[2]) 109 if err != nil { 110 log.Fatal(err) 111 } 112 day15(os.Args[1], y) 113 } else if len(os.Args) == 1 { 114 fmt.Printf("usage: %s inputfile.txt ROW_NUM\n", os.Args[0]) 115 } else { 116 fmt.Println("No input data") 117 } 118 } 119 120 func day15(input_file string, target_y int) { 121 fmt.Printf("day 15 input filename: %s\n", input_file) 122 input, err := os.Open(input_file) 123 if err != nil { 124 log.Fatal(err) 125 } 126 defer input.Close() 127 scanner := bufio.NewScanner(input) 128 var sensors Sensors 129 var beacons Beacons 130 for scanner.Scan() { 131 in := scanner.Text() 132 var s Sensor 133 var b Vec2 134 num, err := fmt.Sscanf(in, "Sensor at x=%d, y=%d: closest beacon is at x=%d, y=%d", &s.x, &s.y, &b.x, &b.y) 135 if num != 4 || err != nil { 136 log.Fatal("Parsing error", err) 137 } 138 s.r = Distance(s, b) 139 sensors = append(sensors, s) 140 if !beacons.Have(b) { 141 beacons = append(beacons, b) 142 } 143 } 144 if err = scanner.Err(); err != nil { 145 log.Fatal(err) 146 } 147 148 points := make(map[int]bool) 149 for _, s := range sensors { 150 if Distance(s, Vec2{x: s.x, y: target_y}) <= s.r { 151 for x := s.x - s.r; x <= s.x+s.r; x++ { 152 p := Vec2{x: x, y: target_y} 153 if Distance(s, p) <= s.r { 154 if !beacons.Have(p) { 155 points[x] = true 156 } 157 } 158 } 159 } 160 } 161 fmt.Println("Points covered:", len(points)) 162 163 // search all pairs of sensors with areas distanced 1 point away from each other borders 164 var pairs []Sensors 165 for i, s1 := range sensors { 166 for j, s2 := range sensors { 167 if i == j { 168 continue 169 } 170 if Distance(s1, s2) == s1.r+s2.r+2 && s1.x != s2.x && s1.y != s2.y { 171 add := true 172 for _, p := range pairs { 173 if p.Have(s1) && p.Have(s1) { 174 add = false 175 break 176 } 177 } 178 if add { 179 pairs = append(pairs, Sensors{s1, s2}) 180 } 181 } 182 } 183 } 184 185 var lines []Line 186 for _, p := range pairs { 187 s1 := p[0] 188 s2 := p[1] 189 var line Line 190 if s1.x > s2.x && s1.y > s2.y { // top-left 191 line[0].x = s1.x - s1.r - 1 192 line[0].y = s1.y 193 line[1].x = s1.x 194 line[1].y = s1.y - s1.r - 1 195 } else if s1.x < s2.x && s1.y > s2.y { // top-right 196 line[0].x = s1.x 197 line[0].y = s1.y - s1.r - 1 198 line[1].x = s1.x + s1.r + 1 199 line[1].y = s1.y 200 } else if s1.x < s2.x && s1.y < s2.y { // bottom-right 201 line[0].x = s1.x 202 line[0].y = s1.y + s1.r + 1 203 line[1].x = s1.x + s1.r + 1 204 line[1].y = s1.y 205 } else if s1.x > s2.x && s1.y < s2.y { // bottom-left 206 line[0].x = s1.x - s1.r - 1 207 line[0].y = s1.y 208 line[1].x = s1.x 209 line[1].y = s1.y + s1.r + 1 210 } else { 211 log.Fatal("invalid line") 212 } 213 lines = append(lines, line) 214 } 215 216 // those are actually points but I don't want to rename Beacons to something else at this point since they did carry me so far 217 var pts Beacons 218 for i, l1 := range lines { 219 for j, l2 := range lines { 220 if i == j { 221 continue 222 } 223 p, found := l1.Intersection(l2) 224 if found && !pts.Have(p) { 225 pts = append(pts, p) 226 } 227 } 228 } 229 230 for _, p := range pts { 231 if p.x >= 0 && p.x <= target_y*2 && 232 p.y >= 0 && p.y <= target_y*2 { 233 fmt.Println("Point checksum:", p.x*4000000+p.y) 234 break 235 } 236 } 237 }