advent2022

Advent of Code 2022 Solutions
git clone git://bsandro.tech/advent2022
Log | Files | Refs | README | LICENSE

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 }