From 5ccafb37c4aeecca4eeefb35e07c97af009121d7 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Sat, 17 Dec 2016 10:00:39 -0600 Subject: [PATCH] Day 17 Complete --- 2016/day17/input | 1 + 2016/day17/main.go | 111 +++++++++++++++++++++++++++++++++++++++++++++ 2016/day17/problem | 94 ++++++++++++++++++++++++++++++++++++++ helpers.go | 16 ++++--- 4 files changed, 215 insertions(+), 7 deletions(-) create mode 100644 2016/day17/input create mode 100644 2016/day17/main.go create mode 100644 2016/day17/problem diff --git a/2016/day17/input b/2016/day17/input new file mode 100644 index 0000000..76432d7 --- /dev/null +++ b/2016/day17/input @@ -0,0 +1 @@ +pvhmgsws diff --git a/2016/day17/main.go b/2016/day17/main.go new file mode 100644 index 0000000..e0ac61c --- /dev/null +++ b/2016/day17/main.go @@ -0,0 +1,111 @@ +package main + +import ( + "crypto/md5" + "fmt" + "os" + + "../../" +) + +var gridWidth, gridHeight int + +func main() { + var passcode string + gridWidth, gridHeight = 4, 4 + + if len(os.Args) < 2 { + printUsageAndExit() + } + passcode = os.Args[1] + if len(os.Args) >= 4 { + gridWidth = aoc.Atoi(os.Args[2]) + gridHeight = aoc.Atoi(os.Args[3]) + } + foundPaths := findPaths("", passcode) + if len(foundPaths) == 0 { + fmt.Println("Couldn't find a valid path!") + os.Exit(1) + } + shortestPath := foundPaths[0] + longestPath := foundPaths[0] + for i := range foundPaths { + if len(foundPaths[i]) < len(shortestPath) { + shortestPath = foundPaths[i] + } + if len(foundPaths[i]) > len(longestPath) { + longestPath = foundPaths[i] + } + } + fmt.Println("Shortest Path:", shortestPath) + fmt.Println("Longest Path Length:", len(longestPath)) +} + +func findPaths(path, passcode string) []string { + var ret []string + cX, cY := currPos(path) + if cY == gridHeight-1 && cX == gridWidth-1 { + return []string{path} + } + if cY > 0 && doorIsOpen(path, passcode, 'U') { + ret = append(ret, findPaths(path+"U", passcode)...) + } + if cY < (gridHeight-1) && doorIsOpen(path, passcode, 'D') { + ret = append(ret, findPaths(path+"D", passcode)...) + } + if cX > 0 && doorIsOpen(path, passcode, 'L') { + ret = append(ret, findPaths(path+"L", passcode)...) + } + if cX < (gridWidth-1) && doorIsOpen(path, passcode, 'R') { + ret = append(ret, findPaths(path+"R", passcode)...) + } + return ret +} + +// We cache the last hash calculated +// It should speed things up a little +var lastHashInput string +var lastHash string + +func doorIsOpen(path, passcode string, dir rune) bool { + if lastHashInput != passcode+path { + lastHashInput = passcode + path + lastHash = fmt.Sprintf("%x", md5.Sum([]byte(lastHashInput))) + } + switch dir { + case 'U': + return lastHash[0] >= 'b' && lastHash[0] <= 'f' + case 'D': + return lastHash[1] >= 'b' && lastHash[1] <= 'f' + case 'L': + return lastHash[2] >= 'b' && lastHash[2] <= 'f' + case 'R': + return lastHash[3] >= 'b' && lastHash[3] <= 'f' + } + return false +} + +func currPos(path string) (int, int) { + var cX, cY int + for i := range path { + switch path[i] { + case 'U': + cY-- + case 'D': + cY++ + case 'L': + cX-- + case 'R': + cX++ + } + } + return cX, cY +} + +func PrintState(path, passcode string) { +} + +func printUsageAndExit() { + fmt.Println("Usage: ./day17 ") + os.Exit(0) +} diff --git a/2016/day17/problem b/2016/day17/problem new file mode 100644 index 0000000..34c43f1 --- /dev/null +++ b/2016/day17/problem @@ -0,0 +1,94 @@ +Advent of Code + +--- Day 17: Two Steps Forward --- + + You're trying to access a secure vault protected by a 4x4 grid of small rooms connected by + doors. You start in the top-left room (marked S), and you can access the vault (marked V) once + you reach the bottom-right room: + + ######### + #S| | | # + #-#-#-#-# + # | | | # + #-#-#-#-# + # | | | # + #-#-#-#-# + # | | | + ####### V + + Fixed walls are marked with #, and doors are marked with - or |. + + The doors in your current room are either open or closed (and locked) based on the hexadecimal + MD5 hash of a passcode (your puzzle input) followed by a sequence of uppercase characters + representing the path you have taken so far (U for up, D for down, L for left, and R for + right). + + Only the first four characters of the hash are used; they represent, respectively, the doors + up, down, left, and right from your current position. Any b, c, d, e, or f means that the + corresponding door is open; any other character (any number or a) means that the corresponding + door is closed and locked. + + To access the vault, all you need to do is reach the bottom-right room; reaching this room + opens the vault and all doors in the maze. + + For example, suppose the passcode is hijkl. Initially, you have taken no steps, and so your + path is empty: you simply find the MD5 hash of hijkl alone. The first four characters of this + hash are ced9, which indicate that up is open (c), down is open (e), left is open (d), and + right is closed and locked (9). Because you start in the top-left corner, there are no "up" or + "left" doors to be open, so your only choice is down. + + Next, having gone only one step (down, or D), you find the hash of hijklD. This produces f2bc, + which indicates that you can go back up, left (but that's a wall), or right. Going right means + hashing hijklDR to get 5745 - all doors closed and locked. However, going up instead is + worthwhile: even though it returns you to the room you started in, your path would then be DU, + opening a different set of doors. + + After going DU (and then hashing hijklDU to get 528e), only the right door is open; after + going DUR, all doors lock. (Fortunately, your actual passcode is not hijkl). + + Passcodes actually used by Easter Bunny Vault Security do allow access to the vault if you + know the right path. For example: + + • If your passcode were ihgpwlah, the shortest path would be DDRRRD. + • With kglvqrro, the shortest path would be DDUDRLRRUDRD. + • With ulqzkmiv, the shortest would be DRURDRUDDLLDLUURRDULRLDUUDDDRR. + + Given your vault's passcode, what is the shortest path (the actual path, not just the length) + to reach the vault? + + Your puzzle answer was __________________. + +--- Part Two --- + + You're curious how robust this security solution really is, and so you decide to find longer + and longer paths which still provide access to the vault. You remember that paths always end + the first time they reach the bottom-right room (that is, they can never pass through it, only + end in it). + + For example: + + • If your passcode were ihgpwlah, the longest path would take 370 steps. + • With kglvqrro, the longest path would be 492 steps long. + • With ulqzkmiv, the longest path would be 830 steps long. + + What is the length of the longest path that reaches the vault? + + Your puzzle answer was _______. + +References + + Visible links + . http://adventofcode.com/ + . http://adventofcode.com/2016/about + . http://adventofcode.com/2016/support + . http://adventofcode.com/2016/events + . http://adventofcode.com/2016/settings + . http://adventofcode.com/2016/auth/logout + . http://adventofcode.com/2016 + . http://adventofcode.com/2016 + . http://adventofcode.com/2016/leaderboard + . http://adventofcode.com/2016/stats + . http://adventofcode.com/2016/sponsors + . http://adventofcode.com/2016/sponsors + . https://en.wikipedia.org/wiki/MD5 + . http://adventofcode.com/2016 diff --git a/helpers.go b/helpers.go index dc212f0..81bb32e 100644 --- a/helpers.go +++ b/helpers.go @@ -3,12 +3,13 @@ package aoc import ( "bufio" "fmt" + "io/ioutil" "log" "os" "strconv" ) -func stdinToStringSlice() []string { +func StdinToStringSlice() []string { var input []string scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { @@ -17,7 +18,7 @@ func stdinToStringSlice() []string { return input } -func atoi(i string) int { +func Atoi(i string) int { var ret int var err error if ret, err = strconv.Atoi(i); err != nil { @@ -26,11 +27,11 @@ func atoi(i string) int { return ret } -func itoa(i int) string { +func Itoa(i int) string { return strconv.Itoa(i) } -func stdinToString() string { +func StdinToString() string { var input string scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { @@ -39,11 +40,12 @@ func stdinToString() string { return input } -func fileToString(fn string) string { +func FileToString(fn string) string { var c []byte - c, err = ioutil.Readfile(filename) + var err error + c, err = ioutil.ReadFile(fn) if err != nil { - fmt.Println("Unable to read file: " + filename) + fmt.Println("Unable to read file: " + fn) os.Exit(1) } return string(c)