From b6a56554af563930fdb9030b5ff35d5fa75e3b77 Mon Sep 17 00:00:00 2001 From: efim Date: Sun, 24 Dec 2023 07:42:04 +0000 Subject: [PATCH] day24, example --- day24/example | 5 ++ day24/lines.go | 147 ++++++++++++++++++++++++++++++++++++ day24/lines_test.go | 71 +++++++++++++++++ day24/neverTellMeTheOdds.go | 12 +++ day24/notes.org | 29 +++++++ main.go | 6 +- 6 files changed, 267 insertions(+), 3 deletions(-) create mode 100644 day24/example create mode 100644 day24/lines.go create mode 100644 day24/lines_test.go create mode 100644 day24/neverTellMeTheOdds.go create mode 100644 day24/notes.org diff --git a/day24/example b/day24/example new file mode 100644 index 0000000..35963dc --- /dev/null +++ b/day24/example @@ -0,0 +1,5 @@ +19, 13, 30 @ -2, 1, -2 +18, 19, 22 @ -1, -1, -2 +20, 25, 34 @ -2, -2, -4 +12, 31, 28 @ -1, -2, -1 +20, 19, 15 @ 1, -5, -3 diff --git a/day24/lines.go b/day24/lines.go new file mode 100644 index 0000000..0a1c823 --- /dev/null +++ b/day24/lines.go @@ -0,0 +1,147 @@ +package day24 + +import ( + "log" + "os" + "strconv" + "strings" +) + +const ( + CoordMin int = 7 + CoordMax int = 27 +) + +type Point struct { + x, y, Z int +} + +type HailParam struct { + p0, p1 Point + Dx, Dy, Dz int + line string +} +func (h HailParam)String() string { + return "(" + h.line + ")" +} + +func CheckPairwiseIntersections(hails []HailParam) (totalIntersections int) { + for i, hail := range hails { + for j := i+1; j < len(hails); j++ { + otherHail := hails[j] + intersect := CheckTaskIntersection(hail, otherHail) + if intersect { + totalIntersections += 1 + } + } + } + return +} + +func CheckTaskIntersection(h1, h2 HailParam) (doIntersect bool) { + log.Printf("intersecting %+v and %+v\n", h1, h2) + x, y, intersectAtAll := IntersectByTwoPoints(h1, h2) + if !intersectAtAll { + log.Printf("no intersection at all\n") + return false + } + if x < float64(CoordMin) || x > float64(CoordMax) || + y < float64(CoordMin) || y > float64(CoordMax) { + log.Printf("intersect at %f %f but outside of area\n", x, y) + return false // outside of area + } + isH1Future := h1.FloatPointInFuture(x,y) + isH2Future := h2.FloatPointInFuture(x,y) + + if !isH1Future { + log.Printf("in the past for h1\n") + } + if !isH2Future { + log.Printf("in the past for h2\n") + } + + if !isH1Future || !isH2Future { + return false + } + + log.Println("intersect inside of the area") + return true +} + +// in 2d only +func IntersectByTwoPoints(h1, h2 HailParam) (intersectionX, intersectoinY float64, isIntersecting bool) { + p1 := h1.p0 + p2 := h1.p1 + p3 := h2.p0 + p4 := h2.p1 + + denominator := (p1.x - p2.x)*(p3.y - p4.y) - (p1.y - p2.y)*(p3.x - p4.x) + if denominator == 0 { + return 0, 0, false + } + + divisibleX := (p1.x * p2.y - p1.y * p2.x)*(p3.x - p4.x) - (p1.x - p2.x)*(p3.x * p4.y - p3.y * p4.x) + divisibleY := (p1.x * p2.y - p1.y * p2.x)*(p3.y - p4.y) - (p1.y - p2.y)*(p3.x * p4.y - p3.y * p4.x) + + x := float64(divisibleX) / float64(denominator) + y := float64(divisibleY) / float64(denominator) + + return x, y, true +} + +func (h HailParam)PointInFuture(p Point) bool { + xPositiveSteps := (p.x - h.p0.x) * h.Dx >= 0 + yPositiveSteps := (p.y - h.p0.y) * h.Dy >= 0 + zPositiveSteps := (p.Z - h.p0.Z) * h.Dz >= 0 + return xPositiveSteps && yPositiveSteps && zPositiveSteps +} +func (h HailParam)FloatPointInFuture(x, y float64) bool { + xPositiveSteps := (x - float64(h.p0.x)) * float64(h.Dx) >= 0 + yPositiveSteps := (y - float64(h.p0.y)) * float64(h.Dy) >= 0 + return xPositiveSteps && yPositiveSteps +} + +// 19, 13, 30 @ -2, 1, -2 +func ReadHailLine(line string) (h HailParam) { + h.line = line + line = strings.ReplaceAll(line, "@", "") + line = strings.ReplaceAll(line, ",", "") + fields := strings.Fields(line) + + h.p0.x = AtoIOrPanic(fields[0]) + h.p0.y = AtoIOrPanic(fields[1]) + h.p0.Z = AtoIOrPanic(fields[2]) + h.Dx = AtoIOrPanic(fields[3]) + h.Dy = AtoIOrPanic(fields[4]) + h.Dz = AtoIOrPanic(fields[5]) + + h.p1.x = h.p0.x + h.Dx + h.p1.y = h.p0.y + h.Dy + h.p1.Z = h.p0.Z + h.Dz + + return +} + +func ReadHailFile(filename string) []HailParam { + bytes, err := os.ReadFile(filename) + if err != nil { + panic(err) + } + text := strings.TrimSpace(string(bytes)) + lines := strings.Split(text, "\n") + result := make([]HailParam, len(lines)) + + for i, line := range lines { + result[i] = ReadHailLine(line) + } + + return result +} + +func AtoIOrPanic(str string) (num int) { + num, err := strconv.Atoi(str) + if err != nil { + panic(err) + } + return +} diff --git a/day24/lines_test.go b/day24/lines_test.go new file mode 100644 index 0000000..cf0de5c --- /dev/null +++ b/day24/lines_test.go @@ -0,0 +1,71 @@ +package day24 + +import ( + "strings" + "testing" +) + +func TestReadLine(t *testing.T) { + lines := `19, 13, 30 @ -2, 1, -2 +18, 19, 22 @ -1, -1, -2 +20, 25, 34 @ -2, -2, -4 +12, 31, 28 @ -1, -2, -1 +20, 19, 15 @ 1, -5, -3` + + for _, line := range strings.Split(lines, "\n") { + hail := ReadHailLine(line) + t.Log(hail) + } + +} + +func TestSecondPointIsInFuture(t *testing.T) { + lines := `19, 13, 30 @ -2, 1, -2 +18, 19, 22 @ -1, -1, -2 +20, 25, 34 @ -2, -2, -4 +12, 31, 28 @ -1, -2, -1 +20, 19, 15 @ 1, -5, -3` + + for _, line := range strings.Split(lines, "\n") { + hail := ReadHailLine(line) + t.Log(hail) + t.Logf("calced seconds point %+v is in future %t\n", hail.p1, hail.PointInFuture(hail.p1)) + } + +} + +func TestIntersectExampleOne(t *testing.T) { + // Hailstone A: 19, 13, 30 @ -2, 1, -2 + // Hailstone B: 18, 19, 22 @ -1, -1, -2 + // Hailstones' paths will cross inside the test area (at x=14.333, y=15.333). + + hA := ReadHailLine("19, 13, 30 @ -2, 1, -2") + hB := ReadHailLine("18, 19, 22 @ -1, -1, -2") + + x, y, check := IntersectByTwoPoints(hA, hB) + if !check { + panic("should intersect") + } + t.Logf("got intersection at %f %f", x, y) +} + +func TestIntersectExampleTwo(t *testing.T) { + // Hailstone A: 18, 19, 22 @ -1, -1, -2 + // Hailstone B: 20, 25, 34 @ -2, -2, -4 + hA := ReadHailLine("18, 19, 22 @ -1, -1, -2") + hB := ReadHailLine("20, 25, 34 @ -2, -2, -4") + + x, y, check := IntersectByTwoPoints(hA, hB) + if check { + panic("should not intersect") + } + t.Logf("got intersection at %f %f", x, y) +} + +func TestExamplePairwiseChecks(t *testing.T) { + filename := "example" + hails := ReadHailFile(filename) + + intersections := CheckPairwiseIntersections(hails) + t.Log("counted intersections ", intersections) +} diff --git a/day24/neverTellMeTheOdds.go b/day24/neverTellMeTheOdds.go new file mode 100644 index 0000000..2c5a928 --- /dev/null +++ b/day24/neverTellMeTheOdds.go @@ -0,0 +1,12 @@ +package day24 + +import ( + "fmt" +) + +func Run() int { + fmt.Println("hello day 24, i'm getting tired") + filenae := "day24/example" + hails := ReadHailFile(filenae) + return CheckPairwiseIntersections(hails) +} diff --git a/day24/notes.org b/day24/notes.org new file mode 100644 index 0000000..477c28f --- /dev/null +++ b/day24/notes.org @@ -0,0 +1,29 @@ +#+title: Notes +* i want help from math +https://math.stackexchange.com/questions/28503/how-to-find-intersection-of-two-lines-in-3d + +'vector parametric form' is exactly what we're getting in the input? +* huh and only 'looking forward in time' so solutions with negative t are not important. +cooool +** i see that speeds are integers, so updated points are integers. +maybe i do calculation of every line on every time point? + +and if i do that is there a way to get intersections efficietly? +** i'll also need the ends for lines? ends to the line segments. +with limits on the x & y by the task + +for example both 7 <= <= 27 +for input 200000000000000 <= <= 400000000000000 + +also. can't we move the coords? maybe not? maybe only for one +so, what do i do? to get the ends of the lines? +i try to calcluate with both x & y in 2 min\max. then if the other is ok, than that's the ends? +wait, what happens when i do x = 7, and x = 27 and y is outside? it means no intesections, i guess +or it could be outside from different sides, so not all x are ok, but there's still line there +* Using homogeneous coordinates +https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection +no, i don't understant that +* https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line +with 2 points. i guess +but also - check if the point in future of the hail, by comparing with speeds? +should be easy diff --git a/main.go b/main.go index a95f887..5fda45c 100644 --- a/main.go +++ b/main.go @@ -4,15 +4,15 @@ import ( "log" "time" - "sunshine.industries/aoc2023/day23" + "sunshine.industries/aoc2023/day24" ) func main() { startTime := time.Now() log.Print("> starting run:") - result := day23.Run() - log.Printf("\n\nday23 result: %d\n****\n", result) + result := day24.Run() + log.Printf("\n\nday24 result: %d\n****\n", result) endTime := time.Now() diff := endTime.Sub(startTime) log.Printf("execution took %s", diff.String())