package day08 import ( "adventofcode2024/utils/grid2d" "adventofcode2024/utils/inputs" "fmt" ) type loc struct { x int y int } type pt struct { display rune antinodes int paired map[loc]pt visited map[rune]bool } func Part1(input string) int { grid := inputs.ToGrid2D(input, "\n", "", pt{display: '.'}, func(c string) pt { return pt{display: rune(c[0]), paired: make(map[loc]pt), visited: make(map[rune]bool)} }) for x1 := 0; x1 < grid.SizeX(); x1++ { for y1 := 0; y1 < grid.SizeY(); y1++ { pt1 := grid.Get(x1, y1) if pt1.display != '.' { for x2 := 0; x2 < grid.SizeX(); x2++ { for y2 := 0; y2 < grid.SizeY(); y2++ { pt2 := grid.Get(x2, y2) if x1 != x2 && y1 != y2 && pt1.display == pt2.display && !isPaired(x1, y1, pt1, pt2) { pt1.paired[loc{x: x2, y: y2}] = pt2 pt2.paired[loc{x: x1, y: y1}] = pt1 } } } } } } for x := 0; x < grid.SizeX(); x++ { for y := 0; y < grid.SizeY(); y++ { pt1 := grid.Get(x, y) if pt1.display != '.' { for ploc := range pt1.paired { addAntiNodes(x, y, ploc.x, ploc.y, grid) } } } } antinodes := 0 for x := 0; x < grid.SizeX(); x++ { for y := 0; y < grid.SizeY(); y++ { pt := grid.Get(x, y) if pt.antinodes > 0 { antinodes++ } } } return antinodes } func Part2(input string) int { grid := inputs.ToGrid2D(input, "\n", "", pt{display: '.'}, func(c string) pt { return pt{display: rune(c[0]), paired: make(map[loc]pt), visited: make(map[rune]bool)}}) for x1 := 0; x1 < grid.SizeX(); x1++ { for y1 := 0; y1 < grid.SizeY(); y1++ { pt1 := grid.Get(x1, y1) if pt1.display != '.' { for x2 := 0; x2 < grid.SizeX(); x2++ { for y2 := 0; y2 < grid.SizeY(); y2++ { pt2 := grid.Get(x2, y2) if x1 != x2 && y1 != y2 && pt1.display == pt2.display && !isPaired(x1, y1, pt1, pt2) { pt1.paired[loc{x: x2, y: y2}] = pt2 pt2.paired[loc{x: x1, y: y1}] = pt1 pt1.antinodes++ pt2.antinodes++ grid.Set(x1, y1, pt1) grid.Set(x2, y2, pt2) } } } } } } for x := 0; x < grid.SizeX(); x++ { for y := 0; y < grid.SizeY(); y++ { pt1 := grid.Get(x, y) if pt1.display != '.' { for ploc := range pt1.paired { addAntiNodes_p2(pt1.display, x, y, ploc.x, ploc.y, grid) } } } } antinodes := 0 fmt.Printf("%v\n", grid.StringWithFormatter(formatter)) for x := 0; x < grid.SizeX(); x++ { for y := 0; y < grid.SizeY(); y++ { pt := grid.Get(x, y) if pt.antinodes > 0 { antinodes++ } else { if pt.display != '.' { antinodes++ } } } } return antinodes } func formatter(p pt, x int, y int) string { if p.antinodes > 0 { return "#" } return "." } func isPaired(x int, y int, p1 pt, pt2 pt) bool { return p1.paired[loc{x: x, y: y}].display == pt2.display } func addAntiNodes(x1 int, y1 int, x2 int, y2 int, grid *grid2d.Grid[pt]) { px1, py1, px2, py2 := offsetPoints(x1, y1, x2, y2) if px1 < 0 || px1 >= grid.SizeX() || py1 < 0 || py1 >= grid.SizeY() { return } pt1 := grid.Get(px1, py1) pt1.antinodes++ grid.Set(px1, py1, pt1) if px2 < 0 || px2 >= grid.SizeX() || py2 < 0 || py2 >= grid.SizeY() { return } pt2 := grid.Get(px2, py2) pt2.antinodes++ grid.Set(px2, py2, pt2) } func addAntiNodes_p2(frequency rune, x1 int, y1 int, x2 int, y2 int, grid *grid2d.Grid[pt]) { px1, py1, px2, py2 := offsetPoints(x1, y1, x2, y2) if !(px1 < 0 || px1 >= grid.SizeX() || py1 < 0 || py1 >= grid.SizeY()) { pt1 := grid.Get(px1, py1) if !pt1.visited[frequency] { pt1.antinodes++ pt1.visited[frequency] = true grid.Set(px1, py1, pt1) addAntiNodes_p2(frequency, x1, y1, px1, py1, grid) } } if !(px2 < 0 || px2 >= grid.SizeX() || py2 < 0 || py2 >= grid.SizeY()) { pt2 := grid.Get(px2, py2) if !pt2.visited[frequency] { pt2.antinodes++ pt2.visited[frequency] = true grid.Set(px2, py2, pt2) addAntiNodes_p2(frequency, x2, y2, px2, py2, grid) } } } // Function to calculate two points symmetrically placed above and below the line func offsetPoints(x1, y1, x2, y2 int) (int, int, int, int) { dx := x1 - x2 dy := y1 - y2 p1x := x1 + dx p1y := y1 + dy p2x := x2 - dx p2y := y2 - dy return p1x, p1y, p2x, p2y }