2
\$\begingroup\$

I tried to solve the Advent of Code quiz for day 5 in Go, since my brute force algorithm wasn't efficient in Ruby. I solved the first part of the quiz with the following Go code.

package main
import (
 "crypto/md5"
 "encoding/hex"
 "fmt"
 "strings"
)
func main() {
 fmt.Println("Password:", extractPassword("abc"))
}
func extractPassword(roomId string) string {
 position := 0
 i := 0
 password := make([]rune, 8)
 for position < 8 {
 letter := evaluateIndex(roomId, i)
 if 0 != letter {
 fmt.Printf("Found %c for index %d\n", letter, i)
 password[position] = letter
 position++
 }
 i++
 }
 return string(password)
}
func evaluateIndex(roomId string, index int) rune {
 text := fmt.Sprintf("%s%d", roomId, index)
 hash := md5Hash(text)
 if strings.HasPrefix(hash, "00000") {
 return []rune(hash)[5]
 } else {
 return 0
 }
}
func md5Hash(text string) string {
 hash := md5.Sum([]byte(text))
 return hex.EncodeToString(hash[:])
}

My biggest issue was the string vs rune conflict. I'm interested in following points.

  1. Idiomatic Go style
  2. Usage of the Go standard library
  3. Performance improvments, while it's fast enough yet
asked Dec 6, 2016 at 22:26
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Your code is pretty idiomatic. The only obvious style thing I'd change is the end of the evaluateIndex function:

if strings.HasPrefix(hash, "00000") {
 return []rune(hash)[5]
}
return 0

you could also write if letter := evaluateIndex(roomId, i); letter != 0 { but it's not that important.

Now, the rune/string/byte question is the interesting one. md5 deals with []byte only; and converting strings to bytes and back is expensive (new memory allocations each time). So I'd change your code to use []byte instead of string, and byte instead of rune. The evaluateIndex function becomes faster:

var prefix = []byte("00000")
func evaluateIndex(roomId []byte, index int) byte {
 bytes := append(roomId, []byte(fmt.Sprint(index))...)
 hash := md5Hash(bytes)
 if bytes.HasPrefix(hash, prefix) {
 return hash[5]
 }
 return 0
}
func md5Hash(bytes []byte) []byte {
 rawHash := md5.Sum(bytes)
 encHash := make([]byte, md5.EncodedLen(len(rawHash)))
 hex.Encode(encHash, rawHash)
 return encHash
}

I put zeroes outside the func, to avoid converting it over and over. You still have a few conversions & allocations, but there's no way around them. The other functions can be modified easily to accommodate the type change.

answered Dec 10, 2016 at 14:40
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.