adventofcode/2021/day05/main.go

204 lines
3.9 KiB
Go

package main
import (
"fmt"
"strings"
h "git.bullercodeworks.com/brian/adventofcode/helpers"
)
var minX, maxX, minY, maxY int
func main() {
maxX = h.MIN_INT
minX = h.MAX_INT
maxY = h.MIN_INT
minY = h.MAX_INT
inp := h.StdinToStringSlice()
lines := parseLines(inp)
fmt.Println("# Part 1")
findDangerSpots(filterStraight(lines))
fmt.Println("# Part 2")
findDangerSpots(lines)
}
var ventMap map[h.Coordinate]int
var dangerSpots []h.Coordinate
func dingMap(c h.Coordinate) {
s := ventMap[c]
s++
if s == 2 {
dangerSpots = append(dangerSpots, c)
}
ventMap[c] = s
}
func findDangerSpots(lines []Line) {
ventMap = make(map[h.Coordinate]int)
for i := range lines {
updateMinMax(lines[i])
}
for _, l := range lines {
// Find all points on this line
// All lines are 0 or 45 deg, so slope should always be 0,1,-1,INF,-INF
if l.start.X == l.end.X { // INF, -INF: vertical
for y := l.lowestY(); y <= l.highestY(); y++ {
dingMap(h.Coordinate{X: l.start.X, Y: y})
}
} else if l.start.Y == l.end.Y { //0: horizontal
for x := l.start.X; x <= l.end.X; x++ {
dingMap(h.Coordinate{Y: l.start.Y, X: x})
}
} else { // 1 or -1
y := l.start.Y
x := l.start.X
dingMap(h.Coordinate{Y: y, X: x})
diffY := -1
if l.start.Y < l.end.Y {
diffY = 1
}
for x != l.end.X && y != l.end.Y {
x += 1
y += diffY
dingMap(h.Coordinate{Y: y, X: x})
}
}
}
fmt.Println("Danger Spots:", len(dangerSpots))
}
type Line struct {
start h.Coordinate
end h.Coordinate
stX float64
stY float64
edX float64
edY float64
slope float64
}
func (l *Line) lowestY() int {
if l.start.Y < l.end.Y {
return l.start.Y
}
return l.end.Y
}
func (l *Line) highestY() int {
if l.start.Y > l.end.Y {
return l.start.Y
}
return l.end.Y
}
func (l *Line) intersects(c h.Coordinate) bool {
ptX := float64(c.X)
ptY := float64(c.Y)
if (c.X == l.start.X && c.Y == l.start.Y) || (c.X == l.end.X && c.Y == l.end.Y) {
return true
}
if c.X < l.start.X || c.X > l.end.X {
return false
}
if (c.Y < l.start.Y && c.Y < l.end.Y) || (c.Y > l.start.Y && c.Y > l.end.Y) {
return false
}
// Check if our slope is infinite
if l.start.X == l.end.X {
if l.start.Y < l.end.Y {
return c.X == l.start.X && c.Y >= l.start.Y && c.Y <= l.end.Y
} else {
return c.X == l.start.X && c.Y <= l.start.Y && c.Y >= l.end.Y
}
}
return slope(ptX, ptY, l.stX, l.stY) == l.slope
}
func (l Line) String() string {
return fmt.Sprintf("%d,%d -> %d,%d; [%f]", l.start.X, l.start.Y, l.end.X, l.end.Y, l.slope)
}
func LineFromString(str string) Line {
start := h.Coordinate{}
end := h.Coordinate{}
r := strings.NewReader(str)
_, err := fmt.Fscanf(r, "%d,%d -> %d,%d", &start.X, &start.Y, &end.X, &end.Y)
if err != nil {
panic(err)
}
var l Line
if start.X < end.X {
l = Line{
start: start,
end: end,
}
} else {
l = Line{
start: end,
end: start,
}
}
l.stX = float64(l.start.X)
l.stY = float64(l.start.Y)
l.edX = float64(l.end.X)
l.edY = float64(l.end.Y)
l.slope = slope(l.stX, l.stY, l.edX, l.edY)
return l
}
func updateMinMax(line Line) {
if line.start.X < minX {
minX = line.start.X
}
if line.start.X > maxX {
maxX = line.start.X
}
if line.end.X < minX {
minX = line.end.X
}
if line.end.X > maxX {
maxX = line.end.X
}
if line.start.Y < minY {
minY = line.start.Y
}
if line.start.Y > maxY {
maxY = line.start.Y
}
if line.end.Y < minY {
minY = line.end.Y
}
if line.end.Y > maxY {
maxY = line.end.Y
}
}
func parseLines(inp []string) []Line {
var ret []Line
for i := range inp {
ret = append(ret, LineFromString(inp[i]))
}
return ret
}
func filterStraight(lines []Line) []Line {
var ret []Line
for i := range lines {
if lines[i].start.X == lines[i].end.X || lines[i].start.Y == lines[i].end.Y {
ret = append(ret, lines[i])
}
}
return ret
}
func slope(x1, y1, x2, y2 float64) float64 {
return (y1 - y2) / (x1 - x2)
}