Compare commits

..

10 Commits

Author SHA1 Message Date
efim 1f83bca4e6 day24: input system for python z3 2023-12-24 11:50:56 +00:00
efim 5b03b8f156 day24: adding python z3, example 2023-12-24 11:47:22 +00:00
efim bea82cb548 day24: why slope&shift work, while points dont? 2023-12-24 09:01:27 +00:00
efim d749979aae day24, already bad 2023-12-24 08:15:18 +00:00
efim b6a56554af day24, example 2023-12-24 07:42:04 +00:00
efim 2f6120fbd8 day23, part2 2023-12-23 15:56:25 +00:00
efim 0c31596018 day23, still bad 2023-12-23 12:54:55 +00:00
efim 28cf35e0e4 day23, example second 2023-12-23 12:00:48 +00:00
efim 7ebb6dee2c day23: started hardcode of part2, way too slow 2023-12-23 10:19:25 +00:00
efim 44de1377ca day23, part1 2023-12-23 09:16:04 +00:00
28 changed files with 1773 additions and 9 deletions

26
day23/aLongWalk.go Normal file
View File

@ -0,0 +1,26 @@
package day23
import (
"fmt"
)
// length of longest scenic route
func Run() int {
fmt.Println("day 23")
max := 0
filename := "day23/input"
field := ReadField(filename)
fmt.Println(field.SparseString())
// finalPaths := RunDFSTingy(field)
// // log.Println(finalPaths)
// for _, path := range finalPaths {
// if path.Visited.Cardinality() > max {
// log.Println("one path len is ", path.Visited.Cardinality())
// max = path.Visited.Cardinality()
// }
// }
return max
}

23
day23/example Normal file
View File

@ -0,0 +1,23 @@
#.#####################
#.......#########...###
#######.#########.#.###
###.....#.>.>.###.#.###
###v#####.#v#.###.#.###
###.>...#.#.#.....#...#
###v###.#.#.#########.#
###...#.#.#.......#...#
#####.#.#.#######.#.###
#.....#.#.#.......#...#
#.#####.#.#.#########v#
#.#...#...#...###...>.#
#.#.#v#######v###.###v#
#...#.>.#...>.>.#.###.#
#####v#.#.###v#.#.###.#
#.....#...#...#.#.#...#
#.#########.###.#.#.###
#...###...#...#...#.###
###.###.#.###v#####v###
#...#...#.#.>.>.#.>.###
#.###.###.#.###.#.#v###
#.....###...###...#...#
#####################.#

10
day23/example2 Normal file
View File

@ -0,0 +1,10 @@
#.#####################
#.#####################
#.##............#######
#.##.##########.#######
#....##########.#######
####..#########.#######
#####...........#######
###############.#######
###############.#######
###############.#######

175
day23/field.go Normal file
View File

