day1
This commit is contained in:
241
2025/go/utils/dijkstra/graph.go
Normal file
241
2025/go/utils/dijkstra/graph.go
Normal file
@@ -0,0 +1,241 @@
|
||||
package dijkstra
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type ItemGraph struct {
|
||||
Nodes []*Node
|
||||
Edges map[Node][]*Edge
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// AddNode adds a node to the graph
|
||||
func (g *ItemGraph) AddNode(n *Node) {
|
||||
g.lock.Lock()
|
||||
g.Nodes = append(g.Nodes, n)
|
||||
g.lock.Unlock()
|
||||
}
|
||||
|
||||
// AddEdge adds an edge to the graph
|
||||
func (g *ItemGraph) AddEdge(n1, n2 *Node, weight int) {
|
||||
g.lock.Lock()
|
||||
if g.Edges == nil {
|
||||
g.Edges = make(map[Node][]*Edge)
|
||||
}
|
||||
ed1 := Edge{
|
||||
Node: n2,
|
||||
Weight: weight,
|
||||
}
|
||||
|
||||
ed2 := Edge{
|
||||
Node: n1,
|
||||
Weight: weight,
|
||||
}
|
||||
g.Edges[*n1] = append(g.Edges[*n1], &ed1)
|
||||
g.Edges[*n2] = append(g.Edges[*n2], &ed2)
|
||||
g.lock.Unlock()
|
||||
}
|
||||
|
||||
// dijkstra implement
|
||||
func getShortestPath(startNode *Node, endNode *Node, g *ItemGraph) ([]Point, int) {
|
||||
visited := make(map[Point]bool)
|
||||
dist := make(map[Point]int)
|
||||
prev := make(map[Point]Point)
|
||||
//pq := make(PriorityQueue, 1)
|
||||
//heap.Init(&pq)
|
||||
q := NodeQueue{}
|
||||
pq := q.NewQ()
|
||||
start := Vertex{
|
||||
Node: startNode,
|
||||
Distance: 0,
|
||||
}
|
||||
for _, nval := range g.Nodes {
|
||||
dist[nval.Value] = math.MaxInt64
|
||||
}
|
||||
dist[startNode.Value] = start.Distance
|
||||
pq.Enqueue(start)
|
||||
//im := 0
|
||||
for !pq.IsEmpty() {
|
||||
v := pq.Dequeue()
|
||||
if visited[v.Node.Value] {
|
||||
continue
|
||||
}
|
||||
visited[v.Node.Value] = true
|
||||
near := g.Edges[*v.Node]
|
||||
|
||||
for _, val := range near {
|
||||
if !visited[val.Node.Value] {
|
||||
if dist[v.Node.Value]+val.Weight < dist[val.Node.Value] {
|
||||
store := Vertex{
|
||||
Node: val.Node,
|
||||
Distance: dist[v.Node.Value] + val.Weight,
|
||||
}
|
||||
dist[val.Node.Value] = dist[v.Node.Value] + val.Weight
|
||||
//prev[val.Node.Value] = fmt.Sprintf("->%s", v.Node.Value)
|
||||
prev[val.Node.Value] = v.Node.Value
|
||||
pq.Enqueue(store)
|
||||
}
|
||||
//visited[val.Node.value] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// fmt.Println(dist)
|
||||
// fmt.Println(prev)
|
||||
pathval := prev[endNode.Value]
|
||||
var finalArr []Point
|
||||
finalArr = append(finalArr, endNode.Value)
|
||||
for pathval != startNode.Value {
|
||||
finalArr = append(finalArr, pathval)
|
||||
pathval = prev[pathval]
|
||||
}
|
||||
finalArr = append(finalArr, pathval)
|
||||
// fmt.Println(finalArr)
|
||||
for i, j := 0, len(finalArr)-1; i < j; i, j = i+1, j-1 {
|
||||
finalArr[i], finalArr[j] = finalArr[j], finalArr[i]
|
||||
}
|
||||
return finalArr, dist[endNode.Value]
|
||||
|
||||
}
|
||||
|
||||
func getAllShortestPaths(startNode *Node, endNode *Node, g *ItemGraph) ([][]Point, int) {
|
||||
visited := make(map[Point]bool)
|
||||
dist := make(map[Point]int)
|
||||
prev := make(map[Point][]Point) // Store multiple predecessors for each node
|
||||
q := NodeQueue{}
|
||||
pq := q.NewQ()
|
||||
|
||||
start := Vertex{
|
||||
Node: startNode,
|
||||
Distance: 0,
|
||||
}
|
||||
|
||||
for _, n := range g.Nodes {
|
||||
dist[n.Value] = math.MaxInt64
|
||||
}
|
||||
dist[startNode.Value] = start.Distance
|
||||
pq.Enqueue(start)
|
||||
|
||||
// Perform Dijkstra's algorithm
|
||||
for !pq.IsEmpty() {
|
||||
v := pq.Dequeue()
|
||||
if visited[v.Node.Value] {
|
||||
continue
|
||||
}
|
||||
visited[v.Node.Value] = true
|
||||
near := g.Edges[*v.Node]
|
||||
|
||||
for _, edge := range near {
|
||||
alt := dist[v.Node.Value] + edge.Weight
|
||||
|
||||
// Case 1: Found a shorter path
|
||||
if alt < dist[edge.Node.Value] {
|
||||
dist[edge.Node.Value] = alt
|
||||
prev[edge.Node.Value] = []Point{v.Node.Value} // Reset predecessors
|
||||
pq.Enqueue(Vertex{
|
||||
Node: edge.Node,
|
||||
Distance: alt,
|
||||
})
|
||||
}
|
||||
|
||||
// Case 2: Found an equally short path
|
||||
if alt == dist[edge.Node.Value] {
|
||||
// Add the current node as a valid predecessor if not already present
|
||||
found := false
|
||||
for _, p := range prev[edge.Node.Value] {
|
||||
if p == v.Node.Value {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
prev[edge.Node.Value] = append(prev[edge.Node.Value], v.Node.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Iteratively reconstruct all paths
|
||||
var paths [][]Point
|
||||
stack := []struct {
|
||||
node Point
|
||||
path []Point
|
||||
}{
|
||||
{node: endNode.Value, path: []Point{}},
|
||||
}
|
||||
|
||||
for len(stack) > 0 {
|
||||
// Pop the top item from the stack
|
||||
current := stack[len(stack)-1]
|
||||
stack = stack[:len(stack)-1]
|
||||
|
||||
// Prepend the current node to the path
|
||||
newPath := append([]Point{current.node}, current.path...)
|
||||
|
||||
// If we've reached the start node, save the path
|
||||
if current.node.X == startNode.Value.X && current.node.Y == startNode.Value.Y {
|
||||
paths = append(paths, newPath)
|
||||
continue
|
||||
}
|
||||
|
||||
// Push all predecessors onto the stack
|
||||
for _, predecessor := range prev[current.node] {
|
||||
if !Contains(newPath, predecessor) {
|
||||
stack = append(stack, struct {
|
||||
node Point
|
||||
path []Point
|
||||
}{
|
||||
node: predecessor,
|
||||
path: newPath,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return paths, dist[endNode.Value]
|
||||
}
|
||||
|
||||
func Contains(slice []Point, value Point) bool {
|
||||
for _, v := range slice {
|
||||
if v == value { // Compare values
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CreateGraph(data InputGraph) *ItemGraph {
|
||||
var g ItemGraph
|
||||
nodes := make(map[Point]*Node)
|
||||
for _, v := range data.Graph {
|
||||
if _, found := nodes[v.Source]; !found {
|
||||
nA := Node{v.Source}
|
||||
nodes[v.Source] = &nA
|
||||
g.AddNode(&nA)
|
||||
}
|
||||
if _, found := nodes[v.Destination]; !found {
|
||||
nA := Node{v.Destination}
|
||||
nodes[v.Destination] = &nA
|
||||
g.AddNode(&nA)
|
||||
}
|
||||
g.AddEdge(nodes[v.Source], nodes[v.Destination], v.Weight)
|
||||
}
|
||||
return &g
|
||||
}
|
||||
|
||||
func GetShortestPath(from, to Point, g *ItemGraph) ([]Point, int) {
|
||||
nA := &Node{from}
|
||||
nB := &Node{to}
|
||||
|
||||
path, distance := getShortestPath(nA, nB, g)
|
||||
return path, distance
|
||||
}
|
||||
|
||||
func GetAllShortestPaths(from, to Point, g *ItemGraph) ([][]Point, int) {
|
||||
nA := &Node{from}
|
||||
nB := &Node{to}
|
||||
|
||||
paths, distance := getAllShortestPaths(nA, nB, g)
|
||||
return paths, distance
|
||||
}
|
||||
35
2025/go/utils/dijkstra/object.go
Normal file
35
2025/go/utils/dijkstra/object.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package dijkstra
|
||||
|
||||
type Point struct {
|
||||
X int
|
||||
Y int
|
||||
Label string
|
||||
}
|
||||
|
||||
type Node struct {
|
||||
Value Point
|
||||
}
|
||||
|
||||
type Edge struct {
|
||||
Node *Node
|
||||
Weight int
|
||||
}
|
||||
|
||||
type Vertex struct {
|
||||
Node *Node
|
||||
Distance int
|
||||
}
|
||||
|
||||
type PriorityQueue []*Vertex
|
||||
|
||||
type InputGraph struct {
|
||||
Graph []InputData
|
||||
From Point
|
||||
To Point
|
||||
}
|
||||
|
||||
type InputData struct {
|
||||
Source Point
|
||||
Destination Point
|
||||
Weight int
|
||||
}
|
||||
65
2025/go/utils/dijkstra/queue.go
Normal file
65
2025/go/utils/dijkstra/queue.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package dijkstra
|
||||
|
||||
import "sync"
|
||||
|
||||
type NodeQueue struct {
|
||||
Items []Vertex
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// Enqueue adds an Node to the end of the queue
|
||||
func (s *NodeQueue) Enqueue(t Vertex) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if len(s.Items) == 0 {
|
||||
s.Items = append(s.Items, t)
|
||||
return
|
||||
}
|
||||
var insertFlag bool
|
||||
for k, v := range s.Items {
|
||||
// add vertex distance less than travers's vertex distance
|
||||
if t.Distance < v.Distance {
|
||||
s.Items = append(s.Items[:k+1], s.Items[k:]...)
|
||||
s.Items[k] = t
|
||||
insertFlag = true
|
||||
}
|
||||
if insertFlag {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !insertFlag {
|
||||
s.Items = append(s.Items, t)
|
||||
}
|
||||
}
|
||||
|
||||
// Dequeue removes an Node from the start of the queue
|
||||
func (s *NodeQueue) Dequeue() *Vertex {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
item := s.Items[0]
|
||||
s.Items = s.Items[1:len(s.Items)]
|
||||
return &item
|
||||
}
|
||||
|
||||
//NewQ Creates New Queue
|
||||
func (s *NodeQueue) NewQ() *NodeQueue {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
s.Items = []Vertex{}
|
||||
return s
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the queue is empty
|
||||
func (s *NodeQueue) IsEmpty() bool {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
return len(s.Items) == 0
|
||||
}
|
||||
|
||||
// Size returns the number of Nodes in the queue
|
||||
func (s *NodeQueue) Size() int {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
return len(s.Items)
|
||||
}
|
||||
77
2025/go/utils/grid2d/grid2d.go
Normal file
77
2025/go/utils/grid2d/grid2d.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package grid2d
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"adventofcode2024/utils"
|
||||
)
|
||||
|
||||
type Grid[T any] struct {
|
||||
sizeX, sizeY int
|
||||
matrix [][]T
|
||||
empty T
|
||||
}
|
||||
|
||||
func NewGrid[T any](sizeX, sizeY int, empty T) *Grid[T] {
|
||||
matrix := make([][]T, sizeY)
|
||||
rows := make([]T, sizeX*sizeY)
|
||||
for i := 0; i < sizeX*sizeY; i++ {
|
||||
rows[i] = empty
|
||||
}
|
||||
|
||||
j := 0
|
||||
for i := 0; i < sizeY; i++ {
|
||||
matrix[i] = rows[j : j+sizeX : j+sizeX]
|
||||
j += sizeX
|
||||
}
|
||||
return &Grid[T]{
|
||||
sizeX: sizeX,
|
||||
sizeY: sizeY,
|
||||
matrix: matrix,
|
||||
empty: empty,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Grid[T]) SizeX() int {
|
||||
return g.sizeX
|
||||
}
|
||||
|
||||
func (g *Grid[T]) SizeY() int {
|
||||
return g.sizeY
|
||||
}
|
||||
func (g *Grid[T]) Matrix() [][]T {
|
||||
return g.matrix
|
||||
}
|
||||
|
||||
func (g *Grid[T]) Get(x, y int) T {
|
||||
if x < 0 || x >= g.sizeX {
|
||||
return g.empty
|
||||
}
|
||||
if y < 0 || y >= g.sizeY {
|
||||
return g.empty
|
||||
}
|
||||
return g.matrix[y][x]
|
||||
}
|
||||
|
||||
func (g *Grid[T]) Set(x, y int, v T) {
|
||||
if x < 0 || x >= g.sizeX {
|
||||
panic("invalid x")
|
||||
}
|
||||
if y < 0 || y >= g.sizeY {
|
||||
panic("invalid y")
|
||||
}
|
||||
g.matrix[y][x] = v
|
||||
}
|
||||
|
||||
func (g *Grid[T]) StringWithFormatter(formatter func(T, int, int) string) string {
|
||||
var r strings.Builder
|
||||
for j := 0; j < g.sizeY; j++ {
|
||||
for i := 0; i < g.sizeX; i++ {
|
||||
_, err := r.WriteString(formatter(g.matrix[j][i], i, j))
|
||||
utils.PanicOnErr(err)
|
||||
}
|
||||
_, err := r.WriteRune('\n')
|
||||
utils.PanicOnErr(err)
|
||||
}
|
||||
return r.String()
|
||||
}
|
||||
63
2025/go/utils/inputFile.go
Normal file
63
2025/go/utils/inputFile.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
func GenInputFile(day int) string {
|
||||
var d string
|
||||
if day < 10 {
|
||||
d = fmt.Sprintf("0%d", day)
|
||||
} else {
|
||||
d = fmt.Sprintf("%d", day)
|
||||
}
|
||||
|
||||
pwd, _ := os.Getwd()
|
||||
path := fmt.Sprintf("%s/day%s/input.txt", pwd, d)
|
||||
fi, _ := os.Stat(path)
|
||||
if fi != nil {
|
||||
return path
|
||||
}
|
||||
|
||||
fmt.Printf("Creating new input file %v...", path)
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
defer f.Close()
|
||||
data := readHttp(2025, day)
|
||||
_, err := f.WriteString(data)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func readHttp(year, day int) string {
|
||||
fmt.Println("Fetching data into file...")
|
||||
|
||||
url := fmt.Sprintf("https://adventofcode.com/%d/day/%d/input", year, day)
|
||||
session := os.Getenv("sessionAoC")
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
req.AddCookie(&http.Cookie{Name: "session", Value: session})
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(body)
|
||||
}
|
||||
|
||||
56
2025/go/utils/inputs/inputs.go
Normal file
56
2025/go/utils/inputs/inputs.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package inputs
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"adventofcode2024/utils"
|
||||
"adventofcode2024/utils/grid2d"
|
||||
sparsegrid "adventofcode2024/utils/sparseGrid"
|
||||
)
|
||||
|
||||
func ToInts(input string, sep string) []int {
|
||||
var r []int
|
||||
for _, line := range strings.Split(input, sep) {
|
||||
if line != "" {
|
||||
r = append(r, utils.MustAtoi(line))
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func ToGrid2D[T any](input, rowSep, colSep string, empty T, conv func(string) T) *grid2d.Grid[T] {
|
||||
|
||||
var width int
|
||||
|
||||
lines := strings.Split(input, rowSep)
|
||||
|
||||
if colSep == "" {
|
||||
// If no colSep, width is the length of each line
|
||||
width = len(lines[0])
|
||||
} else {
|
||||
// Use colSep to determine the width of the grid
|
||||
width = len(strings.Split(lines[0], colSep))
|
||||
}
|
||||
|
||||
grid := grid2d.NewGrid(width, len(lines), empty)
|
||||
for y, line := range lines {
|
||||
for x, v := range strings.Split(line, colSep) {
|
||||
grid.Set(x, y, conv(v))
|
||||
}
|
||||
}
|
||||
|
||||
return grid
|
||||
}
|
||||
|
||||
func ToSparseGrid[T comparable](input, rowSep, colSep string, empty T, conv func(string) T) *sparsegrid.SparseGrid[T] {
|
||||
lines := strings.Split(input, rowSep)
|
||||
|
||||
grid := sparsegrid.NewGrid(empty)
|
||||
for y, line := range lines {
|
||||
for x, v := range strings.Split(line, colSep) {
|
||||
grid.Set(x, y, conv(v))
|
||||
}
|
||||
}
|
||||
|
||||
return grid
|
||||
}
|
||||
47
2025/go/utils/memo/memo.go
Normal file
47
2025/go/utils/memo/memo.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package memo
|
||||
|
||||
import "sync"
|
||||
|
||||
type Func func(key interface{}) interface{}
|
||||
|
||||
type result struct {
|
||||
value interface{}
|
||||
}
|
||||
|
||||
type Memo struct {
|
||||
f Func
|
||||
cache map[interface{}]result
|
||||
mu sync.RWMutex // Allows concurrent reads.
|
||||
}
|
||||
|
||||
func New(f Func) *Memo {
|
||||
return &Memo{
|
||||
f: f,
|
||||
cache: make(map[interface{}]result),
|
||||
}
|
||||
}
|
||||
|
||||
func (memo *Memo) Get(key interface{}) interface{} {
|
||||
// First, try to read the cache using a read lock.
|
||||
memo.mu.RLock()
|
||||
res, ok := memo.cache[key]
|
||||
memo.mu.RUnlock()
|
||||
if ok {
|
||||
return res.value
|
||||
}
|
||||
|
||||
// Compute the result without holding the lock.
|
||||
computed := memo.f(key)
|
||||
|
||||
// Now acquire a write lock to update the cache.
|
||||
memo.mu.Lock()
|
||||
// Double-check: another goroutine may have stored the result in the meantime.
|
||||
res, ok = memo.cache[key]
|
||||
if !ok {
|
||||
res.value = computed
|
||||
memo.cache[key] = res
|
||||
}
|
||||
memo.mu.Unlock()
|
||||
|
||||
return res.value
|
||||
}
|
||||
56
2025/go/utils/ringbuffer/ringbuffer.go
Normal file
56
2025/go/utils/ringbuffer/ringbuffer.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package ringbuffer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type RingBuffer[T any] struct {
|
||||
buffer []T
|
||||
size int
|
||||
mu sync.Mutex
|
||||
write int
|
||||
count int
|
||||
}
|
||||
|
||||
// NewRingBuffer creates a new ring buffer with a fixed size.
|
||||
func NewRingBuffer[T any](size int) *RingBuffer[T] {
|
||||
return &RingBuffer[T]{
|
||||
buffer: make([]T, size),
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
||||
// Add inserts a new element into the buffer, overwriting the oldest if full.
|
||||
func (rb *RingBuffer[T]) Add(value T) {
|
||||
rb.mu.Lock()
|
||||
defer rb.mu.Unlock()
|
||||
|
||||
rb.buffer[rb.write] = value
|
||||
rb.write = (rb.write + 1) % rb.size
|
||||
|
||||
if rb.count < rb.size {
|
||||
rb.count++
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the contents of the buffer in FIFO order.
|
||||
func (rb *RingBuffer[T]) Get() []T {
|
||||
rb.mu.Lock()
|
||||
defer rb.mu.Unlock()
|
||||
|
||||
result := make([]T, 0, rb.count)
|
||||
|
||||
for i := 0; i < rb.count; i++ {
|
||||
index := (rb.write + rb.size - rb.count + i) % rb.size
|
||||
result = append(result, rb.buffer[index])
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Len returns the current number of elements in the buffer.
|
||||
func (rb *RingBuffer[T]) Len() int {
|
||||
rb.mu.Lock()
|
||||
defer rb.mu.Unlock()
|
||||
return rb.count
|
||||
}
|
||||
81
2025/go/utils/sparseGrid/sparseGrid.go
Normal file
81
2025/go/utils/sparseGrid/sparseGrid.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package sparsegrid
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"adventofcode2024/utils"
|
||||
)
|
||||
|
||||
type SparseGrid[T comparable] struct {
|
||||
minX, maxX, minY, maxY int
|
||||
data map[string]T
|
||||
empty T
|
||||
}
|
||||
|
||||
func NewGrid[T comparable](empty T) *SparseGrid[T] {
|
||||
return &SparseGrid[T]{
|
||||
minX: utils.MaxInt,
|
||||
maxX: utils.MinInt,
|
||||
minY: utils.MaxInt,
|
||||
maxY: utils.MinInt,
|
||||
data: map[string]T{},
|
||||
empty: empty,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *SparseGrid[T]) SizeX() (int, int) {
|
||||
return g.minX, g.maxX
|
||||
}
|
||||
|
||||
func (g *SparseGrid[T]) SizeY() (int, int) {
|
||||
return g.minY, g.maxY
|
||||
}
|
||||
|
||||
func (g *SparseGrid[T]) Visited() int {
|
||||
return len(g.data)
|
||||
}
|
||||
|
||||
func (g *SparseGrid[T]) Get(x, y int) T {
|
||||
k := key(x, y)
|
||||
v, ok := g.data[k]
|
||||
if !ok {
|
||||
return g.empty
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (g *SparseGrid[T]) Set(x, y int, v T) {
|
||||
k := key(x, y)
|
||||
current, ok := g.data[k]
|
||||
if ok && v == current {
|
||||
return
|
||||
} else if !ok && v == g.empty {
|
||||
return
|
||||
} else if v == g.empty {
|
||||
delete(g.data, k)
|
||||
} else {
|
||||
g.data[k] = v
|
||||
g.minX = utils.Min(g.minX, x)
|
||||
g.maxX = utils.Max(g.maxX, x)
|
||||
g.minY = utils.Min(g.minY, y)
|
||||
g.maxY = utils.Max(g.maxY, y)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *SparseGrid[T]) StringWithFormatter(formatter func(T, int, int) string) string {
|
||||
var r strings.Builder
|
||||
for j := g.minY; j <= g.maxY; j++ {
|
||||
for i := g.minX; i <= g.maxX; i++ {
|
||||
_, err := r.WriteString(formatter(g.Get(i, j), i, j))
|
||||
utils.PanicOnErr(err)
|
||||
}
|
||||
_, err := r.WriteRune('\n')
|
||||
utils.PanicOnErr(err)
|
||||
}
|
||||
return r.String()
|
||||
}
|
||||
|
||||
func key(x, y int) string {
|
||||
return fmt.Sprintf("%d:%d", x, y)
|
||||
}
|
||||
222
2025/go/utils/utils.go
Normal file
222
2025/go/utils/utils.go
Normal file
@@ -0,0 +1,222 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
func PanicOnErr(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
const MaxInt = int(^uint(0) >> 1)
|
||||
const MinInt = ^MaxInt
|
||||
|
||||
func Max[T constraints.Ordered](a, b T) T {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func Min[T constraints.Ordered](a, b T) T {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func SliceMinMax[T constraints.Ordered](slice []T) (*T, *T) {
|
||||
if len(slice) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
min := &slice[0]
|
||||
max := &slice[0]
|
||||
for i, v := range slice {
|
||||
if v < *min {
|
||||
min = &slice[i]
|
||||
}
|
||||
if v > *max {
|
||||
max = &slice[i]
|
||||
}
|
||||
}
|
||||
return min, max
|
||||
}
|
||||
|
||||
func MustAtoi(s string) int {
|
||||
v, err := strconv.Atoi(s)
|
||||
PanicOnErr(err)
|
||||
return v
|
||||
}
|
||||
|
||||
// Returns key from map[T]int which has the max value
|
||||
func MapFindMax(m interface{}) interface{} {
|
||||
var maxK interface{} = nil
|
||||
var maxV = MinInt
|
||||
iter := reflect.ValueOf(m).MapRange()
|
||||
for iter.Next() {
|
||||
k := iter.Key()
|
||||
v := int(iter.Value().Int())
|
||||
if v > maxV {
|
||||
maxV = v
|
||||
maxK = k.Interface()
|
||||
}
|
||||
}
|
||||
return maxK
|
||||
}
|
||||
|
||||
// Returns key from map[T]int which has the min value
|
||||
func MapFindMin(m interface{}) interface{} {
|
||||
var minK interface{} = nil
|
||||
var minV = MaxInt
|
||||
iter := reflect.ValueOf(m).MapRange()
|
||||
for iter.Next() {
|
||||
k := iter.Key()
|
||||
v := int(iter.Value().Int())
|
||||
if v < minV {
|
||||
minV = v
|
||||
minK = k.Interface()
|
||||
}
|
||||
}
|
||||
return minK
|
||||
}
|
||||
|
||||
func Readfile(day int) string {
|
||||
filename := fmt.Sprintf("day%02d/input.txt", day)
|
||||
file, err := os.Open(filename)
|
||||
PanicOnErr(err)
|
||||
defer file.Close()
|
||||
|
||||
reader := bufio.NewReader(file)
|
||||
contents, err := io.ReadAll(reader)
|
||||
PanicOnErr(err)
|
||||
|
||||
return strings.TrimSuffix(string(contents), "\n")
|
||||
}
|
||||
|
||||
func ReadfileAsSlice(day int) []string {
|
||||
filename := fmt.Sprintf("day%02d/input.txt", day)
|
||||
file, err := os.Open(filename)
|
||||
PanicOnErr(err)
|
||||
defer file.Close()
|
||||
|
||||
arr := make([]string, 0)
|
||||
sc := bufio.NewScanner(file)
|
||||
for sc.Scan() {
|
||||
arr = append(arr, sc.Text())
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
|
||||
func ParseToStruct(re *regexp.Regexp, input string, target interface{}) bool {
|
||||
m := re.FindStringSubmatch(input)
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
var useOffset bool
|
||||
|
||||
for i, name := range re.SubexpNames() {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
var field reflect.Value
|
||||
if name == "" {
|
||||
// use offset
|
||||
if i == 1 {
|
||||
useOffset = true
|
||||
} else if !useOffset {
|
||||
panic("can't mix named and unnamed subexpressions")
|
||||
}
|
||||
field = reflect.ValueOf(target).Elem().Field(i - 1)
|
||||
} else {
|
||||
// use name
|
||||
if i == 1 {
|
||||
useOffset = false
|
||||
} else if useOffset {
|
||||
panic("can't mix named and unnamed subexpressions")
|
||||
}
|
||||
field = reflect.ValueOf(target).Elem().FieldByName(name)
|
||||
}
|
||||
if field.Kind() == reflect.String {
|
||||
field.SetString(m[i])
|
||||
} else if field.Kind() == reflect.Int {
|
||||
v, err := strconv.Atoi(m[i])
|
||||
PanicOnErr(err)
|
||||
field.SetInt(int64(v))
|
||||
} else if field.Kind() == reflect.Uint8 {
|
||||
if len(m[i]) != 1 {
|
||||
panic(fmt.Sprintf("expecting 1 char, got: %s", m[i]))
|
||||
}
|
||||
field.SetUint(uint64(m[i][0]))
|
||||
} else {
|
||||
panic(fmt.Sprintf("unknown kind: %s", field.Kind()))
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func MustParseToStruct(re *regexp.Regexp, input string, target interface{}) {
|
||||
if !ParseToStruct(re, input, target) {
|
||||
panic(fmt.Errorf("failed to parse: %s", input))
|
||||
}
|
||||
}
|
||||
|
||||
func CharToLower(c byte) byte {
|
||||
return strings.ToLower(string(c))[0]
|
||||
}
|
||||
|
||||
func CharToUpper(c byte) byte {
|
||||
return strings.ToUpper(string(c))[0]
|
||||
}
|
||||
|
||||
func Contains(haystack []string, needle string) bool {
|
||||
for _, s := range haystack {
|
||||
if s == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func Abs[T constraints.Signed](x T) T {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func Gcd(x, y int) int {
|
||||
if x <= 0 || y <= 0 {
|
||||
panic(fmt.Errorf("invalid input: %d, %d", x, y))
|
||||
}
|
||||
if x == y {
|
||||
return x
|
||||
}
|
||||
if x > y {
|
||||
return Gcd(x-y, y)
|
||||
} else {
|
||||
return Gcd(x, y-x)
|
||||
}
|
||||
}
|
||||
|
||||
func Sign[T constraints.Signed](x T) int {
|
||||
if x < 0 {
|
||||
return -1
|
||||
} else if x > 0 {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user