From 207d088a09e97f70be5511e71ee63f2c26cb748c Mon Sep 17 00:00:00 2001 From: alexchao26 Date: Thu, 24 Dec 2020 21:39:50 -0500 Subject: [PATCH] 2016-day21: gross scrambling and then checking permutations --- 2016/day21/main.go | 117 ++++++++++++++++++++++++++++++++++++++++ 2016/day21/main_test.go | 38 +++++++++++++ algos/permutations.go | 21 ++++++++ 3 files changed, 176 insertions(+) create mode 100644 2016/day21/main.go create mode 100644 2016/day21/main_test.go diff --git a/2016/day21/main.go b/2016/day21/main.go new file mode 100644 index 0000000..ef77b8a --- /dev/null +++ b/2016/day21/main.go @@ -0,0 +1,117 @@ +package main + +import ( + "flag" + "fmt" + "strings" + + "github.com/alexchao26/advent-of-code-go/algos" + "github.com/alexchao26/advent-of-code-go/cast" + "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) + + var ans string + if part == 1 { + ans = part1(util.ReadFile("./input.txt"), "abcdefgh", "") + } else { + ans = part1(util.ReadFile("./input.txt"), "abcdefgh", "fbgdceah") + } + fmt.Println("Output:", ans) +} + +func part1(input string, starting string, target string) string { + runSteps := func(starting string) string { + registers := strings.Split(starting, "") + for _, inst := range strings.Split(input, "\n") { + registers = modifySlice(inst, registers) + } + return strings.Join(registers, "") + } + + // part 1 + if target == "" { + return runSteps(starting) + } + + for _, p := range algos.MakeStringPermutations(starting) { + if runSteps(p) == target { + return p + } + } + + panic("no perms matched") +} + +func modifySlice(line string, sli []string) []string { + switch { + case strings.HasPrefix(line, "swap"): + var i1, i2 int + if strings.Contains(line, "position") { + fmt.Sscanf(line, "swap position %d with position %d", &i1, &i2) + } else { + var c1, c2 string + fmt.Sscanf(line, "swap letter %1s with letter %1s", &c1, &c2) + i1, i2 = getIndex(sli, c1), getIndex(sli, c2) + } + sli[i1], sli[i2] = sli[i2], sli[i1] + case strings.HasPrefix(line, "rotate"): + var rightShift int + parts := strings.Split(line, " ") + if strings.Contains(line, "letter") { + index := getIndex(sli, parts[6]) + if index >= 4 { + index++ + } + index++ + rightShift = index % len(sli) + } else { + // left or right + rightShift = cast.ToInt(parts[2]) + if parts[1] == "left" { + rightShift = len(sli) - rightShift + } + } + // perform shift + sli = append(sli[len(sli)-rightShift:], sli[:len(sli)-rightShift]...) + case strings.HasPrefix(line, "reverse"): + var i1, i2 int + fmt.Sscanf(line, "reverse positions %d through %d", &i1, &i2) + for i1 < i2 { + sli[i1], sli[i2] = sli[i2], sli[i1] + i1++ + i2-- + } + case strings.HasPrefix(line, "move"): + var i1, i2 int + fmt.Sscanf(line, "move position %d to position %d", &i1, &i2) + store := sli[i1] + + // remove char at i1 + copy(sli[i1:], sli[i1+1:]) + + for i := len(sli) - 1; i >= i2+1; i-- { + sli[i] = sli[i-1] + } + sli[i2] = store + default: + panic("unhandled instruction type: " + line) + } + + // return modified slice + return sli +} + +func getIndex(letters []string, toFind string) int { + for i, v := range letters { + if v == toFind { + return i + } + } + return -1 +} diff --git a/2016/day21/main_test.go b/2016/day21/main_test.go new file mode 100644 index 0000000..2b45966 --- /dev/null +++ b/2016/day21/main_test.go @@ -0,0 +1,38 @@ +package main + +import ( + "testing" + + "github.com/alexchao26/advent-of-code-go/util" +) + +var example = `swap position 4 with position 0 +swap letter d with letter b +reverse positions 0 through 4 +rotate left 1 step +move position 1 to position 4 +move position 3 to position 0 +rotate based on position of letter b +rotate based on position of letter d` + +func Test_part1(t *testing.T) { + type args struct { + input, starting, target string + } + tests := []struct { + name string + args args + want string + }{ + {"part1 example", args{example, "abcde", ""}, "decab"}, + {"part1 actual", args{util.ReadFile("input.txt"), "abcdefgh", ""}, "bfheacgd"}, + {"part2 actual", args{util.ReadFile("input.txt"), "abcdefgh", "fbgdceah"}, "gcehdbfa"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := part1(tt.args.input, tt.args.starting, tt.args.target); got != tt.want { + t.Errorf("part1() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/algos/permutations.go b/algos/permutations.go index 63931e6..d2bfde9 100644 --- a/algos/permutations.go +++ b/algos/permutations.go @@ -1,5 +1,7 @@ package algos +import "strings" + // MakePermutations will make all permutations of the numbers input // returns a pointer to avoid copying a large number of permutations func MakePermutations(numbers []int) *[][]int { @@ -32,3 +34,22 @@ func swapRecurseBacktrack(numbers []int, startIndex int, results *[][]int) { numbers[startIndex], numbers[i] = numbers[i], numbers[startIndex] } } + +// MakeStringPermutations generates all permutations for a given string +func MakeStringPermutations(str string) []string { + return recurse(strings.Split(str, ""), 0) +} + +func recurse(sli []string, index int) []string { + if index == len(sli) { + return []string{strings.Join(sli, "")} + } + + var perms []string + for i := index; i < len(sli); i++ { + sli[i], sli[index] = sli[index], sli[i] + perms = append(perms, recurse(sli, index+1)...) + sli[i], sli[index] = sli[index], sli[i] + } + return perms +}