@ -0,0 +1,175 @@
package day23
import (
"fmt"
"os"
"strings"
)
type Coord struct {
Row, Col int
}
type CellType rune
const (
Path CellType = '.'
Tree CellType = '#'
SlideDown CellType = 'v'
SlideUp CellType = '^'
SlideLeft CellType = '<'
SlideRight CellType = '>'
)
type Field struct {
MaxRow, MaxCol int
Cells map[Coord]CellType
StartCol, EndCol int
}
func (f *Field) EndCoord() Coord {
return Coord{Row: f.MaxRow, Col: f.EndCol}
}
func (f *Field) NeighborsPart2(c Coord) (neighbors []Coord) {
symb, exists := f.Cells[c]
if !exists {
panic(fmt.Sprintf("coord %+v not found in field", c))
}
var coords []Coord
switch symb {
case Tree:
panic(fmt.Sprintf("attempting to get neighbors of a tree at %+v", c))
default:
coords = []Coord{
{Row: c.Row + 1, Col: c.Col},
{Row: c.Row - 1, Col: c.Col},
{Row: c.Row, Col: c.Col + 1},
{Row: c.Row, Col: c.Col - 1},
}
}
for _, coord := range coords {
neighborSymb, found := f.Cells[coord]
if !found || neighborSymb == Tree {
continue
}
neighbors = append(neighbors, coord)
}
return
}
func (f *Field) Neighbors(c Coord) (neighbors []Coord) {
symb, exists := f.Cells[c]
if !exists {
panic(fmt.Sprintf("coord %+v not found in field", c))
}
var coords []Coord
switch symb {
case Path:
coords = []Coord{
{Row: c.Row + 1, Col: c.Col},
{Row: c.Row - 1, Col: c.Col},
{Row: c.Row, Col: c.Col + 1},
{Row: c.Row, Col: c.Col - 1},
}
case Tree:
panic(fmt.Sprintf("attempting to get neighbors of a tree at %+v", c))
case SlideDown:
coords = []Coord{{Row: c.Row + 1, Col: c.Col}}
case SlideUp:
coords = []Coord{{Row: c.Row - 1, Col: c.Col}}
case SlideLeft:
coords = []Coord{{Row: c.Row, Col: c.Col - 1}}
case SlideRight:
coords = []Coord{{Row: c.Row, Col: c.Col + 1}}
}
for _, coord := range coords {
neighborSymb, found := f.Cells[coord]
if !found || neighborSymb == Tree {
continue
}
neighbors = append(neighbors, coord)
}
return
}
func (f *Field) String() (result string) {
result += "\n"
for row := 0; row <= f.MaxRow; row++ {
for col := 0; col <= f.MaxCol; col++ {
if row == 0 && col == f.StartCol {
result += "S"
continue
}
if row == f.MaxRow && col == f.EndCol {
result += "E"
continue
}
symb := f.Cells[Coord{Row: row, Col: col}]
result += string(symb)
}
result += "\n"
}
return
}
func (f *Field) SparseString() (result string) {
result += "\n"
for row := 0; row <= f.MaxRow; row++ {
for col := 0; col <= f.MaxCol; col++ {
if row == 0 && col == f.StartCol {
result += "S"
continue
}
if row == f.MaxRow && col == f.EndCol {
result += "E"
continue
}
symb := f.Cells[Coord{Row: row, Col: col}]
if symb != Tree {
neighbors := f.NeighborsPart2(Coord{Row: row, Col: col})
if len(neighbors) > 2 {
result += "o"
} else {
result += "."
}
} else {
result += " "
}
}
result += "\n"
}
return
}
func ReadField(filename string) (result Field) {
bytes, err := os.ReadFile(filename)
if err != nil {
panic(err)
}
lines := strings.Split(strings.TrimSpace(string(bytes)), "\n")
result.MaxRow = len(lines) - 1
result.MaxCol = len(lines[0]) - 1
rows := make(map[Coord]CellType)
for rowNum, row := range lines {
for colNum, symb := range row {
rows[Coord{Row: rowNum, Col: colNum}] = CellType(symb)
if rowNum == 0 && symb == rune(Path) {
result.StartCol = colNum
}
if rowNum == result.MaxRow && symb == rune(Path) {
result.EndCol = colNum
}
}
}
result.Cells = rows
return
}

31
day23/field_test.go Normal file
View File

@ -0,0 +1,31 @@
package day23
import "testing"
func TestReadField(t *testing.T) {
filename := "example"
field := ReadField(filename)
t.Log(field.String())
}
func TestStartNeighbors(t *testing.T) {
filename := "example"
field := ReadField(filename)
startNeighbors := field.Neighbors(Coord{Row: 0, Col: field.StartCol})
t.Log(startNeighbors)
}
// 5,3
func TestForkNeighbors(t *testing.T) {
filename := "example"
field := ReadField(filename)
startNeighbors := field.Neighbors(Coord{Row: 5, Col: 3})
t.Log(startNeighbors)
}
func TestSlideNeighbors(t *testing.T) {
filename := "example"
field := ReadField(filename)
startNeighbors := field.Neighbors(Coord{Row: 6, Col: 3})
t.Log(startNeighbors)
}

248
day23/graph.go Normal file
View File

