generated from arol-polito/python-project-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
400 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"math" | ||
"os" | ||
"strconv" | ||
"strings" | ||
"time" | ||
) | ||
|
||
func check(e error) { | ||
if e != nil { | ||
panic(e) | ||
} | ||
} | ||
|
||
type Pos struct { | ||
r, c int | ||
} | ||
|
||
var dirSymbols map[Pos]string = map[Pos]string{ | ||
{0, 1}: ">", | ||
{0, -1}: "<", | ||
{1, 0}: "v", | ||
{-1, 0}: "^", | ||
} | ||
|
||
type PosPath struct { | ||
pos Pos | ||
path string | ||
} | ||
|
||
var numPad map[byte]Pos = map[byte]Pos{ | ||
'7': {0, 0}, | ||
'8': {0, 1}, | ||
'9': {0, 2}, | ||
'4': {1, 0}, | ||
'5': {1, 1}, | ||
'6': {1, 2}, | ||
'1': {2, 0}, | ||
'2': {2, 1}, | ||
'3': {2, 2}, | ||
'-': {3, 0}, | ||
'0': {3, 1}, | ||
'A': {3, 2}, | ||
} | ||
|
||
var dirPad map[byte]Pos = map[byte]Pos{ | ||
'-': {0, 0}, | ||
'^': {0, 1}, | ||
'A': {0, 2}, | ||
'<': {1, 0}, | ||
'v': {1, 1}, | ||
'>': {1, 2}, | ||
} | ||
|
||
func getNextDirs(from Pos, to Pos) []Pos { | ||
nextDirs := []Pos{} | ||
if to.r > from.r { | ||
nextDirs = append(nextDirs, Pos{1, 0}) | ||
} | ||
if to.r < from.r { | ||
nextDirs = append(nextDirs, Pos{-1, 0}) | ||
} | ||
if to.c > from.c { | ||
nextDirs = append(nextDirs, Pos{0, 1}) | ||
} | ||
if to.c < from.c { | ||
nextDirs = append(nextDirs, Pos{0, -1}) | ||
} | ||
return nextDirs | ||
} | ||
|
||
func getPadPaths(start byte, end byte, byteToPos map[byte]Pos) []string { | ||
posToByte := map[Pos]byte{} | ||
for k, v := range byteToPos { | ||
posToByte[v] = k | ||
} | ||
paths := []string{} | ||
startPosPath := PosPath{byteToPos[start], ""} | ||
q := []PosPath{startPosPath} | ||
for len(q) > 0 { | ||
curr := q[0] | ||
q = q[1:] | ||
|
||
if curr.pos == byteToPos[end] { | ||
paths = append(paths, curr.path+"A") | ||
continue | ||
} | ||
|
||
for _, dir := range getNextDirs(curr.pos, byteToPos[end]) { | ||
nextPos := Pos{curr.pos.r + dir.r, curr.pos.c + dir.c} | ||
dirSym := dirSymbols[dir] | ||
path := curr.path + dirSym | ||
nextPosPath := PosPath{nextPos, path} | ||
if posToByte[nextPos] != '-' && posToByte[nextPos] != start { | ||
q = append(q, nextPosPath) | ||
} | ||
} | ||
} | ||
return paths | ||
} | ||
|
||
type MemoKey struct { | ||
path string | ||
lvl int | ||
} | ||
|
||
func getShortestPath(seq string, lvl int, maxLvl int, memo *map[MemoKey]int, useNumPad bool) int { | ||
if lvl == maxLvl { | ||
return len(seq) | ||
} | ||
|
||
if val, ok := (*memo)[MemoKey{seq, lvl}]; ok { | ||
return val | ||
} | ||
|
||
start := 'A' | ||
totMinLen := 0 | ||
pad := dirPad | ||
if useNumPad { | ||
pad = numPad | ||
} | ||
for _, c := range seq { | ||
paths := getPadPaths(byte(start), byte(c), pad) | ||
min := math.MaxInt | ||
for _, p := range paths { | ||
l := getShortestPath(p, lvl+1, maxLvl, memo, false) | ||
if l < min { | ||
min = l | ||
} | ||
} | ||
totMinLen += min | ||
start = c | ||
} | ||
(*memo)[MemoKey{seq, lvl}] = totMinLen | ||
return totMinLen | ||
} | ||
|
||
func solve(lines []string, levels int) { | ||
sum := 0 | ||
memo := map[MemoKey]int{} | ||
for _, l := range lines { | ||
totMinLen := getShortestPath(l, -1, levels, &memo, true) | ||
numPart, _ := strconv.Atoi(l[:len(l)-1]) | ||
fmt.Printf("%d * %d\n", totMinLen, numPart) | ||
sum += totMinLen * numPart | ||
} | ||
|
||
fmt.Println(sum) | ||
} | ||
|
||
func part1(lines []string) { | ||
solve(lines, 2) | ||
} | ||
|
||
func part2(lines []string) { | ||
solve(lines, 25) | ||
} | ||
|
||
func main() { | ||
data, err := os.ReadFile("./input21_def.txt") | ||
check(err) | ||
dataStr := strings.ReplaceAll(string(data), "\r\n", "\n") | ||
lines := strings.Split(strings.Trim(dataStr, "\n"), "\n") | ||
|
||
start := time.Now() | ||
part1(lines) | ||
fmt.Printf("part1: %s\n", time.Since(start)) | ||
start = time.Now() | ||
part2(lines) | ||
fmt.Printf("part2: %s\n", time.Since(start)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
module aoc21 | ||
|
||
go 1.23 | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
github.com/stretchr/testify v1.10.0 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= | ||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
package graph | ||
|
||
import ( | ||
"fmt" | ||
"math" | ||
"slices" | ||
) | ||
|
||
type Graph[T comparable] struct { | ||
Nodes []Node[T] | ||
Edges []Edge[T] | ||
AdjList map[T][]Edge[T] | ||
} | ||
|
||
type Node[T any] struct { | ||
Val T | ||
} | ||
|
||
type Edge[T any] struct { | ||
Weight int | ||
From T | ||
To *Node[T] | ||
} | ||
|
||
func (graph *Graph[T]) AddNode(val T) *Node[T] { | ||
node := Node[T]{Val: val} | ||
graph.Nodes = append(graph.Nodes, node) | ||
return &node | ||
} | ||
|
||
func (graph *Graph[T]) RemoveNode(val T) { | ||
delete(graph.AdjList, val) | ||
for i, lst := range graph.AdjList { | ||
idx := -1 | ||
for j, e := range lst { | ||
if e.To.Val == val { | ||
idx = j | ||
break | ||
} | ||
} | ||
if idx != -1 { | ||
graph.AdjList[i] = slices.Concat(lst[:idx], lst[idx+1:]) | ||
} | ||
} | ||
} | ||
|
||
func (graph *Graph[T]) AddEdge(from T, to T, weight int) *Edge[T] { | ||
toNode := graph.GetNode(to) | ||
if toNode == nil { | ||
fmt.Println("Error: toNode is nil") | ||
return nil | ||
} | ||
edge := Edge[T]{Weight: weight, To: toNode} | ||
graph.Edges = append(graph.Edges, edge) | ||
graph.AdjList[from] = append(graph.AdjList[from], edge) | ||
return &edge | ||
} | ||
|
||
func (graph *Graph[T]) AddUndirectedEdge(from T, to T, weight int) { | ||
graph.AddEdge(from, to, weight) | ||
graph.AddEdge(to, from, weight) | ||
} | ||
|
||
func (graph *Graph[T]) HasNode(val T) bool { | ||
return slices.ContainsFunc(graph.Nodes, func(n Node[T]) bool { return n.Val == val }) | ||
} | ||
|
||
func (graph *Graph[T]) HasEdge(from T, to T) bool { | ||
for _, e := range graph.AdjList[from] { | ||
if e.To.Val == to { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func (graph *Graph[T]) GetNode(val T) *Node[T] { | ||
for _, n := range graph.Nodes { | ||
if n.Val == val { | ||
return &n | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (graph *Graph[T]) Init(nodes int, edges int) { | ||
graph.Nodes = make([]Node[T], 0, nodes) | ||
graph.Edges = make([]Edge[T], 0, edges) | ||
graph.AdjList = make(map[T][]Edge[T], nodes) | ||
} | ||
|
||
func (graph *Graph[T]) ShortestPath(from T, to T) (int, map[T]bool, []T) { | ||
start := graph.GetNode(from) | ||
end := graph.GetNode(to) | ||
dist := make(map[T]int, len(graph.Nodes)) | ||
path := map[T]bool{} | ||
orderedPath := []T{} | ||
nodes := make([]T, 0, len(graph.Nodes)) | ||
for _, n := range graph.Nodes { | ||
dist[n.Val] = math.MaxInt | ||
nodes = append(nodes, n.Val) | ||
} | ||
|
||
dist[start.Val] = 0 | ||
for len(path) < len(nodes) { | ||
curr := graph.getClosest(nodes, dist, path) | ||
if path[curr] { | ||
continue | ||
} | ||
|
||
path[curr] = true | ||
orderedPath = append(orderedPath, curr) | ||
|
||
if curr == end.Val { | ||
break | ||
} | ||
|
||
for _, e := range graph.AdjList[curr] { | ||
// relaxation on the edge | ||
if !path[e.To.Val] && dist[curr]+e.Weight < dist[e.To.Val] { | ||
dist[e.To.Val] = dist[curr] + e.Weight | ||
} | ||
} | ||
} | ||
return dist[end.Val], path, orderedPath | ||
} | ||
|
||
func (graph *Graph[T]) GetOnlyPath(from T, to T) (map[T]bool, []T) { | ||
start := graph.GetNode(from) | ||
end := graph.GetNode(to) | ||
path := map[T]bool{} | ||
orderedPath := []T{} | ||
|
||
curr := start.Val | ||
for curr != end.Val { | ||
path[curr] = true | ||
orderedPath = append(orderedPath, curr) | ||
numAdj := len(graph.AdjList[curr]) | ||
if numAdj > 2 { | ||
panic("too many adjacents") | ||
} | ||
for _, e := range graph.AdjList[curr] { | ||
if !path[e.To.Val] { | ||
curr = e.To.Val | ||
break | ||
} | ||
} | ||
} | ||
path[curr] = true | ||
orderedPath = append(orderedPath, curr) | ||
return path, orderedPath | ||
} | ||
|
||
func (graph *Graph[T]) getClosest(nodes []T, dist map[T]int, visited map[T]bool) T { | ||
var minNode T | ||
minDist := math.MaxInt | ||
|
||
for _, n := range nodes { | ||
if !visited[n] && dist[n] <= minDist { | ||
minDist = dist[n] | ||
minNode = n | ||
} | ||
} | ||
return minNode | ||
} | ||
|
||
func (graph *Graph[T]) GetFullPathsEdges(start T, end T) []Edge[T] { | ||
allEdges := []Edge[T]{} | ||
min := math.MaxInt | ||
graph.getBestPathsRec(start, &map[T]bool{}, []Edge[T]{}, &allEdges, end, 0, &min) | ||
return allEdges | ||
} | ||
|
||
func (graph *Graph[T]) getBestPathsRec(currNode T, visited *map[T]bool, currPath []Edge[T], edges *[]Edge[T], end T, currVal int, currMin *int) { | ||
if currNode == end { | ||
fmt.Println("found path") | ||
if currVal < *currMin { | ||
*currMin = currVal | ||
(*edges) = append([]Edge[T]{}, currPath...) // replace all | ||
} else if currVal == *currMin { | ||
(*edges) = append((*edges), currPath...) | ||
} | ||
return | ||
} | ||
|
||
(*visited)[currNode] = true | ||
for _, e := range graph.AdjList[currNode] { | ||
next := e.To.Val | ||
if !(*visited)[next] { | ||
e.From = currNode | ||
if currVal+e.Weight <= *currMin { | ||
graph.getBestPathsRec(next, visited, append(currPath, e), edges, end, currVal+e.Weight, currMin) | ||
} | ||
} | ||
} | ||
(*visited)[currNode] = false | ||
} |
Oops, something went wrong.