Compare commits
9
Commits
e611d48ab7
...
hackerrank
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5ebbe25d0
|
||
|
|
50af2114d4
|
||
|
|
f8fa61672f
|
||
|
|
951c2c298a
|
||
|
|
328a0f3eb3
|
||
|
|
7d7402f054
|
||
|
|
7585526634
|
||
|
|
924c8d74f3
|
||
|
|
22184ed9c7
|
No files matched your search
@@ -0,0 +1,6 @@
|
|||||||
|
-----BEGIN SSH SIGNATURE-----
|
||||||
|
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgZ+OuJYdd3UiUbyBuO1RlsQR20a
|
||||||
|
Qm5mKneuMxRjGo3zkAAAAEZmlsZQAAAAAAAAAGc2hhNTEyAAAAUwAAAAtzc2gtZWQyNTUx
|
||||||
|
OQAAAED8T4C6WILXYZ1KxqDIlVhlrAEjr1Vc+tn8ypcVM3bN7iOexVvuUuvm90nr8eEwKU
|
||||||
|
acrdDxmq2S+oysQbK+pMUE
|
||||||
|
-----END SSH SIGNATURE-----
|
||||||
+185
-102
@@ -9,66 +9,6 @@ import (
|
|||||||
aoc "go.sour.is/advent-of-code"
|
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) {
|
func TestList(t *testing.T) {
|
||||||
is := is.New(t)
|
is := is.New(t)
|
||||||
@@ -85,62 +25,44 @@ func TestPriorityQueue(t *testing.T) {
|
|||||||
is := is.New(t)
|
is := is.New(t)
|
||||||
|
|
||||||
type elem [2]int
|
type elem [2]int
|
||||||
less := func(a, b elem) bool {
|
less := func(b, a *elem) bool {
|
||||||
return a[0] < b[0]
|
return (*a)[0] < (*b)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
pq := aoc.PriorityQueue(less)
|
pq := aoc.PriorityQueue(less)
|
||||||
|
|
||||||
pq.Enqueue(elem{1, 4})
|
pq.Insert(&elem{1, 4})
|
||||||
pq.Enqueue(elem{3, 2})
|
pq.Insert(&elem{3, 2})
|
||||||
pq.Enqueue(elem{2, 3})
|
pq.Insert(&elem{2, 3})
|
||||||
pq.Enqueue(elem{4, 1})
|
pq.Insert(&elem{4, 1})
|
||||||
|
|
||||||
v, ok := pq.Dequeue()
|
v := pq.ExtractMin()
|
||||||
is.True(ok)
|
is.True(v != nil)
|
||||||
is.Equal(v, elem{4, 1})
|
is.Equal(v, &elem{4, 1})
|
||||||
|
|
||||||
v, ok = pq.Dequeue()
|
v = pq.ExtractMin()
|
||||||
is.True(ok)
|
is.True(v != nil)
|
||||||
is.Equal(v, elem{3, 2})
|
is.Equal(v, &elem{3, 2})
|
||||||
|
|
||||||
v, ok = pq.Dequeue()
|
v = pq.ExtractMin()
|
||||||
is.True(ok)
|
is.True(v != nil)
|
||||||
is.Equal(v, elem{2, 3})
|
is.Equal(v, &elem{2, 3})
|
||||||
|
|
||||||
v, ok = pq.Dequeue()
|
v = pq.ExtractMin()
|
||||||
is.True(ok)
|
is.True(v != nil)
|
||||||
is.Equal(v, elem{1, 4})
|
is.Equal(v, &elem{1, 4})
|
||||||
|
|
||||||
v, ok = pq.Dequeue()
|
v = pq.ExtractMin()
|
||||||
is.True(!ok)
|
is.True(v == nil)
|
||||||
is.Equal(v, elem{})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
func ExamplePriorityQueue() {
|
||||||
type memo struct {
|
type memo struct {
|
||||||
pt int
|
pt int
|
||||||
score 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{
|
adj := map[int][][2]int{
|
||||||
0: {{1, 2}, {2, 6}},
|
0: {{1, 2}, {2, 6}},
|
||||||
@@ -156,10 +78,10 @@ func ExamplePriorityQueue() {
|
|||||||
dist := aoc.DefaultMap[int](int(^uint(0) >> 1))
|
dist := aoc.DefaultMap[int](int(^uint(0) >> 1))
|
||||||
|
|
||||||
dist.Set(0, 0)
|
dist.Set(0, 0)
|
||||||
pq.Enqueue(memo{0, 0})
|
pq.Insert(&memo{0, 0})
|
||||||
|
|
||||||
for !pq.IsEmpty() {
|
for !pq.IsEmpty() {
|
||||||
m, _ := pq.Dequeue()
|
m := pq.ExtractMin()
|
||||||
|
|
||||||
u := m.pt
|
u := m.pt
|
||||||
if visited.Has(u) {
|
if visited.Has(u) {
|
||||||
@@ -175,7 +97,7 @@ func ExamplePriorityQueue() {
|
|||||||
|
|
||||||
if !visited.Has(v) && du+w < dv {
|
if !visited.Has(v) && du+w < dv {
|
||||||
dist.Set(v, du+w)
|
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 5 is 22 steps away.
|
||||||
// point 6 is 19 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})
|
||||||
|
}
|
||||||
+61
-26
@@ -29,10 +29,10 @@ func run(scan *bufio.Scanner) (*result, error) {
|
|||||||
log("start day 17")
|
log("start day 17")
|
||||||
|
|
||||||
result := result{}
|
result := result{}
|
||||||
result.valuePT1 = search(m, 1, 3)
|
result.valuePT1 = search(m, 1, 3, seenFn)
|
||||||
log("result from part 1 = ", result.valuePT1)
|
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)
|
log("result from part 2 = ", result.valuePT2)
|
||||||
|
|
||||||
return &result, nil
|
return &result, nil
|
||||||
@@ -90,6 +90,7 @@ type graph struct {
|
|||||||
m Map
|
m Map
|
||||||
target Point
|
target Point
|
||||||
reads int
|
reads int
|
||||||
|
seenFn func(a position) position
|
||||||
}
|
}
|
||||||
|
|
||||||
// Neighbors returns valid steps from given position. if at target returns none.
|
// 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) {
|
if forward := current.step(); current.steps < g.max && g.m.Valid(forward.loc) {
|
||||||
nbs = append(nbs, forward)
|
nbs = append(nbs, forward)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nbs
|
return nbs
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,12 +131,13 @@ func (g *graph) Cost(a, b position) int16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Potential calculates distance to target
|
// Potential calculates distance to target
|
||||||
func (g *graph) Potential(a, b position) int16 {
|
// func (g *graph) Potential(a position) int16 {
|
||||||
return aoc.ManhattanDistance(a.loc, b.loc)
|
// return aoc.ManhattanDistance(a.loc, g.target)
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (g *graph) Target(a position) bool {
|
// Target returns true when target reached. receives node and cost.
|
||||||
if a.loc == g.target && a.steps >= g.min {
|
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 true
|
||||||
}
|
}
|
||||||
return false
|
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.
|
// Seen attempt at simplifying the seen to use horizontal/vertical and no steps.
|
||||||
// It returns correct for part1 but not part 2..
|
// It returns correct for part1 but not part 2..
|
||||||
// func (g *graph) Seen(a position) position {
|
func (g *graph) Seen(a position) position {
|
||||||
// if a.direction == U {
|
if g.seenFn != nil {
|
||||||
// a.direction = D
|
return g.seenFn(a)
|
||||||
// }
|
}
|
||||||
// if a.direction == L {
|
return a
|
||||||
// a.direction = R
|
}
|
||||||
// }
|
|
||||||
// a.steps = 0
|
|
||||||
// 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()
|
rows, cols := m.Size()
|
||||||
start := Point{}
|
start := Point{}
|
||||||
target := Point{rows - 1, cols - 1}
|
target := Point{rows - 1, cols - 1}
|
||||||
|
|
||||||
g := graph{min: minSteps, max: maxSteps, m: m, target: target}
|
g := graph{min: minSteps, max: maxSteps, m: m, target: target, seenFn: seenFn}
|
||||||
cost, path := aoc.FindPath[int16, position](&g, position{loc: start}, position{loc: target})
|
|
||||||
|
|
||||||
log("total map reads = ", g.reads)
|
cost, path, closed := aoc.FindPath[int16, position](&g, position{loc: start}, position{loc: target})
|
||||||
printGraph(m, path)
|
|
||||||
|
log("total map reads = ", g.reads, "cost = ", cost)
|
||||||
|
printGraph(m, path, closed, g.seenFn)
|
||||||
|
|
||||||
return int(cost)
|
return int(cost)
|
||||||
}
|
}
|
||||||
|
|
||||||
// printGraph with the path overlay
|
// printGraph with the path/cost overlay
|
||||||
func printGraph(m Map, path []position) {
|
func printGraph(m Map, path []position, closed map[position]int16, seenFn func(a position) position) {
|
||||||
pts := make(map[Point]position, len(path))
|
pts := make(map[Point]position, len(path))
|
||||||
for _, pt := range path {
|
for _, pt := range path {
|
||||||
pts[pt.loc] = pt
|
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 {
|
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 {
|
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("*")
|
fmt.Print("*")
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Print(".")
|
// fmt.Print(" ....")
|
||||||
|
fmt.Print(" ")
|
||||||
}
|
}
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-9
@@ -28,14 +28,14 @@ func TestExample(t *testing.T) {
|
|||||||
is.Equal(result.valuePT2, 94)
|
is.Equal(result.valuePT2, 94)
|
||||||
}
|
}
|
||||||
|
|
||||||
// func TestSolution(t *testing.T) {
|
func TestSolution(t *testing.T) {
|
||||||
// is := is.New(t)
|
is := is.New(t)
|
||||||
// scan := bufio.NewScanner(bytes.NewReader(input))
|
scan := bufio.NewScanner(bytes.NewReader(input))
|
||||||
|
|
||||||
// result, err := run(scan)
|
result, err := run(scan)
|
||||||
// is.NoErr(err)
|
is.NoErr(err)
|
||||||
|
|
||||||
// t.Log(result)
|
t.Log(result)
|
||||||
// is.Equal(result.valuePT1, 843)
|
is.Equal(result.valuePT1, 843)
|
||||||
// is.Equal(result.valuePT2, 1017)
|
is.Equal(result.valuePT2, 1017)
|
||||||
// }
|
}
|
||||||
+6
-6
@@ -186,8 +186,8 @@ func solveWorkflow(parts []part, workflows map[string][]rule) int {
|
|||||||
|
|
||||||
func solveRanges(workflows map[string][]rule) uint {
|
func solveRanges(workflows map[string][]rule) uint {
|
||||||
|
|
||||||
pq := aoc.PriorityQueue(func(a, b queue) bool { return false })
|
pq := aoc.PriorityQueue(func(a, b *queue) bool { return false })
|
||||||
pq.Enqueue(queue{
|
pq.Insert(&queue{
|
||||||
"in",
|
"in",
|
||||||
block{
|
block{
|
||||||
ranger{1, 4000},
|
ranger{1, 4000},
|
||||||
@@ -200,9 +200,9 @@ func solveRanges(workflows map[string][]rule) uint {
|
|||||||
// var rejected []block
|
// var rejected []block
|
||||||
|
|
||||||
for !pq.IsEmpty() {
|
for !pq.IsEmpty() {
|
||||||
current, _ := pq.Dequeue()
|
current := pq.ExtractMin()
|
||||||
for _, rule := range workflows[current.name] {
|
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 {
|
switch rule.match {
|
||||||
case "x":
|
case "x":
|
||||||
@@ -223,14 +223,14 @@ func solveRanges(workflows map[string][]rule) uint {
|
|||||||
accepted = append(accepted, next.block)
|
accepted = append(accepted, next.block)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
pq.Enqueue(next)
|
pq.Insert(next)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var sum uint
|
var sum uint
|
||||||
for _, a := range accepted {
|
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
|
return sum
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
module go.sour.is/advent-of-code
|
module go.sour.is/advent-of-code
|
||||||
|
|
||||||
go 1.21.4
|
go 1.22.0
|
||||||
|
|
||||||
toolchain go1.21.5
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/matryer/is v1.4.1
|
github.com/matryer/is v1.4.1
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
package aoc
|
package aoc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmp"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
)
|
||||||
|
|
||||||
type Vector struct {
|
type Vector struct {
|
||||||
Offset Point[int]
|
Offset Point[int]
|
||||||
Scale int
|
Scale int
|
||||||
@@ -58,7 +67,7 @@ func NumPoints(outline []Point[int], borderLength int) int {
|
|||||||
|
|
||||||
type Map[I integer, T any] [][]T
|
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
|
var zero T
|
||||||
if !m.Valid(p) {
|
if !m.Valid(p) {
|
||||||
return [2]I{0, 0}, zero, false
|
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
|
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 {
|
if m == nil || len(*m) == 0 {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
return I(len(*m)), I(len((*m)[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()
|
rows, cols := m.Size()
|
||||||
return p[0] >= 0 && p[0] < rows && p[1] >= 0 && p[1] < cols
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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) {}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
1000000 105823341
|
||||||
|
10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 10000 1Line truncated
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
8 90
|
||||||
|
13 47 74 12 89 74 18 38
|
||||||
Whitespace-only changes.
@@ -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},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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})
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,26 +2,70 @@ package aoc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"runtime/pprof"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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) {
|
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")
|
Log("Usage:", filepath.Base(os.Args[0]), "FILE")
|
||||||
os.Exit(22)
|
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 {
|
if err != nil {
|
||||||
Log(err)
|
Log(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
scan := bufio.NewScanner(input)
|
scan := bufio.NewScanner(input)
|
||||||
|
|
||||||
return run(scan)
|
return run(scan)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package aoc
|
package aoc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/bits"
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
type priorityQueue[T any] struct {
|
type priorityQueue[T any] struct {
|
||||||
elems []T
|
elems []*T
|
||||||
less func(a, b T) bool
|
less func(a, b *T) bool
|
||||||
maxDepth int
|
maxDepth int
|
||||||
totalEnqueue int
|
totalEnqueue int
|
||||||
totalDequeue int
|
totalDequeue int
|
||||||
@@ -16,10 +17,10 @@ type priorityQueue[T any] struct {
|
|||||||
// less is the function for sorting. reverse a and b to reverse the sort.
|
// less is the function for sorting. reverse a and b to reverse the sort.
|
||||||
// T is the item
|
// T is the item
|
||||||
// U is a slice of T
|
// 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}
|
return &priorityQueue[T]{less: less}
|
||||||
}
|
}
|
||||||
func (pq *priorityQueue[T]) Enqueue(elem T) {
|
func (pq *priorityQueue[T]) Insert(elem *T) {
|
||||||
pq.totalEnqueue++
|
pq.totalEnqueue++
|
||||||
|
|
||||||
pq.elems = append(pq.elems, elem)
|
pq.elems = append(pq.elems, elem)
|
||||||
@@ -28,35 +29,41 @@ func (pq *priorityQueue[T]) Enqueue(elem T) {
|
|||||||
func (pq *priorityQueue[T]) IsEmpty() bool {
|
func (pq *priorityQueue[T]) IsEmpty() bool {
|
||||||
return len(pq.elems) == 0
|
return len(pq.elems) == 0
|
||||||
}
|
}
|
||||||
func (pq *priorityQueue[T]) Dequeue() (T, bool) {
|
func (pq *priorityQueue[T]) ExtractMin() *T {
|
||||||
pq.totalDequeue++
|
pq.totalDequeue++
|
||||||
|
|
||||||
var elem T
|
var elem *T
|
||||||
if pq.IsEmpty() {
|
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]
|
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.
|
// ManhattanDistance the distance between two points measured along axes at right angles.
|
||||||
func ManhattanDistance[T integer](a, b Point[T]) T {
|
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 {
|
type pather[C number, N comparable] interface {
|
||||||
|
// Neighbors returns all neighbors to node N that should be considered next.
|
||||||
Neighbors(N) []N
|
Neighbors(N) []N
|
||||||
|
|
||||||
|
// Cost returns
|
||||||
Cost(a, b N) C
|
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:
|
// OPTIONAL:
|
||||||
|
// Add heuristic for running as A* search.
|
||||||
|
// Potential(N) C
|
||||||
|
|
||||||
// Seen modify value used by seen pruning.
|
// Seen modify value used by seen pruning.
|
||||||
// Seen(N) N
|
// Seen(N) N
|
||||||
|
|
||||||
// Target returns true if target reached.
|
|
||||||
// Target(N) bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindPath uses the A* path finding algorithem.
|
// FindPath uses the A* path finding algorithem.
|
||||||
@@ -67,9 +74,18 @@ type pather[C number, N comparable] interface {
|
|||||||
//
|
//
|
||||||
// start, end are nodes that dileniate the start and end of the search path.
|
// 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.
|
// 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
|
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 {
|
type node struct {
|
||||||
cost C
|
cost C
|
||||||
@@ -78,7 +94,7 @@ func FindPath[C integer, N comparable](g pather[C, N], start, end N) (C, []N) {
|
|||||||
position N
|
position N
|
||||||
}
|
}
|
||||||
|
|
||||||
NewPath := func(n *node) []N {
|
newPath := func(n *node) []N {
|
||||||
var path []N
|
var path []N
|
||||||
for n.parent != nil {
|
for n.parent != nil {
|
||||||
path = append(path, n.position)
|
path = append(path, n.position)
|
||||||
@@ -90,61 +106,219 @@ func FindPath[C integer, N comparable](g pather[C, N], start, end N) (C, []N) {
|
|||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
less := func(a, b node) bool {
|
less := func(a, b *node) bool {
|
||||||
return b.cost+b.potential < a.cost+a.potential
|
return a.cost+a.potential < b.cost+b.potential
|
||||||
}
|
}
|
||||||
|
|
||||||
pq := PriorityQueue(less)
|
closed := make(map[N]C)
|
||||||
pq.Enqueue(node{position: start})
|
open := FibHeap(less)
|
||||||
closed[start] = false
|
|
||||||
|
|
||||||
defer func() {
|
open.Insert(&node{position: start, potential: potentialFn(start)})
|
||||||
Log("queue max depth = ", pq.maxDepth, "total enqueue = ", pq.totalEnqueue, "total dequeue = ", pq.totalDequeue)
|
closed[start] = zero
|
||||||
}()
|
|
||||||
|
|
||||||
var seenFn = func(a N) N { return a }
|
for !open.IsEmpty() {
|
||||||
if s, ok := g.(interface{ Seen(N) N }); ok {
|
current := open.ExtractMin()
|
||||||
seenFn = s.Seen
|
for _, nb := range g.Neighbors(current.position) {
|
||||||
}
|
next := &node{
|
||||||
|
|
||||||
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(¤t)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, nb := range g.Neighbors(n) {
|
|
||||||
seen := seenFn(nb)
|
|
||||||
if closed[seen] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
cost := g.Cost(n, nb) + current.cost
|
|
||||||
nextPath := node{
|
|
||||||
position: nb,
|
position: nb,
|
||||||
parent: ¤t,
|
parent: current,
|
||||||
cost: cost,
|
cost: g.Cost(current.position, nb) + current.cost,
|
||||||
potential: g.Potential(nb, end),
|
potential: potentialFn(nb),
|
||||||
}
|
}
|
||||||
// check if path is in open list
|
|
||||||
if _, open := closed[seen]; !open {
|
seen := seenFn(nb)
|
||||||
pq.Enqueue(nextPath)
|
cost, ok := closed[seen]
|
||||||
closed[seen] = false // add to open list
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
@@ -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})
|
||||||
|
}
|
||||||
Reference in new issue
Block a user