9 Commits

Author SHA1 Message Date
xuu
a5ebbe25d0 chore: save hacker rank 2024-06-10 21:26:09 -06:00
xuu
50af2114d4 add ranger
Some checks failed
Go Bump / bump (push) Failing after 6s
Go Test / build (push) Failing after 21s
2024-04-05 16:27:02 -06:00
xuu
f8fa61672f chore: fixes
All checks were successful
Go Bump / bump (push) Successful in 7s
Go Test / build (push) Successful in 35s
2024-01-22 16:07:16 -07:00
xuu
951c2c298a chore(aos): add compress graph
Some checks failed
Go Bump / bump (push) Successful in 9s
Go Test / build (push) Failing after 24s
2024-01-12 12:09:44 -07:00
xuu
328a0f3eb3 chore(aoc): add FibHeap DecreaseKey
All checks were successful
Go Bump / bump (push) Successful in 8s
Go Test / build (push) Successful in 40s
2024-01-09 20:41:23 -07:00
xuu
7d7402f054 chore(day17): implement fibHeap for faster priority queue
All checks were successful
Go Test / build (pull_request) Successful in 37s
Go Bump / bump (push) Successful in 7s
Go Test / build (push) Successful in 39s
2024-01-09 13:53:30 -07:00
xuu
7585526634 chore(day17): simplify FindPath. A* is still slower :|
Some checks failed
Go Test / build (pull_request) Failing after 4m27s
2024-01-04 17:15:44 -07:00
xuu
924c8d74f3 chore(day17): disable heuristic. runs faster!? 2024-01-02 20:57:02 -07:00
xuu
22184ed9c7 chore(aoc): initial graph attempt 2024-01-02 17:02:12 -07:00
25 changed files with 1311 additions and 754 deletions

6
README.md.sig Normal file
View File

@@ -0,0 +1,6 @@
-----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgZ+OuJYdd3UiUbyBuO1RlsQR20a
Qm5mKneuMxRjGo3zkAAAAEZmlsZQAAAAAAAAAGc2hhNTEyAAAAUwAAAAtzc2gtZWQyNTUx
OQAAAED8T4C6WILXYZ1KxqDIlVhlrAEjr1Vc+tn8ypcVM3bN7iOexVvuUuvm90nr8eEwKU
acrdDxmq2S+oysQbK+pMUE
-----END SSH SIGNATURE-----

View File

