diff --git a/README.md b/README.md index d3c6fe9..f35a75a 100644 --- a/README.md +++ b/README.md @@ -144,3 +144,12 @@ There are 837 dots after 1 fold #### # #### ### # # ## # # ## This is the activation code ``` + +### Day fourteen + +```sh +$ ./aoc2021 --fourteen +The solution for "fourteen" is: +The result of subtracting the least from most common element quantities is 2988 +The result of subtracting the least from most common element quantities is 3572761917024 +``` diff --git a/fourteen/input.txt b/fourteen/input.txt new file mode 100644 index 0000000..47b7cce --- /dev/null +++ b/fourteen/input.txt @@ -0,0 +1,102 @@ +VFHKKOKKCPBONFHNPHPN + +VS -> B +HK -> B +FO -> P +NC -> F +VN -> C +BS -> O +HS -> K +NS -> C +CV -> P +NV -> C +PH -> H +PB -> B +PK -> K +HF -> P +FV -> C +NN -> H +VO -> K +VP -> P +BC -> B +KK -> S +OK -> C +PN -> H +SB -> V +KO -> P +KH -> C +KS -> S +FP -> B +PV -> B +BO -> C +OS -> H +NB -> S +SP -> C +HN -> N +FN -> B +PO -> O +FS -> O +NH -> B +SO -> P +OB -> S +KC -> C +OO -> H +BB -> V +SC -> F +NP -> P +SH -> C +BH -> O +BP -> F +CC -> S +BN -> H +SS -> P +BF -> B +VK -> P +OV -> H +FC -> S +VB -> S +PF -> N +HH -> O +HC -> V +CH -> B +HP -> H +FF -> H +VF -> V +CS -> F +KP -> F +OP -> H +KF -> F +PP -> V +OC -> C +PS -> F +ON -> H +BK -> B +HV -> S +CO -> K +FH -> C +FB -> F +OF -> V +SN -> S +PC -> K +NF -> F +NK -> P +NO -> P +CP -> P +CK -> S +HB -> H +BV -> C +SF -> K +HO -> H +OH -> B +KV -> S +KN -> F +SK -> K +VH -> S +CN -> S +VC -> P +CB -> H +SV -> S +VV -> P +CF -> F +FK -> F +KB -> V \ No newline at end of file diff --git a/fourteen/main.go b/fourteen/main.go new file mode 100644 index 0000000..fd2f4bf --- /dev/null +++ b/fourteen/main.go @@ -0,0 +1,24 @@ +package fourteen + +import "fmt" + +type Fourteen struct { + polymer polymer +} + +func Init(filepath string) *Fourteen { + fourteen := &Fourteen{ + polymer: polymer{}, + } + + fourteen.polymer.load(filepath) + return fourteen +} + +func (d *Fourteen) Answer() string { + return fmt.Sprintf("The result of subtracting the least from most common element quantities is %d", d.polymer.elementCount(10)) +} + +func (d *Fourteen) FollowUp() string { + return fmt.Sprintf("The result of subtracting the least from most common element quantities is %d", d.polymer.elementCountMap(40)) +} diff --git a/fourteen/polymer.go b/fourteen/polymer.go new file mode 100644 index 0000000..e9d240e --- /dev/null +++ b/fourteen/polymer.go @@ -0,0 +1,146 @@ +package fourteen + +import ( + "bufio" + "fmt" + "os" +) + +type polymer struct { + template string + rules map[string]string +} + +func (p *polymer) load(filename string) error { + file, err := os.Open(filename) + if err != nil { + return err + } + defer file.Close() + scanner := bufio.NewScanner(file) + + p.rules = map[string]string{} + scannedTemplate := false + for scanner.Scan() { + if !scannedTemplate { + p.template = scanner.Text() + + scannedTemplate = true + continue + } + + if scanner.Text() == "" { + continue + } + + var pair, insert string + if count, err := fmt.Sscanf(scanner.Text(), "%s -> %s", &pair, &insert); count != 2 || err != nil { + return fmt.Errorf("expected to scan 2 values from %q, found %d: %w", scanner.Text(), count, err) + } + p.rules[pair] = insert + } + + return nil +} + +func (p *polymer) elementCount(size int) int { + result := p.step(size) + elemCounts := map[rune]int{} + + for _, r := range result { + elemCounts[r]++ + } + + min := len(result) + max := 0 + for _, v := range elemCounts { + if v < min { + min = v + } + + if v > max { + max = v + } + } + + return max - min +} + +func (p *polymer) elementCountMap(size int) int { + _, counts := p.stepMap(size) + + min := -1 + max := -1 + for _, v := range counts { + if v < min || min == -1 { + min = v + } + + if v > max { + max = v + } + } + + return max - min +} + +func (p *polymer) stepMap(steps int) (map[string]int, map[string]int) { + pairs := map[string]int{} + counts := map[string]int{} + + // Build initial pairs and counts from template + for i := 0; i < len(p.template)-1; i++ { + // Count first element from pair + counts[string(p.template[i])]++ + pairs[fmt.Sprintf("%c%c", p.template[i], p.template[i+1])]++ + // If this is the last pair add the second element + if i == len(p.template)-2 { + counts[string(p.template[i+1])]++ + } + } + + for i := 0; i < steps; i++ { + pairs, counts = p.stepMapOne(pairs, counts) + } + + return pairs, counts +} + +func (p *polymer) stepMapOne(pairs map[string]int, counts map[string]int) (map[string]int, map[string]int) { + newPairs := map[string]int{} + for k, v := range pairs { + result := p.rules[k] + counts[result] += (1 * v) + pair1 := fmt.Sprintf("%c%s", k[0], result) + pair2 := fmt.Sprintf("%s%c", result, k[1]) + newPairs[pair1] += 1 * v + newPairs[pair2] += 1 * v + } + + return newPairs, counts +} + +func (p *polymer) step(steps int) string { + seed := p.template + for i := 0; i < steps; i++ { + seed = p.stepOne(seed) + } + + return seed +} + +func (p *polymer) stepOne(from string) string { + result := "" + for i := 0; i < len(from)-1; i++ { + // Add first element from pair + result += string(from[i]) + // Lookup and add element result from pair + pair := fmt.Sprintf("%c%c", from[i], from[i+1]) + result += p.rules[pair] + // If this is the last pair add the second element + if i == len(from)-2 { + result += string(from[i+1]) + } + } + return result +} diff --git a/fourteen/polymer_test.go b/fourteen/polymer_test.go new file mode 100644 index 0000000..0773bf5 --- /dev/null +++ b/fourteen/polymer_test.go @@ -0,0 +1,72 @@ +package fourteen + +import "testing" + +func Test_read(t *testing.T) { + p := polymer{} + if err := p.load("test_input.txt"); err != nil { + t.Log(err) + t.FailNow() + } + + if p.template != "NNCB" { + t.Logf("Expected template of NNCB, found %s", p.template) + t.Fail() + } + + if len(p.rules) != 16 { + t.Logf("Expected 16 rules, found %d", len(p.rules)) + t.Fail() + } +} + +func Test_step(t *testing.T) { + p := polymer{} + p.load("test_input.txt") + + if result := p.step(1); result != "NCNBCHB" { + t.Logf("Expected NCNBCHB, found %s", result) + t.Fail() + } + + if result := p.step(2); result != "NBCCNBBBCBHCB" { + t.Logf("Expected NBCCNBBBCBHCB, found %s", result) + t.Fail() + } + + if result := p.step(3); result != "NBBBCNCCNBBNBNBBCHBHHBCHB" { + t.Logf("Expected NBBBCNCCNBBNBNBBCHBHHBCHB, found %s", result) + t.Fail() + } + + if result := p.step(4); result != "NBBNBNBBCCNBCNCCNBBNBBNBBBNBBNBBCBHCBHHNHCBBCBHCB" { + t.Logf("Expected NBBNBNBBCCNBCNCCNBBNBBNBBBNBBNBBCBHCBHHNHCBBCBHCB, found %s", result) + t.Fail() + } + +} + +func Test_elemCount(t *testing.T) { + p := polymer{} + p.load("test_input.txt") + + if result := p.elementCount(10); result != 1588 { + t.Logf("Expected a result of 1588, got %d", result) + t.Fail() + } +} + +func Test_elemMapCount(t *testing.T) { + p := polymer{} + p.load("test_input.txt") + + if result := p.elementCountMap(10); result != 1588 { + t.Logf("Expected a result of 1588, got %d", result) + t.Fail() + } + + if result := p.elementCountMap(40); result != 2188189693529 { + t.Logf("Expected a result of 2188189693529, got %d", result) + t.Fail() + } +} diff --git a/fourteen/test_input.txt b/fourteen/test_input.txt new file mode 100644 index 0000000..6c1c3a1 --- /dev/null +++ b/fourteen/test_input.txt @@ -0,0 +1,18 @@ +NNCB + +CH -> B +HH -> N +CB -> H +NH -> C +HB -> C +HC -> B +HN -> C +NN -> C +BH -> H +NC -> B +NB -> B +BN -> B +BB -> N +BC -> B +CC -> N +CN -> C \ No newline at end of file diff --git a/main.go b/main.go index 5e026bb..35036ad 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ import ( "unsupervised.ca/aoc2021/eleven" "unsupervised.ca/aoc2021/five" "unsupervised.ca/aoc2021/four" + "unsupervised.ca/aoc2021/fourteen" "unsupervised.ca/aoc2021/nine" "unsupervised.ca/aoc2021/one" "unsupervised.ca/aoc2021/seven" @@ -61,6 +62,8 @@ func main() { day = twelve.Init("twelve/input.txt") case "thirteen": day = thirteen.Init("thirteen/input.txt") + case "fourteen": + day = fourteen.Init("fourteen/input.txt") default: fmt.Printf("%q does not have a solution.\n", flagParts[1]) help()