package day24 import ( "log" "os" "strconv" "strings" ) const ( // CoordMin int = 7 // CoordMax int = 27 CoordMin int = 200000000000000 CoordMax int = 400000000000000 ) type Point struct { x, y, Z int } type HailParam struct { p0, p1 Point Dx, Dy, Dz int line string // for 2d : ay + bx = 0 a, b, c int // for 2d : y = slope*x + shift slope, shift float64 } 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 := IntersectByLineEquasions(h1, h2) // x, y, intersectAtAll := IntersectByTwoPoints(h1, h2) // x, y, intersectAtAll := IntersectByTwoPointsAttempt2(h1, h2) // x, y, intersectAtAll := IntersectByTwoPointsAttempt3(h1, h2) x, y, intersectAtAll := IntersectBySlopeAndShift(h1, h2) if !intersectAtAll { log.Println("no intersection at all\n", x, y) return false } isH1Future := h1.FloatPointInFuture(x, y) isH2Future := h2.FloatPointInFuture(x, y) if !isH1Future { log.Printf("point %f, %f in the past for h1\n", x, y) } if !isH2Future { log.Printf("point %f, %f in the past for h2\n", x, y) } if !isH1Future || !isH2Future { 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 } log.Println("> intersect inside of the area! ", x, y) return true } // WRONG func IntersectByLineEquasions(h1, h2 HailParam) (intersectionX, intersectionY float64, isIntersecting bool) { D := h1.a*h2.b - h1.b*h2.a if D == 0 { return 0, 0, false } Dx := h1.c*h2.b - h1.b*h2.c Dy := h1.a*h2.c - h1.b*h2.a intersectionX = float64(Dx) / float64(D) intersectionY = float64(Dy) / float64(D) isIntersecting = true return } // WRONG // 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 IntersectByTwoPointsAttempt2(h1, h2 HailParam) (intersectionX, intersectionY float64, isIntersecting bool) { x1 := h1.p0.x y1 := h1.p0.y x2 := h1.p1.x y2 := h1.p1.y x3 := h2.p0.x y3 := h2.p0.y x4 := h2.p1.x y4 := h2.p1.y divisor := (x1-x2)*(y3-y4)-(y1-y2)*(x3-x4) if divisor == 0 { return } intersectionX = float64((x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4)) / float64(divisor) intersectionY = float64((x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4)) / float64(divisor) isIntersecting = true return } func determinant(a, b Point) int { return a.x * b.y - a.y * b.x } func IntersectByTwoPointsAttempt3(h1, h2 HailParam) (intersectionX, intersectionY float64, isIntersecting bool) { xdiff := Point{ h1.p0.x - h1.p1.x, h2.p0.x - h2.p1.x, 0 } ydiff := Point { h1.p0.y - h1.p1.y, h2.p0.y - h2.p1.y , 0 } div := determinant(xdiff, ydiff) if div == 0 { return } d := Point { determinant(h1.p0, h1.p1), determinant(h2.p0, h2.p1), 0} x := float64(determinant(d, xdiff)) / float64(div) y := float64(determinant(d, ydiff)) / float64(div) return x, y, true } func IntersectBySlopeAndShift(h1, h2 HailParam) (intersectionX, intersectionY float64, isIntersecting bool) { if h1.slope == h2.slope { return } // y = slope * x + shift // slope1 * x + shift1 = slope2 * x + shift2 // x = ( shift2 - shift1 ) / (slope1 - slope2) x := (h2.shift - h1.shift) / (h1.slope - h2.slope) y := h1.slope * x + h1.shift 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 return xPositiveSteps } // 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]) countP1AfterMillis := 1 h.p1.x = h.p0.x + countP1AfterMillis * h.Dx h.p1.y = h.p0.y + countP1AfterMillis * h.Dy h.p1.Z = h.p0.Z + countP1AfterMillis * h.Dz h.a = h.p0.y - h.p1.y h.b = h.p1.x - h.p0.x h.c = -(h.p0.x*h.p1.y - h.p1.x*h.p0.y) h.slope = float64(h.Dy) / float64(h.Dx) // y = slope * x + shift // shift = y - slope * x // for some point h.shift = float64(h.p0.y) - h.slope * float64(h.p0.x) 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 }