@ -0,0 +1,248 @@
package day23
import (
"fmt"
"log"
"slices"
mapset "github.com/deckarep/golang-set/v2"
)
type Node struct {
index int
c Coord
name string
}
func (n Node)Name() string {
var r string
if n.index < 25 {
num := 'A' + n.index
r = string(rune(num))
} else {
num := 'a' + n.index - 25
r = string(rune(num))
}
return r
}
type Graph struct {
nodes map[Coord]Node
nodesByIndex []Node
edges [][]int // from, to, length. excluding from, including to
}
func MaxDist(from, to Node) (result int) {
return
}
func PrintFieldWithGraph(g Graph, f Field) (result string) {
result += "\n"
for row := 0; row <= f.MaxRow; row++ {
for col := 0; col <= f.MaxCol; col++ {
symb := f.Cells[Coord{Row: row, Col: col}]
if symb != Tree {
coord := Coord{Row: row, Col: col}
node, exists := g.nodes[coord]
if exists {
result += fmt.Sprint(node.Name())
} else {
result += "."
}
} else {
result += " "
}
}
result += "\n"
}
return
}
func CreateGraph(f Field) (g Graph) {
startCoord := Coord{Row: 0, Col: f.StartCol}
// directly below start
initialPath := PathEnd{
end: Coord{Row: 1, Col: f.StartCol}, visited: mapset.NewSet[Coord](),
}
g = Graph{
nodes: map[Coord]Node{
startCoord: Node{
index: 0,
c: startCoord,
name: "A",
},
},
}
const presumedNodeCount = 36
g.edges = make([][]int, presumedNodeCount)
for i := 0; i < presumedNodeCount; i++ {
g.edges[i] = make([]int, presumedNodeCount)
}
recursiveGraphStep(f, initialPath, &g, startCoord, 1, mapset.NewSet[Coord]())
g.edges[0][0] = 0
g.nodesByIndex = make([]Node, len(g.nodes))
for _, node := range g.nodes {
g.nodesByIndex[node.index] = node
}
return
}
func (g *Graph)Neighbors(node Node) (nodes []Node) {
index := node.index
for toIndex, len := range g.edges[index] {
if len > 0 {
nodes = append(nodes, g.nodesByIndex[toIndex])
}
}
return
}
var maxSoFar int = -1
func CheckMaxSoFar(maybe int) {
if maybe > maxSoFar {
maxSoFar = maybe
}
}
func (g *Graph) DFSLenOnGraph(atNode Node, visited mapset.Set[int],
toNode Node, lenSoFar int) int {
if atNode == toNode {
CheckMaxSoFar(lenSoFar)
return lenSoFar
}
log.Printf("at %+v to %+v cur dist is %d.\t\t|max so far %d| \n", atNode, toNode, lenSoFar, maxSoFar)
neighbors := g.Neighbors(atNode)
toVisit := slices.DeleteFunc(neighbors, func(n Node) bool {
return visited.Contains(n.index)
})
if len(toVisit) == 0 {
return -1
}
max := -1
for _, nextNode := range toVisit {
newVisited := visited.Clone()
newVisited.Add(atNode.index)
dist := g.edges[atNode.index][nextNode.index]
maxFromNext := g.DFSLenOnGraph(nextNode, newVisited, toNode, lenSoFar + dist)
if maxFromNext > max {
max = maxFromNext
}
}
return max
}
// run dfs, remembering from which remembers from which node we go, which path already traversed
func recursiveGraphStep(f Field, p PathEnd, g *Graph, goingFrom Coord, goingLen int, visitedPathPoints mapset.Set[Coord]) {
// log.Printf("entering coord %+v. from %+v with len %d\n", p.end, goingFrom, goingLen)
// if visitedPathPoints.Contains(p.end) {
// return
// }
neighbors := f.NeighborsPart2(p.end)
isCrossRoad := len(neighbors) > 2
if isCrossRoad {
log.Println("this should be crossroad ", p.end)
}
isStart := p.end == Coord{Row: 0, Col: f.StartCol}
isEnd := p.end == f.EndCoord()
if isEnd {
log.Println("this should be end ", p.end)
}
isNode := isCrossRoad || isStart || isEnd
continuedPaths := ExtendPath(p, f)
if !isNode {
// just recurse into next paths, from same node, with increased len
visitedPathPoints.Add(p.end)
for _, nextStep := range continuedPaths {
recursiveGraphStep(f, nextStep, g, goingFrom, goingLen+1, visitedPathPoints)
}
} else {
node, known := g.nodes[p.end]
// check if known, if not known - create
if !known {
node = Node{
c: p.end,
index: len(g.nodes),
}
node.name = node.Name()
g.nodes[p.end] = node
log.Printf("creating node %s %+v\n", node.Name(), node)
}
from := g.nodes[goingFrom]
log.Printf("from %s to %s\n", from.Name(), node.Name())
// and add vertices to currently traversed
if g.edges[node.index][from.index] == 0 {
g.edges[node.index][from.index] = goingLen
g.edges[from.index][node.index] = goingLen
} else {
knownEdge := g.edges[node.index][from.index]
if goingLen > knownEdge {
g.edges[node.index][from.index] = goingLen
g.edges[from.index][node.index] = goingLen
}
}
// NOTE ah, it's possible to have two edges between i and j
// but, i only need the longest one
// log.Printf("adding edges between %d & %d of len %d\n", node.index, from.index, goingLen)
// continue with new 'from' and len of 1
if !known {
for _, nextStep := range continuedPaths {
log.Printf("from %s should recurse to %+v", node.Name(), nextStep)
recursiveGraphStep(f, nextStep, g, p.end, 1, visitedPathPoints)
}
}
}
return
}
func GraphToMermaid(g Graph) (result string) {
result += "\nflowchart LR\n"
lines := mapset.NewSet[string]()
for _, node := range g.nodes {
for to, len := range g.edges[node.index] {
var toNode Node
for _, other := range g.nodes {
if other.index == to {
toNode = other
}
}
if len > 0 {
var fromName, toName string
if node.index < toNode.index {
fromName = node.Name()
toName = toNode.Name()
} else {
fromName = toNode.Name()
toName = node.Name()
}
line := fmt.Sprintf("\t%s---|length %d|%s\n", fromName, len, toName)
lines.Add(line)
}
}
// result += fmt.Sprintf("%s--|%d|%s\n", a ...any)
}
for line := range lines.Iter() {
result += line
}
return
}

