diff --git a/day24/hailMary.go b/day24/hailMary.go new file mode 100644 index 0000000..5689d20 --- /dev/null +++ b/day24/hailMary.go @@ -0,0 +1,73 @@ +package day24 + +import ( + "fmt" +) + +// most inner loop +// assumint stone hits h1 at t1, and h2 at t2 +// return the line. so 'HailParam' for my stone trajectory +func AssumeHails(h1, h2 HailParam, t1, t2 int) (stoneTrajectory HailParam, isInt bool) { + Dx, isXInt := AssumedDelta(h1.p0.x, h2.p0.x, h1.Dx, h2.Dx, t1, t2) + Dy, isYInt := AssumedDelta(h1.p0.y, h2.p0.y, h1.Dy, h2.Dy, t1, t2) + Dz, isZInt := AssumedDelta(h1.p0.z, h2.p0.z, h1.Dz, h2.Dz, t1, t2) + + isInt = isXInt && isYInt && isZInt + + x := AssumedStartFromDelta(h1.p0.x, h1.Dx, t1, Dx) + y := AssumedStartFromDelta(h1.p0.y, h1.Dy, t1, Dy) + z := AssumedStartFromDelta(h1.p0.z, h1.Dz, t1, Dz) + + stoneTrajectoryLine := fmt.Sprintf("%d, %d, %d @ %d, %d, %d", x, y, z, Dx, Dy, Dz) + stoneTrajectory = ReadHailLine(stoneTrajectoryLine) + + return +} + +func HailMaryLoop(hails []HailParam) { + // for t1, t2 from [1, 100] + // try to fit stoneTrajectory on every pair of hails. + // and hope for integer fit + for t1 := 1; t1 <= 100; t1++ { + for t2 := t1+1 ; t2 <= 100; t2++ { + for i, hail := range hails { + innerHail: + for j, otherHail := range hails { + if i == j { + continue innerHail + } + _, isInt := AssumeHails(hail, otherHail, t1, t2) + if !isInt { + continue innerHail // TODO first hope to loose + } + // if isInt { + // log.Printf("hail mary int fit between %s (%d) and %s (%d)", + // hail.SomeString(), t1, otherHail.SomeString(), t2) + // } + } + } + } + } +} + +// TODO check for inner loop : when get assumed stoneTrajectory +// for all hail params, check that they intercept +// func CheckAssumedTrajectory(assumedStone HailParam, hails []HailParam) bool { +// for _, hail := range hails { +// // i guess i could try to do what? +// // assume oh, no. there can be t whatever +// } +// } + +func AssumedDelta(c1, c2 int, Dc1, Dc2 int, t1, t2 int) (delta int, isInt bool) { + divisor := t1 - t2 + divisible := c1 - c2 + (t1 * Dc1) - (t2 * Dc2) + + isInt = divisible % divisor == 0 + delta = divisible / divisor + return +} + +func AssumedStartFromDelta(c1 int, Dc1 int, t1, Dc int) (c int) { + return c1 + t1 * Dc1 - t1 * Dc +} diff --git a/day24/hailMary_test.go b/day24/hailMary_test.go new file mode 100644 index 0000000..1fc1557 --- /dev/null +++ b/day24/hailMary_test.go @@ -0,0 +1,9 @@ +package day24 + +import "testing" + +func TestHailMaryOnExamle(t *testing.T) { + filename := "input" + hails := ReadHailFile(filename) + HailMaryLoop(hails) +} diff --git a/day24/lines.go b/day24/lines.go index 4125797..8c2b43d 100644 --- a/day24/lines.go +++ b/day24/lines.go @@ -15,7 +15,7 @@ const ( ) type Point struct { - x, y, Z int + x, y, z int } type HailParam struct { @@ -28,6 +28,39 @@ type HailParam struct { slope, shift float64 } +func (h *HailParam) SomeString() string { + return h.line +} + +func (h *HailParam) GetCoord(name string) (result int) { + switch name { + case "x": + result = h.p0.x + case "y": + result = h.p0.y + case "z": + result = h.p0.z + default: + panic("unknown param") + } + + return +} + +func (h *HailParam) GetSpeedOf(name string) (result int) { + switch name { + case "x": + result = h.Dx + case "y": + result = h.Dy + case "z": + result = h.Dz + default: + panic("unknown param") + } + + return +} func CheckPairwiseIntersections(hails []HailParam) (totalIntersections int) { for i, hail := range hails { @@ -44,10 +77,7 @@ func CheckPairwiseIntersections(hails []HailParam) (totalIntersections int) { 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) @@ -76,79 +106,12 @@ func CheckTaskIntersection(h1, h2 HailParam) (doIntersect bool) { 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 +func IntersectInTheeDimentions(h1, h2 HailParam) (interX, interY, interZ float64, + interT float64, isIntersecting bool) { + 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 @@ -156,9 +119,9 @@ func IntersectBySlopeAndShift(h1, h2 HailParam) (intersectionX, intersectionY fl // 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 + y := h1.slope*x + h1.shift return x, y, true } @@ -166,7 +129,7 @@ func IntersectBySlopeAndShift(h1, h2 HailParam) (intersectionX, intersectionY fl 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 + zPositiveSteps := (p.z-h.p0.z)*h.Dz >= 0 return xPositiveSteps && yPositiveSteps && zPositiveSteps } func (h HailParam) FloatPointInFuture(x, y float64) bool { @@ -185,16 +148,16 @@ func ReadHailLine(line string) (h HailParam) { h.p0.x = AtoIOrPanic(fields[0]) h.p0.y = AtoIOrPanic(fields[1]) - h.p0.Z = AtoIOrPanic(fields[2]) + 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.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 @@ -203,8 +166,8 @@ func ReadHailLine(line string) (h HailParam) { 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) - + h.shift = float64(h.p0.y) - h.slope*float64(h.p0.x) + return } diff --git a/day24/lines_test.go b/day24/lines_test.go index c957e1d..a1ab6b7 100644 --- a/day24/lines_test.go +++ b/day24/lines_test.go @@ -55,7 +55,7 @@ func TestIntersectExampleOne(t *testing.T) { hA := ReadHailLine("19, 13, 30 @ -2, 1, -2") hB := ReadHailLine("18, 19, 22 @ -1, -1, -2") - x, y, check := IntersectByTwoPoints(hA, hB) + x, y, check := IntersectBySlopeAndShift(hA, hB) if !check { panic("should intersect") } @@ -68,7 +68,7 @@ func TestIntersectExampleTwo(t *testing.T) { hA := ReadHailLine("18, 19, 22 @ -1, -1, -2") hB := ReadHailLine("20, 25, 34 @ -2, -2, -4") - x, y, check := IntersectByTwoPoints(hA, hB) + x, y, check := IntersectBySlopeAndShift(hA, hB) if check { panic("should not intersect") } @@ -78,6 +78,9 @@ func TestIntersectExampleTwo(t *testing.T) { func TestExamplePairwiseChecks(t *testing.T) { filename := "example" hails := ReadHailFile(filename) + for _, hail := range hails { + t.Log(hail) + } intersections := CheckPairwiseIntersections(hails) t.Log("counted intersections ", intersections) diff --git a/day24/notes.org b/day24/notes.org index 37037a4..c9ab37d 100644 --- a/day24/notes.org +++ b/day24/notes.org @@ -20,16 +20,86 @@ 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 +** 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 +** 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 -* and i got wrong result +** and i got wrong result day24 result: 8406 -* another formula gives +** another formula gives day24 result: 8406 -* another formula +** another formula 12938 +* +* ok, part 2. +what if. +i start checking t = 0, 1, etc. +for each t, i need two points of the two hail lines. + +it would constitute the trajectory. +then condition for the solution that all other hail lines will intersect it at some t. +so check for intersection (maybe not necessarily in the field?) + +go though lines, if any fail to intersect - continue with t + +if all intersect, find where the rock has to be in time 0 + +oh. no. +it's not just intersect. it's that the movement of the rock with t would be there at correct time? yuck? + +would there really be more than i line that intersects all of the hail lines? + +i'll just need to also figure out t=0 from other coords. + +i don't like this at all. + +And intersections have to be over (X, Y, Z) +** so 'hail mary' approach would be +scan first 1k nanoseconds. so already 1M calculations +( this is first part of desperation, that at least 2 hails will intercept in first 1k ticks ) + +for collision 1, assume HailA is on path. +then iterate for all other assumint they are intercepted on t 2 etc ? + +no. the intersections could be on non-integer times? +( this would be second part of the 'hail mary' ) + +from that i should be able to construct the 'trajectory' line. +and then check with all other points - do the intersect? +( and check of intersection in future would be nice ) + +then if line confirmed, will need to calc for t = 0, t = 1, and get speeds +*** not hoping for all integer intersections +or what if i will hope for that? +let's try? +* ok, what if i could do system of equasions? + #+begin_src + yuck_test.go:12: + x + Dx * t0 == 19 + -2 * t0 + y + Dy * t0 == 13 + 1 * t0 + z + Dz * t0 == 19 + -2 * t0 + x + Dx * t1 == 18 + -1 * t1 + y + Dy * t1 == 19 + -1 * t1 + z + Dz * t1 == 18 + -2 * t1 + x + Dx * t2 == 20 + -2 * t2 + y + Dy * t2 == 25 + -2 * t2 + z + Dz * t2 == 20 + -4 * t2 + solve for x, y, z, Dx, Dy, Dz, t1, t2, t3. ti > 0 + #+end_src + + #+begin_src + yuck_test.go:18: + x + Dx * t0 == 147847636573416 + 185 * t0 + y + Dy * t0 == 190826994408605 + 49 * t0 + z + Dz * t0 == 147847636573416 + 219 * t0 + x + Dx * t1 == 287509258905812 + -26 * t1 + y + Dy * t1 == 207449079739538 + 31 * t1 + z + Dz * t1 == 287509258905812 + 8 * t1 + x + Dx * t2 == 390970075767404 + -147 * t2 + y + Dy * t2 == 535711685410735 + -453 * t2 + z + Dz * t2 == 390970075767404 + -149 * t2 + solve for x, y, z, Dx, Dy, Dz, t1, t2, t3. ti > 0 + #+end_src diff --git a/day24/pythonZ3/example.py b/day24/pythonZ3/example.py new file mode 100644 index 0000000..4bffca9 --- /dev/null +++ b/day24/pythonZ3/example.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 + +from z3 import * + +s = Solver() + +x = Real('x') +Dx = Real('Dx') +y = Real('y') +Dy = Real('Dy') +z = Real('z') +Dz = Real('Dz') +t0 = Real('t0') +eqT0 = t0 >= 0 +eq0x = x + Dx * t0 == (-2 * t0) + 19 +eq0y = y + Dy * t0 == (1 * t0) + 13 +eq0z = z + Dz * t0 == (-2 * t0) + 30 +t1 = Real('t1') +eqT1 = t1 >= 0 +eq1x = x + Dx * t1 == (-1 * t1) + 18 +eq1y = y + Dy * t1 == (-1 * t1) + 19 +eq1z = z + Dz * t1 == (-2 * t1) + 22 +t2 = Real('t2') +eqT2 = t2 >= 0 +eq2x = x + Dx * t2 == (-2 * t2) + 20 +eq2y = y + Dy * t2 == (-2 * t2) + 25 +eq2z = z + Dz * t2 == (-4 * t2) + 34 +#solve for x, y, z, Dx, Dy, Dz, t1, t2, t3. + + +s.add(eqT0, + eq0x, + eq0y, + eq0z, + eqT1, + eq1x, + eq1y, + eq1z, + eqT2, + eq2x, + eq2y, +eq2z) + +if s.check() == sat: + print("Solution:", s.model()) +else: + print("No solution found") diff --git a/day24/pythonZ3/practice.py b/day24/pythonZ3/practice.py new file mode 100644 index 0000000..1fe9026 --- /dev/null +++ b/day24/pythonZ3/practice.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 + +from z3 import * + +x = Real('x') +y = Real('y') + +eq1 = x + y == 5 +eq2 = x - y == 3 + +s = Solver() +s.add(eq1, eq2) + +if s.check() == sat: + print("Solution:", s.model()) +else: + print("No solution found") diff --git a/day24/yuck.go b/day24/yuck.go new file mode 100644 index 0000000..15dcd9b --- /dev/null +++ b/day24/yuck.go @@ -0,0 +1,78 @@ +package day24 + +import ( + "fmt" +) + +func SystemsWithSymbols() (result string) { + result += "\n" + coords := []string{"x", "y", "z"} + for i := 0; i < 3; i++ { + for _, coord := range coords { + result += fmt.Sprintf("%s + D%s * t%d == %s%d + D%s%d * t%d\n", + coord, coord, i, coord, i, coord, i, i) + } + } + + result += "solve for x, y, z, Dx, Dy, Dz, t1, t2, t3. ti > 0" + + return +} + +func SystemFromThreeHailstones(hails []HailParam) (result string) { + result += "\n" + coords := []string{"x", "y", "z"} + for i := 0; i < 3; i++ { + result += fmt.Sprintf("t%d >= 0\n", i) + hailIter := hails[i] + for _, coord := range coords { + result += fmt.Sprintf("%s + D%s * t%d == %d + %d * t%d\n", + coord, coord, i, + hailIter.GetCoord(coord), hailIter.GetSpeedOf(coord), i) + } + } + + result += "solve for x, y, z, Dx, Dy, Dz, t1, t2, t3." + return +} + +func SystemFromThreeHailstonesToTheLeft(hails []HailParam) (result string) { + result += "\n" + coords := []string{"x", "y", "z"} + for i := 0; i < 3; i++ { + result += fmt.Sprintf("t%d >= 0\n", i) + hailIter := hails[i] + for _, coord := range coords { + result += fmt.Sprintf("%s + D%s * t%d - (%d * t%d) == %d \n", + coord, coord, i, + hailIter.GetSpeedOf(coord), i, hailIter.GetCoord(coord)) + } + } + + result += "solve for x, y, z, Dx, Dy, Dz, t1, t2, t3." + return +} + +func SystemAsPythonInit(hails []HailParam) (result string) { + result += "\n" + coords := []string{"x", "y", "z"} + for _, coord := range coords { + result += fmt.Sprintf("%s = Real('%s')\n", coord, coord) + result += fmt.Sprintf("D%s = Real('D%s')\n", coord, coord) + } + + for i := 0; i < 3; i++ { + result += fmt.Sprintf("t%d = Real('t%d')\n", i, i) + result += fmt.Sprintf("eqT%d = t%d >= 0\n", i, i) + hailIter := hails[i] + for _, coord := range coords { + result += fmt.Sprintf("eq%d%s = %s + D%s * t%d == (%d * t%d) + %d \n", + i, coord, + coord, coord, i, + hailIter.GetSpeedOf(coord), i, hailIter.GetCoord(coord)) + } + } + + result += "//solve for x, y, z, Dx, Dy, Dz, t1, t2, t3." + return +} diff --git a/day24/yuck_test.go b/day24/yuck_test.go new file mode 100644 index 0000000..54e4949 --- /dev/null +++ b/day24/yuck_test.go @@ -0,0 +1,19 @@ +package day24 + +import "testing" + +func TestPrintJustSymbol(t *testing.T) { + t.Log(SystemsWithSymbols()) +} + +func TestPrintSystemExample(t *testing.T) { + filename := "example" + hails := ReadHailFile(filename) + t.Log(SystemAsPythonInit(hails)) +} + +func TestPrintSystemInput(t *testing.T) { + filename := "input" + hails := ReadHailFile(filename) + t.Log(SystemAsPythonInit(hails)) +} diff --git a/flake.lock b/flake.lock index aa98498..80c5341 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1694529238, - "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", "owner": "numtide", "repo": "flake-utils", - "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", "type": "github" }, "original": { @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1701253981, - "narHash": "sha256-ztaDIyZ7HrTAfEEUt9AtTDNoCYxUdSd6NrRHaYOIxtk=", + "lastModified": 1703255338, + "narHash": "sha256-Z6wfYJQKmDN9xciTwU3cOiOk+NElxdZwy/FiHctCzjU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e92039b55bcd58469325ded85d4f58dd5a4eaf58", + "rev": "6df37dc6a77654682fe9f071c62b4242b5342e04", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 8069776..943ed47 100644 --- a/flake.nix +++ b/flake.nix @@ -14,6 +14,9 @@ pkgs.wgo pkgs.semgrep pkgs.gopls + + pkgs.python3 + pkgs.python311Packages.z3-solver ]; shellHook = '' export GOPATH=$PWD/.go