diff --git a/2016/day14/main.go b/2016/day14/main.go new file mode 100644 index 0000000..165c90f --- /dev/null +++ b/2016/day14/main.go @@ -0,0 +1,68 @@ +package main + +import ( + "crypto/md5" + "flag" + "fmt" + "regexp" + "strings" + + "github.com/alexchao26/advent-of-code-go/util" +) + +func main() { + var part int + flag.IntVar(&part, "part", 1, "part 1 or 2") + flag.Parse() + fmt.Println("Running part", part) + + ans := oneTimePad(util.ReadFile("./input.txt"), part) + fmt.Println("Output:", ans) +} + +func oneTimePad(input string, part int) int { + hashCycles := 1 + if part == 2 { + hashCycles = 2016 + 1 + } + + hashes := []string{} + for i := 0; i < 1000; i++ { + hashes = append(hashes, hash(input, i, hashCycles)) + } + var keys []int + for index := 0; len(keys) < 64; index++ { + currentHash := hashes[0] + + // maintain the next 1000 hashes + hashes = append(hashes, hash(input, index+1000, hashCycles)) + hashes = hashes[1:] + + if char := hasTriple(currentHash); char != "" { + // this is slow... but it's simple + pattern := regexp.MustCompile(fmt.Sprintf("[%s]{5}", char)) + if pattern.MatchString(strings.Join(hashes, ",")) { + keys = append(keys, index) + } + } + } + + return keys[len(keys)-1] +} + +func hash(input string, index, cycles int) string { + hashed := fmt.Sprintf("%s%d", input, index) + for i := 0; i < cycles; i++ { + hashed = fmt.Sprintf("%x", md5.Sum([]byte(hashed))) + } + return hashed +} + +func hasTriple(in string) string { + for i := 2; i < len(in); i++ { + if in[i-2] == in[i-1] && in[i-1] == in[i] { + return string(in[i]) + } + } + return "" +} diff --git a/2016/day14/main_test.go b/2016/day14/main_test.go new file mode 100644 index 0000000..1165e43 --- /dev/null +++ b/2016/day14/main_test.go @@ -0,0 +1,28 @@ +package main + +import ( + "testing" + + "github.com/alexchao26/advent-of-code-go/util" +) + +func Test_part1(t *testing.T) { + tests := []struct { + name string + input string + part int + want int + }{ + {"example_part1", "abc", 1, 22728}, + {"actual_part1", util.ReadFile("input.txt"), 1, 23769}, + {"example_part2", "abc", 2, 22551}, + {"actual_part2", util.ReadFile("input.txt"), 2, 20606}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := oneTimePad(tt.input, tt.part); got != tt.want { + t.Errorf("part1() = %v, want %v", got, tt.want) + } + }) + } +}