101
day23/graph_test.go Normal file
View File

@ -0,0 +1,101 @@
package day23
import (
"fmt"
"os"
"testing"
mapset "github.com/deckarep/golang-set/v2"
)
func TestGraphCreate(t *testing.T) {
filename := "example2"
field := ReadField(filename)
fmt.Println(field.SparseString())
graph := CreateGraph(field)
t.Log(graph)
}
func TestPrintGraph(t *testing.T) {
filename := "example2"
field := ReadField(filename)
fmt.Println(field.SparseString())
graph := CreateGraph(field)
t.Log(PrintFieldWithGraph(graph, field))
t.Logf(">>>\n %+v\n", graph)
}
func TestPrintGraphInput(t *testing.T) {
filename := "input"
field := ReadField(filename)
fmt.Println(field.SparseString())
graph := CreateGraph(field)
t.Log(PrintFieldWithGraph(graph, field))
t.Logf(">>>\n %+v\n", graph)
}
func TestPrintMermaidGraphInput(t *testing.T) {
filename := "input"
field := ReadField(filename)
fmt.Println(field.SparseString())
graph := CreateGraph(field)
mmdContent := GraphToMermaid(graph)
t.Log(mmdContent)
fileBorder, err := os.Create(filename + ".mmd")
if err != nil {
panic(err)
}
defer func() {
if err := fileBorder.Close(); err != nil {
panic(err)
}
}()
fileBorder.WriteString(mmdContent)
}
func TestGraphMaxBetweenExample(t *testing.T) {
filename := "example"
field := ReadField(filename)
graph := CreateGraph(field)
t.Log(PrintFieldWithGraph(graph, field))
from := graph.nodes[Coord{Row: 0, Col: field.StartCol}]
to := graph.nodes[field.EndCoord()]
dist := graph.DFSLenOnGraph(from, mapset.NewSet[int](), to, 0)
t.Log(graph)
t.Logf("please dist %d", dist)
}
func TestGraphMaxBetweenInput(t *testing.T) {
filename := "input"
field := ReadField(filename)
graph := CreateGraph(field)
t.Log(PrintFieldWithGraph(graph, field))
from := graph.nodes[Coord{Row: 0, Col: field.StartCol}]
to := graph.nodes[field.EndCoord()]
dist := graph.DFSLenOnGraph(from, mapset.NewSet[int](), to, 0)
t.Log(graph)
t.Logf("please dist %d", dist)
}

