package day21 import ( "adventofcode2024/utils" "adventofcode2024/utils/dijkstra" "adventofcode2024/utils/memo" "fmt" "math" "strings" ) type Pair struct { s dijkstra.Point d dijkstra.Point } type Path struct { path []dijkstra.Point path_str string cost int } type State struct { code string depth int isNumericPad bool } var numericPad = map[dijkstra.Point]string{ {X: 0, Y: 0}: "7", {X: 1, Y: 0}: "8", {X: 2, Y: 0}: "9", {X: 0, Y: 1}: "4", {X: 1, Y: 1}: "5", {X: 2, Y: 1}: "6", {X: 0, Y: 2}: "1", {X: 1, Y: 2}: "2", {X: 2, Y: 2}: "3", {X: 1, Y: 3}: "0", {X: 2, Y: 3}: "A", } var directionPad = map[dijkstra.Point]string{ {X: 1, Y: 0}: "^", {X: 2, Y: 0}: "A", {X: 0, Y: 1}: "<", {X: 1, Y: 1}: "v", {X: 2, Y: 1}: ">", } var directions = []dijkstra.Point{{X: 0, Y: 1}, {X: 0, Y: -1}, {X: 1, Y: 0}, {X: -1, Y: 0}} var lookup_np = get_paths(numericPad) var lookup_dp = get_paths(directionPad) var m *memo.Memo func init() { m = memo.New(get_shortest_path) } func Part1(input string) int { codes := strings.Split(input, "\n") cost := 0 for _, code := range codes { initialState := State{ code: code, depth: 2, isNumericPad: true, } result := m.Get(initialState).(int) cost += result * utils.MustAtoi(code[:len(code)-1]) } return cost } func Part2(input string) int { codes := strings.Split(input, "\n") cost := 0 for _, code := range codes { initialState := State{ code: code, depth: 25, isNumericPad: true, } result := m.Get(initialState).(int) cost += result * utils.MustAtoi(code[:len(code)-1]) } return cost } func get_paths(in map[dijkstra.Point]string) map[Pair][]Path { lookup := make(map[Pair][]Path) data := dijkstra.InputGraph{} for p := range in { for _, dir := range directions { np := dijkstra.Point{X: p.X + dir.X, Y: p.Y + dir.Y} if in[np] != "" { data.Graph = append(data.Graph, dijkstra.InputData{ Source: p, Destination: np, Weight: 1, }) } } } graph := dijkstra.CreateGraph(data) for s := range in { for d := range in { paths, cost := dijkstra.GetAllShortestPaths(s, d, graph) for _, path := range paths { path_str := "" for x := 1; x < len(path); x++ { f := path[x-1] t := path[x] switch { case f.X == t.X && f.Y < t.Y: // Moving verticallY down path_str += "v" case f.X == t.X && f.Y > t.Y: // Moving verticallY up path_str += "^" case f.Y == t.Y && f.X < t.X: // Moving horizontallY right path_str += ">" case f.Y == t.Y && f.X > t.X: // Moving horizontally left path_str += "<" default: // No matching case or invalid input fmt.Println("Invalid direction") } } lookup[Pair{s, d}] = append(lookup[Pair{s, d}], Path{path, path_str, cost}) } } } return lookup } func find_point(in string, lookup map[dijkstra.Point]string) dijkstra.Point { for k, v := range lookup { if v == in { return k } } return dijkstra.Point{} } func get_shortest_path(key interface{}) interface{} { state := key.(State) if state.depth < 0 { return len(state.code) } // (Optional) If you need to modify the state, work on a copy. newState := state newState.code = "A" + state.code cost := 0 for x := 0; x