graphs #21
							
								
								
									
										39
									
								
								aoc_test.go
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								aoc_test.go
									
									
									
									
									
								
							@ -195,3 +195,42 @@ 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 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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										102
									
								
								grids.go
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								grids.go
									
									
									
									
									
								
							@ -1,5 +1,10 @@
 | 
				
			|||||||
package aoc
 | 
					package aoc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"cmp"
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Vector struct {
 | 
					type Vector struct {
 | 
				
			||||||
	Offset Point[int]
 | 
						Offset Point[int]
 | 
				
			||||||
	Scale  int
 | 
						Scale  int
 | 
				
			||||||
@ -58,7 +63,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 +71,104 @@ 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 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
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// func GraphFromMap()
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										122
									
								
								search.go
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								search.go
									
									
									
									
									
								
							@ -1,6 +1,7 @@
 | 
				
			|||||||
package aoc
 | 
					package aoc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"maps"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -41,6 +42,30 @@ func (pq *priorityQueue[T]) Dequeue() (T, bool) {
 | 
				
			|||||||
	return elem, true
 | 
						return elem, true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.
 | 
					// 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[1]-b[1]) + ABS(a[0]-b[0])
 | 
				
			||||||
@ -148,3 +173,100 @@ func FindPath[C integer, N comparable](g pather[C, N], start, end N) (C, []N) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return zero, nil
 | 
						return zero, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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(¤t))
 | 
				
			||||||
 | 
								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:    ¤t,
 | 
				
			||||||
 | 
									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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user