graphs #21
							
								
								
									
										39
									
								
								aoc_test.go
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								aoc_test.go
									
									
									
									
									
								
							@ -195,3 +195,42 @@ 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)
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										96
									
								
								grids.go
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								grids.go
									
									
									
									
									
								
							@ -1,5 +1,10 @@
 | 
			
		||||
package aoc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"cmp"
 | 
			
		||||
	"sort"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Vector struct {
 | 
			
		||||
	Offset Point[int]
 | 
			
		||||
	Scale  int
 | 
			
		||||
@ -76,3 +81,94 @@ 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 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
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"maps"
 | 
			
		||||
	"sort"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -41,6 +42,30 @@ func (pq *priorityQueue[T]) Dequeue() (T, bool) {
 | 
			
		||||
	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.
 | 
			
		||||
func ManhattanDistance[T integer](a, b Point[T]) T {
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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