204 lines
3.9 KiB
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)
|
|
}
|