62
day23/input.mmd Normal file
View File

@ -0,0 +1,62 @@
flowchart LR
M---|length 166|N
d---|length 62|h
H---|length 190|I
f---|length 136|h
j---|length 94|k
B---|length 152|L
I---|length 40|J
W---|length 56|X
E---|length 214|F
C---|length 60|K
V---|length 142|b
a---|length 110|b
I---|length 138|P
J---|length 184|K
Y---|length 146|a
c---|length 190|d
Q---|length 114|T
J---|length 240|O
C---|length 184|D
L---|length 172|M
Q---|length 140|R
Y---|length 464|k
O---|length 76|V
N---|length 102|O
K---|length 152|L
U---|length 80|c
V---|length 72|W
b---|length 202|j
A---|length 39|B
W---|length 236|a
P---|length 166|Q
e---|length 174|f
G---|length 186|R
T---|length 258|d
X---|length 142|Y
b---|length 128|c
F---|length 378|G
S---|length 108|T
N---|length 62|W
U---|length 110|V
a---|length 138|k
S---|length 234|e
d---|length 108|e
H---|length 166|Q
O---|length 158|P
M---|length 360|X
h---|length 184|i
B---|length 244|C
D---|length 96|J
D---|length 154|E
R---|length 118|S
E---|length 146|I
P---|length 128|U
T---|length 268|U
i---|length 198|j
G---|length 144|H
F---|length 102|H
f---|length 77|g
K---|length 266|N
c---|length 64|i

125
day23/notes.org Normal file
View File

@ -0,0 +1,125 @@
#+title: Notes
* ok, second part is long.
and here optimization of storing direction of enter into path, and it's length,
would that be helpful?
it might not, because based on visited some future longer path might not be available.
i don't know how to optimize.
i could maybe do same calculation in parallel, somehow
put not into queue, but into channel
* wait a second. previous answer was 2018
and now long checks result in me waiting for intermediate 1882.
let's add early cutoffs, if not end by 2018, then abandon
doubt that's implementable
* well. do i want to try parallel?
seems like false path, really
like there should be a better optimizaiton first
* maybe we could join detours into 'potential longest paths'
like if we traverse, and get to a point which was previously visited,
for evey path that went through that split path,
i could check whether i can take paths that went through this point, and switch their part with the detoured part.
* and maybe we could continue longest paths first?
like making pathsToFurther a heap by visited.Cordinality ?
oh, and then we'll find 'some path to end'
and additional paths will try to add their detour.
so, i guess when finding a path to end, i could save path to end for each point.
then if i reach the point, i could check if i can use some of the
and i guess if i do depth first then i'll always have all paths to end from a point if i return to it?
* this sounds like an idea.
with heap do depth first.
if it's first visit to a point, just go further
if i find the end, i'd want to mark all points on the path with path info
hm. recursive calls might make this easier.
because i'd want both 'prefixVisited' set and totalPathSet
due to depth first, we'll discover shortest path first.
and points will get mapped with this first (of potential multiple) path info to end.
now if on followup steps i get into the point with info on paths to end,
that should mean that i've already found all paths to end from that point, right?
now i need to check for the 'detour' which 'paths to end' are still possible with that detour added
by taking set of elements from this point, to end. and checking that intersection with detour elements is 0.
if there are like this - report finding a new path, and save to all elements of that path somehow.
and now on finding detours i wouldn't need to re-check path to end, that should save a lot of time
** so how to go about in coding this?
have shared map[Coord][]EndPathInfo
the DFS means i'm recursing into each child.
and taking the result of the call.
it should be info on path to end? or multiple paths to end.
which should be added to current node.
and then calling with start point will return paths to end from start, and i'll be able to take the by length
ok. but. if i'm entering the coord, and there are already paths to end.
then i need to presume that those are only possible paths to end from this point,
because all other paths should have been explored by now,
i for my 'detour' determine whether it is consistent with any of already found paths to end.
** NO. dfs doesn't mean i'll find shortest path first.
so if i'm in visited, it doesn't mean that stored is shorter and current is a detour.
but dfs should mean that all paths from this prefix have finished.
so, sure. there have to be all done?
** my example2 has fork on row 3 col 10
so 4,10 and 3,11 should be visited separately.
6,17 is where they join and the point which should have second entry
** allright, ugh. my new solution is memory hogging.
maybe i can draw the stuff and it will be several neat thingies
* maybe new approach?
make a graph. with vertices of Start, End and Crossroads.
yes.
let's create a graph representation.
** so, from A to AG
i think i can do this manually now
** distances are
39
244
184
154
214
378
144
190
40
184
152
172
166
102
158
166
140
118
108
268
110
72
56
142
146
110
128
190
108
174
77
1
** again?
no, let's write code.
** didn't count all the way
2023/12/23 15:55:55 at {index:30 c:{Row:125 Col:137} name:f} to {index:31 c:{Row:140 Col:139} name:g} cur dist is 3997. |max so far 6406|
signal: interrupt
FAIL sunshine.industries/aoc2023/day23 380.499s
tried more or less stable value, and interrupted