@@ -9,66 +9,6 @@ import (
aoc "go.sour.is/advent-of-code"
)
func TestReverse(t *testing.T) {
is := is.New(t)
is.Equal(aoc.Reverse([]int{1, 2, 3, 4}), []int{4, 3, 2, 1})
}
func TestLCM(t *testing.T) {
is := is.New(t)
is.Equal(aoc.LCM([]int{}...), 0)
is.Equal(aoc.LCM(5), 5)
is.Equal(aoc.LCM(5, 3), 15)
is.Equal(aoc.LCM(5, 3, 2), 30)
}
func TestReadStringToInts(t *testing.T) {
is := is.New(t)
is.Equal(aoc.ReadStringToInts([]string{"1", "2", "3"}), []int{1, 2, 3})
}
func TestRepeat(t *testing.T) {
is := is.New(t)
is.Equal(aoc.Repeat(5, 3), []int{5, 5, 5})
}
func TestPower2(t *testing.T) {
is := is.New(t)
is.Equal(aoc.Power2(0), 1)
is.Equal(aoc.Power2(1), 2)
is.Equal(aoc.Power2(2), 4)
}
func TestABS(t *testing.T) {
is := is.New(t)
is.Equal(aoc.ABS(1), 1)
is.Equal(aoc.ABS(0), 0)
is.Equal(aoc.ABS(-1), 1)
}
func TestTranspose(t *testing.T) {
is := is.New(t)
is.Equal(
aoc.Transpose(
[][]int{
{1, 1},
{0, 0},
{1, 1},
},
),
[][]int{
{1, 0, 1},
{1, 0, 1},
},
)
}
func TestList(t *testing.T) {
is := is.New(t)
@@ -85,62 +25,44 @@ func TestPriorityQueue(t *testing.T) {
is := is.New(t)
type elem [2]int
less := func(a, b elem) bool {
return a[0] < b[0]
less := func(b, a *elem) bool {
return (*a)[0] < (*b)[0]
}
pq := aoc.PriorityQueue(less)
pq.Enqueue(elem{1, 4})
pq.Enqueue(elem{3, 2})
pq.Enqueue(elem{2, 3})
pq.Enqueue(elem{4, 1})
pq.Insert(&elem{1, 4})
pq.Insert(&elem{3, 2})
pq.Insert(&elem{2, 3})
pq.Insert(&elem{4, 1})
v, ok := pq.Dequeue()
is.True(ok)
is.Equal(v, elem{4, 1})
v := pq.ExtractMin()
is.True(v != nil)
is.Equal(v, &elem{4, 1})
v, ok = pq.Dequeue()
is.True(ok)
is.Equal(v, elem{3, 2})
v = pq.ExtractMin()
is.True(v != nil)
is.Equal(v, &elem{3, 2})
v, ok = pq.Dequeue()
is.True(ok)
is.Equal(v, elem{2, 3})
v = pq.ExtractMin()
is.True(v != nil)
is.Equal(v, &elem{2, 3})
v, ok = pq.Dequeue()
is.True(ok)
is.Equal(v, elem{1, 4})
v = pq.ExtractMin()
is.True(v != nil)
is.Equal(v, &elem{1, 4})
v, ok = pq.Dequeue()
is.True(!ok)
is.Equal(v, elem{})
v = pq.ExtractMin()
is.True(v == nil)
}
func TestSet(t *testing.T) {
is := is.New(t)
s := aoc.Set(1, 2, 3)
is.True(!s.Has(0))
is.True(s.Has(1))
is.True(s.Has(2))
is.True(s.Has(3))
is.True(!s.Has(4))
s.Add(4)
is.True(s.Has(4))
items := s.Items()
sort.Ints(items)
is.Equal(items, []int{1, 2, 3, 4})
}
func ExamplePriorityQueue() {
type memo struct {
pt int
score int
}
less := func(a, b memo) bool { return b.score < a.score }
less := func(a, b *memo) bool { return a.score < b.score }
adj := map[int][][2]int{
0: {{1, 2}, {2, 6}},
@@ -156,10 +78,10 @@ func ExamplePriorityQueue() {
dist := aoc.DefaultMap[int](int(^uint(0) >> 1))
dist.Set(0, 0)
pq.Enqueue(memo{0, 0})
pq.Insert(&memo{0, 0})
for !pq.IsEmpty() {
m, _ := pq.Dequeue()
m := pq.ExtractMin()
u := m.pt
if visited.Has(u) {
@@ -175,7 +97,7 @@ func ExamplePriorityQueue() {
if !visited.Has(v) && du+w < dv {
dist.Set(v, du+w)
pq.Enqueue(memo{v, du + w})
pq.Insert(&memo{v, du + w})
}
}
}
@@ -195,3 +117,164 @@ func ExamplePriorityQueue() {
// point 5 is 22 steps away.
// point 6 is 19 steps away.
}
func TestGraph(t *testing.T) {
is := is.New(t)
var adjacencyList = map[int][]int{
2: {3, 5, 1},
1: {2, 4},
3: {6, 2},
4: {1, 5, 7},
5: {2, 6, 8, 4},
6: {3, 0, 9, 5},
7: {4, 8},
8: {5, 9, 7},
9: {6, 0, 8},
}
g := aoc.Graph(aoc.WithAdjacencyList[int, int](adjacencyList))
is.Equal(g.Neighbors(1), []int{2, 4})
is.Equal(map[int][]int(g.AdjacencyList()), adjacencyList)
}
func ExampleFibHeap() {
type memo struct {
pt int
score int
}
less := func(a, b *memo) bool { return (*a).score < (*b).score }
adj := map[int][][2]int{
0: {{1, 2}, {2, 6}},
1: {{3, 5}},
2: {{3, 8}},
3: {{4, 10}, {5, 15}},
4: {{6, 2}},
5: {{6, 6}},
}
pq := aoc.FibHeap(less)
visited := aoc.Set([]int{}...)
dist := aoc.DefaultMap[int](int(^uint(0) >> 1))
dist.Set(0, 0)
pq.Insert(&memo{0, 0})
for !pq.IsEmpty() {
m := pq.ExtractMin()
u := m.pt
if visited.Has(u) {
continue
}
visited.Add(u)
du, _ := dist.Get(u)
for _, edge := range adj[u] {
v, w := edge[0], edge[1]
dv, _ := dist.Get(v)
if !visited.Has(v) && du+w < dv {
dist.Set(v, du+w)
pq.Insert(&memo{v, du + w})
}
}
}
items := dist.Items()
sort.Slice(items, func(i, j int) bool { return items[i].K < items[j].K })
for _, v := range items {
fmt.Printf("point %d is %d steps away.\n", v.K, v.V)
}
// Output:
// point 0 is 0 steps away.
// point 1 is 2 steps away.
// point 2 is 6 steps away.
// point 3 is 7 steps away.
// point 4 is 17 steps away.
// point 5 is 22 steps away.
// point 6 is 19 steps away.
}
func TestFibHeap(t *testing.T) {
is := is.New(t)
type elem [2]int
less := func(a, b *elem) bool {
return (*a)[0] < (*b)[0]
}
pq := aoc.FibHeap(less)
pq.Insert(&elem{1, 4})
pq.Insert(&elem{3, 2})
pq.Insert(&elem{2, 3})
pq.Insert(&elem{4, 1})
v := pq.ExtractMin()
is.True(v != nil)
is.Equal(v, &elem{1, 4})
pq.Insert(&elem{5, 8})
pq.Insert(&elem{6, 7})
pq.Insert(&elem{7, 6})
pq.Insert(&elem{8, 5})
v = pq.ExtractMin()
is.True(v != nil)
is.Equal(v, &elem{2, 3})
v = pq.ExtractMin()
is.True(v != nil)
is.Equal(v, &elem{3, 2})
v = pq.ExtractMin()
is.True(v != nil)
is.Equal(v, &elem{4, 1})
v = pq.ExtractMin()
is.True(v != nil)
is.Equal(v, &elem{5, 8})
m := aoc.FibHeap(less)
m.Insert(&elem{1, 99})
m.Insert(&elem{12, 9})
m.Insert(&elem{11, 10})
m.Insert(&elem{10, 11})
m.Insert(&elem{9, 12})
pq.Merge(m)
v = pq.Find(func(t *elem) bool {
return (*t)[0] == 6
})
is.Equal(v, &elem{6, 7})
v = pq.Find(func(t *elem) bool {
return (*t)[0] == 12
})
is.Equal(v, &elem{12, 9})
v = pq.ExtractMin()
is.True(v != nil)
is.Equal(v, &elem{1, 99})
pq.DecreaseKey(
func(t *elem) bool { return t[0] == 12 },
func(t *elem) { t[0] = 3 },
)
v = pq.ExtractMin()
is.True(v != nil)
is.Equal(v, &elem{3, 9})
var keys []int
for !pq.IsEmpty() {
v := pq.ExtractMin()
fmt.Println(v)
keys = append(keys, v[0])
}
is.Equal(keys, []int{6, 7, 8, 9, 10, 11})
}

View File

@@ -29,10 +29,10 @@ func run(scan *bufio.Scanner) (*result, error) {
log("start day 17")
result := result{}
result.valuePT1 = search(m, 1, 3)
result.valuePT1 = search(m, 1, 3, seenFn)
log("result from part 1 = ", result.valuePT1)
result.valuePT2 = search(m, 4, 10)
result.valuePT2 = search(m, 4, 10, nil)
log("result from part 2 = ", result.valuePT2)
return &result, nil
@@ -90,6 +90,7 @@ type graph struct {
m Map
target Point
reads int
seenFn func(a position) position
}
// Neighbors returns valid steps from given position. if at target returns none.
@@ -118,6 +119,7 @@ func (g *graph) Neighbors(current position) []position {
if forward := current.step(); current.steps < g.max && g.m.Valid(forward.loc) {
nbs = append(nbs, forward)
}
return nbs
}
@@ -129,12 +131,13 @@ func (g *graph) Cost(a, b position) int16 {
}
// Potential calculates distance to target
func (g *graph) Potential(a, b position) int16 {
return aoc.ManhattanDistance(a.loc, b.loc)
}
// func (g *graph) Potential(a position) int16 {
// return aoc.ManhattanDistance(a.loc, g.target)
// }
func (g *graph) Target(a position) bool {
if a.loc == g.target && a.steps >= g.min {
// Target returns true when target reached. receives node and cost.
func (g *graph) Target(a position, c int16) bool {
if a.loc == g.target && a.steps >= g.min && a.steps <= g.max {
return true
}
return false
@@ -142,47 +145,79 @@ func (g *graph) Target(a position) bool {
// Seen attempt at simplifying the seen to use horizontal/vertical and no steps.
// It returns correct for part1 but not part 2..
// func (g *graph) Seen(a position) position {
// if a.direction == U {
// a.direction = D
// }
// if a.direction == L {
// a.direction = R
// }
// a.steps = 0
// return a
// }
func (g *graph) Seen(a position) position {
if g.seenFn != nil {
return g.seenFn(a)
}
return a
}
func search(m Map, minSteps, maxSteps int8) int {
func seenFn(a position) position {
if a.direction == U {
a.direction = D
}
if a.direction == L {
a.direction = R
}
// a.steps = 0
return a
}
func search(m Map, minSteps, maxSteps int8, seenFn func(position) position) int {
rows, cols := m.Size()
start := Point{}
target := Point{rows - 1, cols - 1}
g := graph{min: minSteps, max: maxSteps, m: m, target: target}
cost, path := aoc.FindPath[int16, position](&g, position{loc: start}, position{loc: target})
g := graph{min: minSteps, max: maxSteps, m: m, target: target, seenFn: seenFn}
log("total map reads = ", g.reads)
printGraph(m, path)
cost, path, closed := aoc.FindPath[int16, position](&g, position{loc: start}, position{loc: target})
log("total map reads = ", g.reads, "cost = ", cost)
printGraph(m, path, closed, g.seenFn)
return int(cost)
}
// printGraph with the path overlay
func printGraph(m Map, path []position) {
// printGraph with the path/cost overlay
func printGraph(m Map, path []position, closed map[position]int16, seenFn func(a position) position) {
pts := make(map[Point]position, len(path))
for _, pt := range path {
pts[pt.loc] = pt
}
clpt := make(map[position]position, len(closed))
for pt := range closed {
clpt[position{loc: pt.loc, steps: pt.steps}] = pt
}
for r, row := range m {
// if r == 0 {
// for c := range row {
// if c == 0 {
// fmt.Print(" ")
// }
// fmt.Printf("% 5d", c)
// }
// fmt.Println("")
// }
for c := range row {
if _, ok := pts[Point{int16(r), int16(c)}]; ok {
// if c == 0 {
// fmt.Printf("% 5d", r)
// }
if pt, ok := pts[Point{int16(r), int16(c)}]; ok {
if seenFn != nil {
pt = seenFn(pt)
}
_ = pt
// fmt.Printf("% 5d", closed[pt])
fmt.Print("*")
continue
}
fmt.Print(".")
// fmt.Print(" ....")
fmt.Print(" ")
}
fmt.Println("")
}

View File

@@ -28,14 +28,14 @@ func TestExample(t *testing.T) {
is.Equal(result.valuePT2, 94)
}
// func TestSolution(t *testing.T) {
// is := is.New(t)
// scan := bufio.NewScanner(bytes.NewReader(input))
func TestSolution(t *testing.T) {
is := is.New(t)
scan := bufio.NewScanner(bytes.NewReader(input))
// result, err := run(scan)
// is.NoErr(err)
result, err := run(scan)
is.NoErr(err)
// t.Log(result)
// is.Equal(result.valuePT1, 843)
// is.Equal(result.valuePT2, 1017)
// }
t.Log(result)
is.Equal(result.valuePT1, 843)
is.Equal(result.valuePT2, 1017)
}

View File

@@ -186,8 +186,8 @@ func solveWorkflow(parts []part, workflows map[string][]rule) int {
func solveRanges(workflows map[string][]rule) uint {
pq := aoc.PriorityQueue(func(a, b queue) bool { return false })
pq.Enqueue(queue{
pq := aoc.PriorityQueue(func(a, b *queue) bool { return false })
pq.Insert(&queue{
"in",
block{
ranger{1, 4000},
@@ -200,9 +200,9 @@ func solveRanges(workflows map[string][]rule) uint {
// var rejected []block
for !pq.IsEmpty() {
current, _ := pq.Dequeue()
current := pq.ExtractMin()
for _, rule := range workflows[current.name] {
next := queue{name: rule.queue, block: current.block}
next := &queue{name: rule.queue, block: current.block}
switch rule.match {
case "x":
@@ -223,14 +223,14 @@ func solveRanges(workflows map[string][]rule) uint {
accepted = append(accepted, next.block)
default:
pq.Enqueue(next)
pq.Insert(next)
}
}
}
var sum uint
for _, a := range accepted {
sum += uint((a.x[1]-a.x[0]+1) * (a.m[1]-a.m[0]+1) * (a.a[1]-a.a[0]+1) * (a.s[1]-a.s[0]+1))
sum += uint((a.x[1] - a.x[0] + 1) * (a.m[1] - a.m[0] + 1) * (a.a[1] - a.a[0] + 1) * (a.s[1] - a.s[0] + 1))
}
return sum

View File

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

View File

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

View File

@@ -1,237 +0,0 @@
package main
import (
"bufio"
_ "embed"
"fmt"
"strings"
aoc "go.sour.is/advent-of-code"
)
var log = aoc.Log
func main() { aoc.MustResult(aoc.Runner(run)) }
type result struct {
valuePT1 int
valuePT2 int
}
func (r result) String() string { return fmt.Sprintf("%#v", r) }
func run(scan *bufio.Scanner) (*result, error) {
var m aoc.Map[int16, rune]
start := Point{0, 0}
target := Point{0, 0}
var text string
for scan.Scan() {
text = scan.Text()
if start == target {
start[1] = int16(strings.IndexRune(text, '.'))
}
m = append(m, []rune(text))
}
target[0] = int16(len(m) - 1)
target[1] = int16(strings.IndexRune(text, '.'))
result := &result{}
result.valuePT1 = search(&graph{m: m, start: start, target: target, neighbors: part1nbs})
result.valuePT2 = search(&graph{m: m, start: start, target: target, neighbors: part2nbs})
return result, nil
}
type Point = aoc.Point[int16]
type Map = aoc.Map[int16, rune]
// diretion of path steps
type direction int8
var (
U = Point{-1, 0}
R = Point{0, 1}
D = Point{1, 0}
L = Point{0, -1}
)
var directions = []Point{U, R, D, L}
var dirIDX = func() map[Point]direction {
m := make(map[Point]direction, len(directions))
for k, v := range directions {
m[v] = direction(k)
}
return m
}()
var arrows = []rune{'^', '>', 'v', '<'}
var arrowIDX = func() map[rune]Point {
m := make(map[rune]Point, len(arrows))
for k, v := range arrows {
m[v] = directions[k]
}
return m
}()
// position on the map
type position struct {
loc Point
direction Point
}
func (p position) step(to Point) position {
return position{p.loc.Add(to), to}
}
// implements FindPath graph interface
type graph struct {
m Map
start Point
target Point
neighbors func(g *graph, current position) []position
}
// Neighbors returns valid steps from given position. if at target returns none.
func (g *graph) Neighbors(current position) []position {
return g.neighbors(g, current)
}
// Cost calculates heat cost to neighbor from map
func (g *graph) Cost(a, b position) int16 {
return 1
}
// Potential calculates distance to target
func (g *graph) Potential(a, b position) int16 {
return aoc.ManhattanDistance(a.loc, b.loc)
}
func (g *graph) Seen(a position) position {
a.direction = Point{}
return a
}
func match[T comparable](match T, lis ...T) bool {
for _, b := range lis {
if b == match {
return true
}
}
return false
}
func search(g *graph) int {
costs, paths := aoc.FindPaths[int16, position](g, position{loc: g.start}, position{loc: g.target})
for i, path := range paths {
log("path length = ", costs[i])
printGraph(g.m, path)
}
return int(aoc.Max(0, costs...))
}
// printGraph with the path overlay
func printGraph(m Map, path []position) {
pts := make(map[Point]position, len(path))
for _, pt := range path {
pts[pt.loc] = pt
}
for r, row := range m {
for c, x := range row {
if _, ok := pts[Point{int16(r), int16(c)}]; ok {
if x == '.' {
fmt.Print("*")
} else {
fmt.Print(string(x))
}
continue
}
fmt.Print(".")
_ = x
// fmt.Print(string(x))
}
fmt.Println("")
}
fmt.Println("")
}
func opposite(d Point) Point {
return directions[(dirIDX[d]+2)%4]
}
func part1nbs(g *graph, current position) []position {
var nbs []position
if current.loc == g.start {
return []position{
{current.loc.Add(D), D},
}
}
if current.loc == g.target {
return nil
}
// only one direction on arrow.
_, r, _ := g.m.Get(current.loc)
if match(r, arrows...) {
to := arrowIDX[r]
if next := current.step(to); g.m.Valid(next.loc) {
_, r, _ := g.m.Get(next.loc)
d := arrows[(dirIDX[to]+2)%4] // flow from opposite direction
if !match(r, rune(d), '#') {
nbs = append(nbs, next)
}
}
return nbs
}
for _, to := range directions {
if next := current.step(to); g.m.Valid(next.loc) {
_, r, _ := g.m.Get(next.loc)
d := arrows[(dirIDX[to]+2)%4] // flow from opposite direction
if !match(r, rune(d), '#') {
nbs = append(nbs, next)
}
}
}
return nbs
}
func part2nbs(g *graph, current position) []position {
var nbs []position
if current.loc == g.start {
return []position{
{current.loc.Add(D), D},
}
}
if current.loc == g.target {
return nil
}
for _, to := range directions {
if next := current.step(to); g.m.Valid(next.loc) {
if next.direction == opposite(current.direction) {
continue
}
_, r, _ := g.m.Get(next.loc)
if r == '#' {
continue
}
nbs = append(nbs, next)
}
}
return nbs
}

View File

@@ -1,42 +0,0 @@
package main
import (
"bufio"
"bytes"
"testing"
_ "embed"
"github.com/matryer/is"
)
//go:embed example.txt
var example []byte
//go:embed input.txt
var input []byte
func TestExample(t *testing.T) {
is := is.New(t)
scan := bufio.NewScanner(bytes.NewReader(example))
result, err := run(scan)
is.NoErr(err)
t.Log(result)
// is.Equal(result.valuePT1, 94)
is.Equal(result.valuePT2, 154)
}
// func TestSolution(t *testing.T) {
// is := is.New(t)
// scan := bufio.NewScanner(bytes.NewReader(input))
// result, err := run(scan)
// is.NoErr(err)
// t.Log(result)
// is.True(result.valuePT1 > 1918)
// is.Equal(result.valuePT1, 2074)
// is.Equal(result.valuePT2, 0)
// }

4
go.mod
View File

@@ -1,8 +1,6 @@
module go.sour.is/advent-of-code
go 1.21.4
toolchain go1.21.5
go 1.22.0
require (
github.com/matryer/is v1.4.1

174
grids.go
View File

@@ -1,5 +1,14 @@
package aoc
import (
"cmp"
"fmt"
"sort"
"strings"
"golang.org/x/exp/maps"
)
type Vector struct {
Offset Point[int]
Scale int
@@ -58,7 +67,7 @@ func NumPoints(outline []Point[int], borderLength int) int {
type Map[I integer, T any] [][]T
func (m *Map[I,T]) Get(p Point[I]) (Point[I], T, bool) {
func (m *Map[I, T]) Get(p Point[I]) (Point[I], T, bool) {
var zero T
if !m.Valid(p) {
return [2]I{0, 0}, zero, false
@@ -66,13 +75,172 @@ func (m *Map[I,T]) Get(p Point[I]) (Point[I], T, bool) {
return p, (*m)[p[0]][p[1]], true
}
func (m *Map[I,T]) Size() (I, I) {
func (m *Map[I, T]) Size() (I, I) {
if m == nil || len(*m) == 0 {
return 0, 0
}
return I(len(*m)), I(len((*m)[0]))
}
func (m *Map[I,T]) Valid(p Point[I]) bool {
func (m *Map[I, T]) Valid(p Point[I]) bool {
rows, cols := m.Size()
return p[0] >= 0 && p[0] < rows && p[1] >= 0 && p[1] < cols
}
type cmap[C number, N comparable] struct {
base pather[C, N]
neighbors map[N]map[N]C
}
func (m *cmap[C, N]) Cost(a, b N) C {
if v, ok := m.neighbors[a]; ok {
return v[b]
}
return 0
}
func (m *cmap[C, N]) Neighbors(n N) []N {
if v, ok := m.neighbors[n]; ok {
return maps.Keys(v)
}
return nil
}
func (m *cmap[C, N]) Target(n N, c C) bool {
return m.base.Target(n, c)
}
func (m *cmap[C, N]) String() string {
var b = &strings.Builder{}
for k, nbs := range m.neighbors {
fmt.Fprintln(b, k)
for to, c := range nbs {
fmt.Fprintln(b, " ", to, c)
}
}
return b.String()
}
func CompressMap[C number, N comparable](p pather[C, N], start N) pather[C, N] {
var next = []N{start}
var visited = make(map[N]map[N]C)
var n N
for len(next) > 0 {
n, next = next[len(next)-1], next[:len(next)-1]
if _, ok := visited[n]; ok {
continue
}
nbs := p.Neighbors(n)
if len(nbs) == 2 {
a, b := nbs[0], nbs[1]
if to, ok := visited[a]; ok {
to[b] = to[n] + p.Cost(n, b)
delete(to, n)
visited[a] = to
} else if to, ok := visited[b]; ok {
to[a] = to[n] + p.Cost(n, a)
delete(to, n)
visited[b] = to
}
continue
}
visited[n] = make(map[N]C)
next = append(next, nbs...)
for _, to := range nbs {
visited[n][to] = p.Cost(n, to)
}
}
return &cmap[C, N]{base: p, neighbors: visited}
}
type adjacencyList[V any, C comparable] map[C][]V
type graph[V any, W cmp.Ordered, C comparable] map[C]*vertex[V, W]
type graphOption[V any, W cmp.Ordered, C comparable] func(g *graph[V, W, C])
type vertex[V any, W cmp.Ordered] struct {
Value V
Edges edges[V, W]
}
func (v *vertex[V, W]) Neighbors() []V {
var nbs []V
sort.Sort(v.Edges)
for _, e := range v.Edges {
nbs = append(nbs, e.Vertex.Value)
}
return nbs
}
type edge[V any, W cmp.Ordered] struct {
Vertex *vertex[V, W]
Weight W
}
type edges[V any, W cmp.Ordered] []edge[V, W]
func (e edges[V, W]) Len() int { return len(e) }
func (e edges[V, W]) Less(i, j int) bool { return e[i].Weight < e[j].Weight }
func (e edges[V, W]) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
func Graph[V any, W cmp.Ordered, C comparable](opts ...graphOption[V, W, C]) *graph[V, W, C] {
g := make(graph[V, W, C])
for _, opt := range opts {
opt(&g)
}
return &g
}
func (g *graph[V, W, C]) AddVertex(id C, value V) {
(*g)[id] = &vertex[V, W]{Value: value}
}
func (g *graph[V, W, C]) AddEdge(from, to C, w W) {
if g == nil {
return
}
if _, ok := (*g)[from]; !ok {
return
}
if _, ok := (*g)[to]; !ok {
return
}
(*g)[from].Edges = append((*g)[from].Edges, edge[V, W]{(*g)[to], w})
}
func (g *graph[V, W, C]) Neighbors(v C) []V {
if g == nil {
return nil
}
return (*g)[v].Neighbors()
}
func (g *graph[V, W, C]) AdjacencyList() adjacencyList[V, C] {
m := make(map[C][]V)
for id, v := range *g {
if len(v.Edges) == 0 {
continue
}
m[id] = v.Neighbors()
}
return m
}
func WithAdjacencyList[W cmp.Ordered, C comparable](list adjacencyList[C, C]) graphOption[C, W, C] {
var zeroW W
return func(g *graph[C, W, C]) {
for vertex, edges := range list {
if _, ok := (*g)[vertex]; !ok {
g.AddVertex(vertex, vertex)
}
// add edges to vertex
for _, edge := range edges {
// add edge as vertex, if not added
if _, ok := (*g)[edge]; !ok {
g.AddVertex(edge, edge)
}
g.AddEdge(vertex, edge, zeroW) // no weights in this adjacency list
}
}
}
}

69
hacker01/hacker.go Normal file
View File

@@ -0,0 +1,69 @@
package main
import "fmt"
import "os"
import "bufio"
import "strconv"
import "strings"
var input1 = strings.NewReader(`
6
1 2 5 3 6 4
`)
var input2 = strings.NewReader(`
15
1 14 3 7 4 5 15 6 13 10 11 2 12 8 9
`)
var input3 = os.Stdin
func main() {
var length int
var tree *node
scanner := bufio.NewScanner(input1)
for scanner.Scan() {
if length == 0 {
length, _ = strconv.Atoi(scanner.Text())
continue
}
for _, txt := range strings.Fields(scanner.Text()) {
if v, err := strconv.Atoi(txt); err == nil {
tree = insert(tree, &node{value: v})
}
}
}
foo(input{1, "hi"})
fmt.Println(tree)
}
type node struct {
value int
left *node
right *node
}
func insert(root, n *node) *node {
if root == nil {
return n
}
if root.value > n.value {
root.left = insert(root.left, n)
return root
}
root.right = insert(root.right, n)
return root
}
func (n *node) String() string {
if n == nil { return "" }
return fmt.Sprintf("%v %v%v", n.value, n.left.String(), n.right.String())
}
type input struct{a int; b string}
func foo (in input) {}

109
hacker02/hacker.go Normal file
View File

@@ -0,0 +1,109 @@
package main
import (
"bufio"
"fmt"
"io"
"os"
"strconv"
"strings"
aoc "go.sour.is/advent-of-code"
)
/*
* Complete the 'cookies' function below.
*
* The function is expected to return an INTEGER.
* The function accepts following parameters:
* 1. INTEGER k
* 2. INTEGER_ARRAY A
*/
func cookies(k int32, A []int32) int32 {
var i int32
// fmt.Println(" ", A)
// sort.Slice(A, func(i, j int) bool { return A[j] < A[i]})
// for A[len(A)-1] < k {
// if len(A) < 2 {
// return -1
// }
// // fmt.Println(" ", A[len(A)-1], " + 2x", A[len(A)-2], 2*A[len(A)-2] + A[len(A)-1])
// A[len(A)-2] = 2*A[len(A)-2] + A[len(A)-1]
// A = A[:len(A)-1]
// for j:=len(A)-1; j>0 && A[j] > A[j-1]; j-- {
// A[j], A[j-1] = A[j-1], A[j]
// }
// i++
// fmt.Println("A", len(A), i)
// }
pq := aoc.FibHeap(func(a, b *int32) bool { return *a < *b })
for i := range A {
pq.Insert(&A[i])
}
for !pq.IsEmpty() && *pq.GetMin() < k {
first := *pq.ExtractMin()
second := *pq.ExtractMin()
third := first + 2*second
pq.Insert(&third)
i++
}
return i
}
func main() {
reader := bufio.NewReaderSize(os.Stdin, 16*1024*1024)
stdout, err := os.Create("/dev/stderr")
checkError(err)
defer stdout.Close()
writer := bufio.NewWriterSize(stdout, 16*1024*1024)
firstMultipleInput := strings.Split(strings.TrimSpace(readLine(reader)), " ")
nTemp, err := strconv.ParseInt(firstMultipleInput[0], 10, 64)
checkError(err)
n := int32(nTemp)
kTemp, err := strconv.ParseInt(firstMultipleInput[1], 10, 64)
checkError(err)
k := int32(kTemp)
ATemp := strings.Split(strings.TrimSpace(readLine(reader)), " ")
var A []int32
for i := 0; i < int(n); i++ {
AItemTemp, err := strconv.ParseInt(ATemp[i], 10, 64)
checkError(err)
AItem := int32(AItemTemp)
A = append(A, AItem)
}
result := cookies(k, A)
fmt.Fprintf(writer, "%d\n", result)
writer.Flush()
}
func readLine(reader *bufio.Reader) string {
str, _, err := reader.ReadLine()
if err == io.EOF {
return ""
}
return strings.TrimRight(string(str), "\r\n")
}
func checkError(err error) {
if err != nil {
panic(err)
}
}

2
hacker02/input.txt Normal file

File diff suppressed because one or more lines are too long

2
hacker02/other.txt Normal file
View File

@@ -0,0 +1,2 @@
8 90
13 47 74 12 89 74 18 38

0
hacker02/output.txt Normal file
View File

44
itertools_test.go Normal file
View File

@@ -0,0 +1,44 @@
package aoc_test
import (
"testing"
"github.com/matryer/is"
aoc "go.sour.is/advent-of-code"
)
func TestReverse(t *testing.T) {
is := is.New(t)
is.Equal(aoc.Reverse([]int{1, 2, 3, 4}), []int{4, 3, 2, 1})
}
func TestReadStringToInts(t *testing.T) {
is := is.New(t)
is.Equal(aoc.ReadStringToInts([]string{"1", "2", "3"}), []int{1, 2, 3})
}
func TestRepeat(t *testing.T) {
is := is.New(t)
is.Equal(aoc.Repeat(5, 3), []int{5, 5, 5})
}
func TestTranspose(t *testing.T) {
is := is.New(t)
is.Equal(
aoc.Transpose(
[][]int{
{1, 1},
{0, 0},
{1, 1},
},
),
[][]int{
{1, 0, 1},
{1, 0, 1},
},
)
}

33
math_test.go Normal file
View File

@@ -0,0 +1,33 @@
package aoc_test
import (
"testing"
"github.com/matryer/is"
aoc "go.sour.is/advent-of-code"
)
func TestLCM(t *testing.T) {
is := is.New(t)
is.Equal(aoc.LCM([]int{}...), 0)
is.Equal(aoc.LCM(5), 5)
is.Equal(aoc.LCM(5, 3), 15)
is.Equal(aoc.LCM(5, 3, 2), 30)
}
func TestPower2(t *testing.T) {
is := is.New(t)
is.Equal(aoc.Power2(0), 1)
is.Equal(aoc.Power2(1), 2)
is.Equal(aoc.Power2(2), 4)
}
func TestABS(t *testing.T) {
is := is.New(t)
is.Equal(aoc.ABS(1), 1)
is.Equal(aoc.ABS(0), 0)
is.Equal(aoc.ABS(-1), 1)
}

15
ranger/channel.go Normal file
View File

@@ -0,0 +1,15 @@
package main
func countChan(n int) <-chan int {
ch := make(chan int)
go func() {
for i := range n {
ch <- i
}
close(ch)
}()
return ch
}

84
ranger/range.go Normal file
View File

@@ -0,0 +1,84 @@
package main
// export GOEXPERIMENT=rangefunc
import "iter"
func Step(start, stop, step int) iter.Seq[int] {
if step < 0 {
return func(yield func(int) bool) {
for i := start; i <= start && i > stop; i += step {
next := yield(i)
if !next {
return
}
}
}
}
if step > 0 {
return func(yield func(int) bool) {
for i := start; i >= start && i < stop; i += step {
next := yield(i)
if !next {
println("stopped!")
return
}
}
}
}
return func(yield func(int) bool) { yield(start) }
}
func Fib() iter.Seq[uint] {
return func(yield func(uint) bool) {
previous, value := uint(1), uint(1)
next := yield(value)
for next {
next = yield(value)
value, previous = value+previous, value
if value < previous {
return
}
}
}
}
func Fizzbuzzed[T int | uint](from iter.Seq[T]) iter.Seq2[T, string] {
pull, stop := iter.Pull(from)
return func(yield func(T, string) bool) {
defer stop()
for {
var fizzbuzz string
p, ok := pull()
if !ok {
return
}
if p%3 == 0 {
fizzbuzz = "fizz"
}
if p%5 == 0 {
fizzbuzz += "buzz"
}
if !yield(p, fizzbuzz) {
return
}
}
}
}
// func main() {
// for i, v := range Fizzbuzzed(Fib()) {
// println(i, v)
// }
// }
func countScan(n int) (func() bool, func() int) {
pos := -1
return func() bool {
pos++
return pos < n
}, func() int {
return pos
}
}

193
ranger/range_test.go Normal file
View File

@@ -0,0 +1,193 @@
package main_test
import (
"testing"
"github.com/matryer/is"
)
// func BenchmarkChan(b *testing.B) {
// sum := 0
// for n := range countChan(b.N) {
// sum += n
// }
// b.StopTimer()
// b.Log(sum)
// }
// func BenchmarkScan(b *testing.B) {
// sum := 0
// scan, value := countScan(b.N)
// for scan() {
// sum += value()
// }
// b.StopTimer()
// b.Log(sum)
// }
// func BenchmarkRange(b *testing.B) {
// sum := 0
// for n := range countRF(b.N) {
// sum += n
// }
// b.StopTimer()
// b.Log(sum)
// }
// func TestRangeS(t *testing.T) {
// for i := range Step(0, 10, 3) {
// t.Log(i)
// }
// for i := range Step(10, 0, -3) {
// t.Log(i)
// }
// }
func TestSelect(t *testing.T) {
done1 := make(chan struct{})
done2 := make(chan struct{})
go func() {
close(done1)
close(done2)
}()
for i, ch := range []chan struct{}{done1, done2} {
<-ch
println("closed", i)
}
}
func TestGCD(t *testing.T) {
is := is.New(t)
is.Equal(gcdOfStrings("ABAB", "ABABAB"), "AB")
is.Equal(gcdOfStrings("ABAB", "ABABABAB"), "ABAB")
is.Equal(gcdOfStrings("ABC", "ABCABC"), "ABC")
is.Equal(gcdOfStrings("ABC", "ABCDEF"), "")
is.Equal(gcdOfStrings("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
}
func gcdOfStrings(str1 string, str2 string) string {
if len(str1) == 0 {
return ""
}
runes1 := []rune(str1)
runes2 := []rune(str2)
n := max(len(runes1), len(runes2))
m := min(len(runes1), len(runes2))
if len(runes2) == m {
runes1, runes2 = runes2, runes1
}
if isRepeating(runes1, runes2) {
return string(runes1)
}
for i := n; i > 0; i-- {
if runes1[i%len(runes1)] != runes2[i%len(runes2)] {
return ""
}
gcd := runes2[:i]
if isRepeating(gcd, runes1) && isRepeating(gcd, runes2) {
return string(gcd)
}
}
return ""
}
func isRepeating(str1, str2 []rune) bool {
if len(str1) > len(str2) {
return false
}
if len(str2)%len(str1) != 0 {
return false
}
for i := range str2 {
if str1[i%len(str1)] != str2[i] {
return false
}
}
return true
}
func canPlaceFlowers(flowerbed []int, n int) bool {
var c int
for i := 0; i < len(flowerbed); i++ {
if (i == 0 || flowerbed[i-1] == 0) &&
flowerbed[i] == 0 &&
(i == len(flowerbed)-1 || flowerbed[i+1] == 0) {
c++
flowerbed[i] = 1
}
}
return c >= n
}
func TestFlowers(t *testing.T) {
is := is.New(t)
is.Equal(canPlaceFlowers([]int{1, 0, 0, 0, 1}, 1), true)
is.Equal(canPlaceFlowers([]int{0, 0, 1, 0, 1}, 1), true)
is.Equal(canPlaceFlowers([]int{1, 0, 1, 0, 0}, 1), true)
}
func dailyTemperatures(temperatures []int) []int {
answers := make([]int, len(temperatures))
var stack [][2]int
push := func(v int) {
if len(stack) == 0 {
stack = append(stack, [2]int{v, 1})
return
}
end := len(stack)-1
if v < stack[end][0]{
stack = append(stack, [2]int{v, 1})
return
}
if v == stack[end][0] {
stack[len(stack)-1][1]++
return
}
var vs = [2]int{v, 1}
// for ; end>0; end-- {
// if stack[end][0]<vs[0] {
// vs[1]+=stack[end][1]
// continue
// }
// break
// }
stack = append(stack, vs)
}
for i := len(temperatures) - 2; i >= 0; i-- {
push(temperatures[i+1])
currentTemp := temperatures[i]
days := 1
end := len(stack)-1
for j := range stack {
nextTemp := stack[end-j]
if currentTemp < nextTemp[0] {
answers[i] = days
break
}
days+=nextTemp[1]
}
}
return answers
}
func TestTemps(t *testing.T) {
is := is.New(t)
is.Equal(dailyTemperatures(
[]int{73, 74, 75, 71, 69, 72, 76, 73}),
[]int{1, 1, 4, 2, 1, 1, 0, 0})
is.Equal(dailyTemperatures(
[]int{99,99,99,99,100}), []int{4,3,2,1,0})
}

12
ranger/rangefunc.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func countRF(n int) func(yield func(int) bool) {
return func(yield func(int) bool) {
for i := range n {
if !yield(i) {
return
}
}
}
}

View File

@@ -2,26 +2,70 @@ package aoc
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"runtime/pprof"
"strings"
"time"
)
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
var memprofile = flag.String("memprofile", "", "write memory profile to `file`")
func Runner[R any, F func(*bufio.Scanner) (R, error)](run F) (R, error) {
if len(os.Args) != 2 {
if len(os.Args) < 2 {
Log("Usage:", filepath.Base(os.Args[0]), "FILE")
os.Exit(22)
}
input, err := os.Open(os.Args[1])
inputFilename := os.Args[1]
os.Args = append(os.Args[:1], os.Args[2:]...)
flag.Parse()
Log(cpuprofile, memprofile, *cpuprofile, *memprofile)
if *cpuprofile != "" {
Log("enabled cpu profile")
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
defer f.Close() // error handling omitted for example
Log("write cpu profile to", f.Name())
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
}
if *memprofile != "" {
Log("enabled mem profile")
defer func() {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal("could not create memory profile: ", err)
}
Log("write mem profile to", f.Name())
defer f.Close() // error handling omitted for example
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatal("could not write memory profile: ", err)
}
}()
}
input, err := os.Open(inputFilename)
if err != nil {
Log(err)
os.Exit(1)
}
scan := bufio.NewScanner(input)
return run(scan)
}

396
search.go
View File

@@ -1,13 +1,13 @@
package aoc
import (
"maps"
"math/bits"
"sort"
)
type priorityQueue[T any] struct {
elems []T
less func(a, b T) bool
elems []*T
less func(a, b *T) bool
maxDepth int
totalEnqueue int
totalDequeue int
@@ -17,10 +17,10 @@ type priorityQueue[T any] struct {
// less is the function for sorting. reverse a and b to reverse the sort.
// T is the item
// U is a slice of T
func PriorityQueue[T any](less func(a, b T) bool) *priorityQueue[T] {
func PriorityQueue[T any](less func(a, b *T) bool) *priorityQueue[T] {
return &priorityQueue[T]{less: less}
}
func (pq *priorityQueue[T]) Enqueue(elem T) {
func (pq *priorityQueue[T]) Insert(elem *T) {
pq.totalEnqueue++
pq.elems = append(pq.elems, elem)
@@ -29,35 +29,41 @@ func (pq *priorityQueue[T]) Enqueue(elem T) {
func (pq *priorityQueue[T]) IsEmpty() bool {
return len(pq.elems) == 0
}
func (pq *priorityQueue[T]) Dequeue() (T, bool) {
func (pq *priorityQueue[T]) ExtractMin() *T {
pq.totalDequeue++
var elem T
var elem *T
if pq.IsEmpty() {
return elem, false
return elem
}
sort.Slice(pq.elems, func(i, j int) bool { return pq.less(pq.elems[i], pq.elems[j]) })
sort.Slice(pq.elems, func(i, j int) bool { return pq.less(pq.elems[j], pq.elems[i]) })
pq.elems, elem = pq.elems[:len(pq.elems)-1], pq.elems[len(pq.elems)-1]
return elem, true
return elem
}
// ManhattanDistance the distance between two points measured along axes at right angles.
func ManhattanDistance[T integer](a, b Point[T]) T {
return ABS(a[1]-b[1]) + ABS(a[0]-b[0])
return ABS(a[0]-b[0]) + ABS(a[1]-b[1])
}
type pather[C number, N comparable] interface {
// Neighbors returns all neighbors to node N that should be considered next.
Neighbors(N) []N
// Cost returns
Cost(a, b N) C
Potential(a, b N) C
// Target returns true when target reached. receives node and cost.
Target(N, C) bool
// OPTIONAL:
// Add heuristic for running as A* search.
// Potential(N) C
// Seen modify value used by seen pruning.
// Seen(N) N
// Target returns true if target reached.
// Target(N) bool
}
// FindPath uses the A* path finding algorithem.
@@ -68,9 +74,18 @@ type pather[C number, N comparable] interface {
//
// start, end are nodes that dileniate the start and end of the search path.
// The returned values are the calculated cost and the path taken from start to end.
func FindPath[C integer, N comparable](g pather[C, N], start, end N) (C, []N) {
func FindPath[C integer, N comparable](g pather[C, N], start, end N) (C, []N, map[N]C) {
var zero C
closed := make(map[N]bool)
var seenFn = func(a N) N { return a }
if s, ok := g.(interface{ Seen(N) N }); ok {
seenFn = s.Seen
}
var potentialFn = func(N) C { var zero C; return zero }
if p, ok := g.(interface{ Potential(N) C }); ok {
potentialFn = p.Potential
}
type node struct {
cost C
@@ -79,7 +94,7 @@ func FindPath[C integer, N comparable](g pather[C, N], start, end N) (C, []N) {
position N
}
NewPath := func(n *node) []N {
newPath := func(n *node) []N {
var path []N
for n.parent != nil {
path = append(path, n.position)
@@ -91,158 +106,219 @@ func FindPath[C integer, N comparable](g pather[C, N], start, end N) (C, []N) {
return path
}
less := func(a, b node) bool {
return b.cost+b.potential < a.cost+a.potential
less := func(a, b *node) bool {
return a.cost+a.potential < b.cost+b.potential
}
pq := PriorityQueue(less)
pq.Enqueue(node{position: start})
closed[start] = false
closed := make(map[N]C)
open := FibHeap(less)
defer func() {
Log("queue max depth = ", pq.maxDepth, "total enqueue = ", pq.totalEnqueue, "total dequeue = ", pq.totalDequeue)
}()
open.Insert(&node{position: start, potential: potentialFn(start)})
closed[start] = zero
var seenFn = func(a N) N { return a }
if s, ok := g.(interface{ Seen(N) N }); ok {
seenFn = s.Seen
}
var targetFn = func(a N) bool { return true }
if s, ok := g.(interface{ Target(N) bool }); ok {
targetFn = s.Target
}
for !pq.IsEmpty() {
current, _ := pq.Dequeue()
cost, potential, n := current.cost, current.potential, current.position
seen := seenFn(n)
if closed[seen] {
continue
}
closed[seen] = true
if cost > 0 && potential == zero && targetFn(current.position) {
return cost, NewPath(&current)
}
for _, nb := range g.Neighbors(n) {
seen := seenFn(nb)
if closed[seen] {
continue
}
cost := g.Cost(n, nb) + current.cost
nextPath := node{
for !open.IsEmpty() {
current := open.ExtractMin()
for _, nb := range g.Neighbors(current.position) {
next := &node{
position: nb,
parent: &current,
cost: cost,
potential: g.Potential(nb, end),
parent: current,
cost: g.Cost(current.position, nb) + current.cost,
potential: potentialFn(nb),
}
// check if path is in open list
if _, open := closed[seen]; !open {
pq.Enqueue(nextPath)
closed[seen] = false // add to open list
seen := seenFn(nb)
cost, ok := closed[seen]
if !ok || next.cost < cost {
open.Insert(next)
closed[seen] = next.cost
}
if next.potential == zero && g.Target(next.position, next.cost) {
return next.cost, newPath(next), closed
}
}
}
return zero, nil
return zero, nil, closed
}
// FindPath uses the A* path finding algorithem.
// g is the graph source that implements the pather interface.
//
// C is an numeric type for calculating cost/potential
// N is the node values. is comparable for storing in visited table for pruning.
//
// start, end are nodes that dileniate the start and end of the search path.
// The returned values are the calculated cost and the path taken from start to end.
func FindPaths[C integer, N comparable](g pather[C, N], start, end N) ([]C, [][]N) {
var zero C
// closed := make(map[N]bool)
type node struct {
cost C
potential C
parent *node
position N
closed map[N]bool
}
NewPath := func(n *node) []N {
var path []N
for n.parent != nil {
path = append(path, n.position)
n = n.parent
}
path = append(path, n.position)
Reverse(path)
return path
}
less := func(b, a node) bool {
return b.cost+b.potential < a.cost+a.potential
}
pq := PriorityQueue(less)
pq.Enqueue(node{position: start, closed: make(map[N]bool)})
defer func() {
Log("queue max depth = ", pq.maxDepth, "total enqueue = ", pq.totalEnqueue, "total dequeue = ", pq.totalDequeue)
}()
var seenFn = func(a N) N { return a }
if s, ok := g.(interface{ Seen(N) N }); ok {
seenFn = s.Seen
}
var targetFn = func(a N) bool { return true }
if s, ok := g.(interface{ Target(N) bool }); ok {
targetFn = s.Target
}
var paths [][]N
var costs []C
for !pq.IsEmpty() {
current, _ := pq.Dequeue()
cost, potential, n := current.cost, current.potential, current.position
seen := seenFn(n)
if current.closed[seen] {
continue
}
current.closed[seen] = true
if cost > 0 && potential == zero && cost > Max(0, costs...) && targetFn(current.position) {
paths = append([][]N(nil), NewPath(&current))
costs = append([]C(nil), cost)
Log("new record = ", cost)
continue
}
for _, nb := range g.Neighbors(n) {
seen := seenFn(nb)
if current.closed[seen] {
continue
}
cost := g.Cost(n, nb) + current.cost
next := node{
position: nb,
parent: &current,
cost: cost,
potential: g.Potential(nb, end),
closed: maps.Clone(current.closed),
}
// check if path is in open list
if _, open := current.closed[seen]; !open {
next.closed[seen] = false // add to open list
pq.Enqueue(next)
}
}
}
return costs, paths
type fibTree[T any] struct {
value *T
parent *fibTree[T]
child []*fibTree[T]
mark bool
}
func (t *fibTree[T]) Value() *T { return t.value }
func (t *fibTree[T]) addAtEnd(n *fibTree[T]) {
n.parent = t
t.child = append(t.child, n)
}
type fibHeap[T any] struct {
trees []*fibTree[T]
least *fibTree[T]
count uint
less func(a, b *T) bool
}
func FibHeap[T any](less func(a, b *T) bool) *fibHeap[T] {
return &fibHeap[T]{less: less}
}
func (h *fibHeap[T]) GetMin() *T {
return h.least.value
}
func (h *fibHeap[T]) IsEmpty() bool { return h.least == nil }
func (h *fibHeap[T]) Insert(v *T) {
ntree := &fibTree[T]{value: v}
h.trees = append(h.trees, ntree)
if h.least == nil || h.less(v, h.least.value) {
h.least = ntree
}
h.count++
}
func (h *fibHeap[T]) ExtractMin() *T {
smallest := h.least
if smallest != nil {
// Remove smallest from root trees.
for i := range h.trees {
pos := h.trees[i]
if pos == smallest {
h.trees[i] = h.trees[len(h.trees)-1]
h.trees = h.trees[:len(h.trees)-1]
break
}
}
// Add children to root
h.trees = append(h.trees, smallest.child...)
smallest.child = smallest.child[:0]
h.least = nil
if len(h.trees) > 0 {
h.consolidate()
}
h.count--
return smallest.value
}
return nil
}
func (h *fibHeap[T]) consolidate() {
aux := make([]*fibTree[T], bits.Len(h.count)+1)
for _, x := range h.trees {
order := len(x.child)
// consolidate the larger roots under smaller roots of same order until we have at most one tree per order.
for aux[order] != nil {
y := aux[order]
if h.less(y.value, x.value) {
x, y = y, x
}
x.addAtEnd(y)
aux[order] = nil
order++
}
aux[order] = x
}
h.trees = h.trees[:0]
// move ordered trees to root and find least node.
for _, k := range aux {
if k != nil {
k.parent = nil
h.trees = append(h.trees, k)
if h.least == nil || h.less(k.value, h.least.value) {
h.least = k
}
}
}
}
func (h *fibHeap[T]) Merge(a *fibHeap[T]) {
h.trees = append(h.trees, a.trees...)
h.count += a.count
if h.least == nil || a.least != nil && h.less(a.least.value, h.least.value) {
h.least = a.least
}
}
func (h *fibHeap[T]) find(fn func(*T) bool) *fibTree[T] {
var st []*fibTree[T]
st = append(st, h.trees...)
var tr *fibTree[T]
for len(st) > 0 {
tr, st = st[0], st[1:]
ro := *tr.value
if fn(&ro) {
break
}
st = append(st, tr.child...)
}
return tr
}
func (h *fibHeap[T]) Find(fn func(*T) bool) *T {
if needle := h.find(fn); needle != nil {
return needle.value
}
return nil
}
func (h *fibHeap[T]) DecreaseKey(find func(*T) bool, decrease func(*T)) {
needle := h.find(find)
if needle == nil {
return
}
decrease(needle.value)
if h.less(needle.value, h.least.value) {
h.least = needle
}
if parent := needle.parent; parent != nil {
if h.less(needle.value, parent.value) {
h.cut(needle)
h.cascadingCut(parent)
}
}
}
func (h *fibHeap[T]) cut(x *fibTree[T]) {
parent := x.parent
for i := range parent.child {
pos := parent.child[i]
if pos == x {
parent.child[i] = parent.child[len(parent.child)-1]
parent.child = parent.child[:len(parent.child)-1]
break
}
}
x.parent = nil
x.mark = false
h.trees = append(h.trees, x)
if h.less(x.value, h.least.value) {
h.least = x
}
}
func (h *fibHeap[T]) cascadingCut(y *fibTree[T]) {
if y.parent != nil {
if !y.mark {
y.mark = true
return
}
h.cut(y)
h.cascadingCut(y.parent)
}
}

27
set_test.go Normal file
View File

@@ -0,0 +1,27 @@
package aoc_test
import (
"sort"
"testing"
"github.com/matryer/is"
aoc "go.sour.is/advent-of-code"
)
func TestSet(t *testing.T) {
is := is.New(t)
s := aoc.Set(1, 2, 3)
is.True(!s.Has(0))
is.True(s.Has(1))
is.True(s.Has(2))
is.True(s.Has(3))
is.True(!s.Has(4))
s.Add(4)
is.True(s.Has(4))
items := s.Items()
sort.Ints(items)
is.Equal(items, []int{1, 2, 3, 4})
}