day24: adding python z3, example

This commit is contained in:
efim 2023-12-24 11:47:22 +00:00
parent bea82cb548
commit 5b03b8f156
11 changed files with 378 additions and 96 deletions

73
day24/hailMary.go Normal file
View File

@ -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
}

9
day24/hailMary_test.go Normal file
View File

@ -0,0 +1,9 @@
package day24
import "testing"
func TestHailMaryOnExamle(t *testing.T) {
filename := "input"
hails := ReadHailFile(filename)
HailMaryLoop(hails)
}

View File

@ -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
@ -158,7 +121,7 @@ func IntersectBySlopeAndShift(h1, h2 HailParam) (intersectionX, intersectionY fl
// 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,7 +166,7 @@ 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
}

View File

@ -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)

View File

@ -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

47
day24/pythonZ3/example.py Normal file
View File

@ -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")

View File

@ -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")

78
day24/yuck.go Normal file
View File

@ -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
}

19
day24/yuck_test.go Normal file
View File

@ -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))
}

View File

@ -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": {

View File

@ -14,6 +14,9 @@
pkgs.wgo
pkgs.semgrep
pkgs.gopls
pkgs.python3
pkgs.python311Packages.z3-solver
];
shellHook = ''
export GOPATH=$PWD/.go