161
day23/paths.go Normal file
View File

@ -0,0 +1,161 @@
package day23
import (
"fmt"
"log"
mapset "github.com/deckarep/golang-set/v2"
)
type PathEnd struct {
end Coord
visited mapset.Set[Coord]
}
func (p PathEnd)Sring() string {
return fmt.Sprintf("PathEnd[at %+v, visited: %+v]", p.end, p.visited)
}
func ExtendPath(p PathEnd, f Field) (nextPaths []PathEnd) {
endPointNeighbors := f.NeighborsPart2(p.end)
for _, potentialNewEnd := range endPointNeighbors {
if !p.visited.Contains(potentialNewEnd) {
nextVisited := p.visited.Clone()
nextVisited.Add(p.end)
nextPaths = append(nextPaths, PathEnd{
end: potentialNewEnd,
visited: nextVisited,
})
}
}
return
}
// info on path from start to end
type PathInfo struct {
Visited mapset.Set[Coord]
}
func RunDFSTingy(f Field) []PathInfo {
initialPath := PathEnd{
end: Coord{Row: 0, Col: f.StartCol}, visited: mapset.NewSet[Coord](),
}
initialShared := make(map[Coord][]PathInfo)
return DFSScenicPaths(f, initialPath, initialShared)
}
var knownMax int = 0
func CheckAndPrintMax(maybeNewMax int) {
if maybeNewMax > knownMax {
log.Printf("\n\n>>>>found new max: %d\n", maybeNewMax)
knownMax = maybeNewMax
}
}
func DFSScenicPaths(f Field, curPath PathEnd,
sharedMem map[Coord][]PathInfo) (pathsFromTheStartToEnd []PathInfo) {
curCoord := curPath.end
if curCoord == (Coord{ Row: 6, Col: 15 }) {
log.Println(">>>>>>>>")
}
// log.Printf("entering %+v with mem %+v\n", curPath, sharedMem[curCoord])
if curCoord == f.EndCoord() {
pathsFromTheStartToEnd = append(pathsFromTheStartToEnd, PathInfo{curPath.visited.Clone()})
log.Printf("got to end. cur len is %d\n", curPath.visited.Cardinality())
CheckAndPrintMax(curPath.visited.Cardinality())
// i guess return only from current to end?
// and on non terminal first time, return copy with self added?
return
}
// now for non final point
knownPaths, visitedBefore := sharedMem[curCoord]
// NOTE but this only if we haven't visited this coord before!
if !visitedBefore {
nextSteps := ExtendPath(curPath, f)
suffixesFromCurToEnd := make([]PathInfo, 0)
for _, nextPath := range nextSteps {
pathsToEndThrough := DFSScenicPaths(f, nextPath, sharedMem)
// i guess here deduct the prefix.
for _, path := range pathsToEndThrough {
// will contain this and further
suffix := PathInfo{
Visited: path.Visited.Difference(curPath.visited).Clone(),
}
// log.Printf(">> from path \n%+v make suffix \n%+v\n\n", path, suffix)
suffixesFromCurToEnd = append(suffixesFromCurToEnd, suffix)
}
pathsFromTheStartToEnd = append(pathsFromTheStartToEnd, pathsToEndThrough...)
if len(pathsToEndThrough) != 0 {
// log.Printf("setting mem for %+v to %+v", curCoord, suffixesFromCurToEnd)
sharedMem[curCoord] = suffixesFromCurToEnd
}
}
return
} else {
// have visited this point before, due to dfs all possible paths to end should already be known
// so curPath.visited should contian a detour.
// need to figure out if this detour is compatible with any of the known paths to end
// from those create 'new' paths to end with that detour
// return those and add those to the shared mem
for _, knownPathToEnd := range knownPaths {
// those are all points through which this known path goes from current to end
// if our curPath
fromCurToEnd := knownPathToEnd.Visited
thisPrefix := curPath.visited
if thisPrefix.Intersect(fromCurToEnd).Cardinality() == 0 {
// then current prefix is compatible with this path.
fromCurPrefixToEnd := thisPrefix.Clone()
fromCurPrefixToEnd.Union(fromCurToEnd)
pathsFromTheStartToEnd = append(pathsFromTheStartToEnd, PathInfo{fromCurPrefixToEnd})
log.Printf("additional path to end of len %d\n", fromCurPrefixToEnd.Cardinality())
CheckAndPrintMax(fromCurPrefixToEnd.Cardinality())
}
}
log.Printf("having second visit into %+v.\n", curPath)
return
}
panic("should not be reachable")
}
// return paths that end on End
func RunAllScenicPaths(f Field) (result []PathEnd) {
pathsToFurther := []PathEnd{
{end: Coord{Row: 0, Col: f.StartCol}, visited: mapset.NewSet[Coord]()},
}
for len(pathsToFurther) > 0 {
curCheckedPath := pathsToFurther[0]
pathsToFurther = pathsToFurther[1:]
if curCheckedPath.end == f.EndCoord() {
result = append(result, curCheckedPath)
// log.Printf("found end path of len %d . %+v", curCheckedPath.visited.Cardinality(), curCheckedPath)
continue
}
nextSteps := ExtendPath(curCheckedPath, f)
// log.Printf("for %+v next steps %+v\n", curCheckedPath, pathsToFurther)
// log.Printf("remaining paths to check len is %d", len(pathsToFurther))
// log.Println(pathsToFurther)
if len(nextSteps) > 0 {
pathsToFurther = append(pathsToFurther, nextSteps...)
}
}
return
}

