diff --git a/aoc_test.go b/aoc_test.go index ae579c9..c723dd2 100644 --- a/aoc_test.go +++ b/aoc_test.go @@ -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) @@ -116,23 +56,6 @@ func TestPriorityQueue(t *testing.T) { 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 { @@ -194,26 +117,6 @@ func ExamplePriorityQueue() { // point 5 is 22 steps away. // point 6 is 19 steps away. } - -func TestStack(t *testing.T) { - is := is.New(t) - - s := aoc.Stack(1, 2, 3, 4) - is.True(!s.IsEmpty()) - is.Equal(s.Pop(), 4) - is.Equal(s.Pop(), 3) - is.Equal(s.Pop(), 2) - is.Equal(s.Pop(), 1) - is.True(s.IsEmpty()) - s.Push(4, 3, 2, 1) - is.True(!s.IsEmpty()) - is.Equal(s.Pop(), 1) - is.Equal(s.Pop(), 2) - is.Equal(s.Pop(), 3) - is.Equal(s.Pop(), 4) - is.True(s.IsEmpty()) -} - func TestGraph(t *testing.T) { is := is.New(t) diff --git a/grids.go b/grids.go index b8227f4..17fdb58 100644 --- a/grids.go +++ b/grids.go @@ -3,6 +3,8 @@ package aoc import ( "cmp" "sort" + + "golang.org/x/exp/maps" ) type Vector struct { @@ -82,6 +84,76 @@ func (m *Map[I, T]) Valid(p Point[I]) bool { 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, v := range nbs { + fmt.Fprintln(b, " ", to, k) + } + } + + 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]) @@ -117,7 +189,7 @@ func Graph[V any, W cmp.Ordered, C comparable](opts ...graphOption[V, W, C]) *gr return &g } func (g *graph[V, W, C]) AddVertex(id C, value V) { - (*g)[id] = &vertex[V,W]{Value: value} + (*g)[id] = &vertex[V, W]{Value: value} } func (g *graph[V, W, C]) AddEdge(from, to C, w W) { if g == nil { @@ -130,7 +202,7 @@ func (g *graph[V, W, C]) AddEdge(from, to C, w W) { return } - (*g)[from].Edges = append((*g)[from].Edges, edge[V,W]{(*g)[to], w}) + (*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 { @@ -170,5 +242,3 @@ func WithAdjacencyList[W cmp.Ordered, C comparable](list adjacencyList[C, C]) gr } } } - -// func GraphFromMap() diff --git a/itertools_test.go b/itertools_test.go new file mode 100644 index 0000000..db9ea78 --- /dev/null +++ b/itertools_test.go @@ -0,0 +1,47 @@ +package aoc_test + +import ( + "fmt" + "sort" + "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}, + }, + ) +} \ No newline at end of file diff --git a/math_test.go b/math_test.go new file mode 100644 index 0000000..14b2fd3 --- /dev/null +++ b/math_test.go @@ -0,0 +1,27 @@ +package aoc_test + + +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) +} \ No newline at end of file diff --git a/search.go b/search.go index c2dc67c..1895af4 100644 --- a/search.go +++ b/search.go @@ -42,30 +42,6 @@ func (pq *priorityQueue[T]) ExtractMin() *T { return elem } -type stack[T any] []T - -func Stack[T any](a ...T) *stack[T] { - var s stack[T] = a - return &s -} -func (s *stack[T]) Push(a ...T) { - if s == nil { - return - } - *s = append(*s, a...) -} -func (s *stack[T]) IsEmpty() bool { - return s == nil || len(*s) == 0 -} -func (s *stack[T]) Pop() T { - var a T - if s.IsEmpty() { - return a - } - a, *s = (*s)[len(*s)-1], (*s)[:len(*s)-1] - return a -} - // ManhattanDistance the distance between two points measured along axes at right angles. func ManhattanDistance[T integer](a, b Point[T]) T { return ABS(a[0]-b[0]) + ABS(a[1]-b[1]) diff --git a/set_test.go b/set_test.go new file mode 100644 index 0000000..16b12c9 --- /dev/null +++ b/set_test.go @@ -0,0 +1,20 @@ +package aoc_test + + +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}) +} \ No newline at end of file