19
day23/paths_test.go Normal file
View File

@ -0,0 +1,19 @@
package day23
import "testing"
func TestRunAllPaths(t *testing.T) {
filename := "example"
field := ReadField(filename)
finalPaths := RunAllScenicPaths(field)
t.Log(finalPaths)
max := 0
for _, path := range finalPaths {
if path.visited.Cardinality() > max {
max = path.visited.Cardinality()
}
}
t.Logf("max path len is %d", max)
}

72
day23/willHelp.mmd Normal file
View File

@ -0,0 +1,72 @@
flowchart LR
L---|length 152|K
L---|length 172|M
U---|length 268|T
U---|length 110|V
W---|length 72|V
W---|length 56|X
a---|length 146|Y
a---|length 110|b
f---|length 174|e
f---|length 77|g
f---|length 136|h
H---|length 144|G
H---|length 190|I
T---|length 108|S
T---|length 268|U
M---|length 172|L
M---|length 166|N
F---|length 214|E
F---|length 378|G
I---|length 190|H
I---|length 40|J
A---|length 2|A
A---|length 39|B
Q---|length 166|P
Q---|length 140|R
Y---|length 142|X
Y---|length 146|a
d---|length 190|c
d---|length 108|e
e---|length 108|d
e---|length 174|f
h---|length 136|f
h---|length 184|i
J---|length 40|I
J---|length 184|K
N---|length 166|M
N---|length 102|O
X---|length 56|W
X---|length 142|Y
j---|length 198|i
j---|length 94|k
B---|length 39|A
B---|length 244|C
G---|length 378|F
G---|length 144|H
P---|length 158|O
P---|length 166|Q
D---|length 184|C
D---|length 154|E
E---|length 154|D
E---|length 214|F
K---|length 184|J
K---|length 152|L
O---|length 102|N
O---|length 158|P
R---|length 140|Q
R---|length 118|S
S---|length 118|R
S---|length 108|T
V---|length 110|U
V---|length 72|W
c---|length 128|b
c---|length 190|d
C---|length 244|B
C---|length 184|D
k---|length 94|j
i---|length 184|h
i---|length 198|j
g---|length 77|f
b---|length 110|a
b---|length 128|c

BIN
day23/willHelp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

5
day24/example Normal file
View File

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

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

196
day24/lines.go Normal file
View File

@ -0,0 +1,196 @@
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 (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 {
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)
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
}
func IntersectInTheeDimentions(h1, h2 HailParam) (interX, interY, interZ float64,
interT float64, isIntersecting bool) {
return
}
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
}

87
day24/lines_test.go Normal file
View File

@ -0,0 +1,87 @@
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 TestReadLineInput(t *testing.T) {
lines := `147847636573416, 190826994408605, 140130741291716 @ 185, 49, 219
287509258905812, 207449079739538, 280539021150559 @ -26, 31, 8
390970075767404, 535711685410735, 404166182422876 @ -147, -453, -149
306391780523937, 382508967958270, 264612201472049 @ -24, -274, 28
278063616684570, 510959526404728, 288141792965603 @ -18, -441, -6`
for _, line := range strings.Split(lines, "\n") {
hail := ReadHailLine(line)
t.Logf("%+v\n", 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 := IntersectBySlopeAndShift(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 := IntersectBySlopeAndShift(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)
for _, hail := range hails {
t.Log(hail)
}
intersections := CheckPairwiseIntersections(hails)
t.Log("counted intersections ", intersections)
}

View File

@ -0,0 +1,12 @@
package day24
import (
"fmt"
)
func Run() int {
fmt.Println("hello day 24, i'm getting tired")
filenae := "day24/input"
hails := ReadHailFile(filenae)
return CheckPairwiseIntersections(hails)
}

116
day24/notes.org Normal file
View File

@ -0,0 +1,116 @@
#+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
** and i got wrong result
day24 result: 8406
** another formula gives
day24 result: 8406
** 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
* got some solution
enefedov@LLF33A87M:~/Documents/personal/advent-of-code-2023$ python day24/pythonZ3/forInput.py
Solution: [t0 = 666003776903,
t2 = 779453185471,
t1 = 654152070134,
Dz = 18,
Dx = 47,
Dy = -360,
z = 273997500449219,
y = 463222539161932,
x = 239756157786030]

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,49 @@
#!/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 == (185 * t0) + 147847636573416
eq0y = y + Dy * t0 == (49 * t0) + 190826994408605
eq0z = z + Dz * t0 == (219 * t0) + 140130741291716
t1 = Real('t1')
eqT1 = t1 >= 0
eq1x = x + Dx * t1 == (-26 * t1) + 287509258905812
eq1y = y + Dy * t1 == (31 * t1) + 207449079739538
eq1z = z + Dz * t1 == (8 * t1) + 280539021150559
t2 = Real('t2')
eqT2 = t2 >= 0
eq2x = x + Dx * t2 == (-147 * t2) + 390970075767404
eq2y = y + Dy * t2 == (-453 * t2) + 535711685410735
eq2z = z + Dz * t2 == (-149 * t2) + 404166182422876
#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")
print(273997500449219 + 463222539161932 + 239756157786030)

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

View File

@ -4,15 +4,15 @@ import (
"log"
"time"
"sunshine.industries/aoc2023/day22"
"sunshine.industries/aoc2023/day24"
)
func main() {
startTime := time.Now()
log.Print("> starting run:")
result := day22.Run()
log.Printf("\n\nday22 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())