From 7047dcbaa1860c2e84de09253cff97437972fa1c Mon Sep 17 00:00:00 2001
From: Christopher Bohn <bohn@unl.edu>
Date: Fri, 17 Dec 2021 11:06:17 -0600
Subject: [PATCH] Day 14 in-progress.

---
 2021/README.md                                |  30 ++++-
 .../java/edu/unl/cse/bohn/year2021/Day14.java | 122 ++++++++++++++++++
 2 files changed, 151 insertions(+), 1 deletion(-)
 create mode 100644 2021/src/main/java/edu/unl/cse/bohn/year2021/Day14.java

diff --git a/2021/README.md b/2021/README.md
index c31634c..300ba54 100644
--- a/2021/README.md
+++ b/2021/README.md
@@ -159,9 +159,37 @@ Hmm... part 1 took longer than I think it should have. The big-O complexity is
 manageable, but I guess the constant factors are problematic.
 
 For part 2, I'm not even going to try to algorithmically determine which letters
-are created (and not just because I'd have to return soemthing other than a
+are created (and not just because I'd have to return something other than a
 long integer).
 
 Hmm... part 2 really didn't take much longer than part 1 did, which suggests
 the computational pain point is in parsing the data and creating the "paper"
 and not in the folding.
+
+# Day 14
+
+Again, no fancy data structures. Just need a way to store the rules, such as a Map,
+and iterate over a string.
+
+The only interesting piece of Part 2 is trying to trip you up over integer
+overflow. Since I already converted my abstract base class to long integers back
+on Day 6, no big deal. That, and waiting a bit longer for the answer. Quite a bit
+longer, it seems. Is there any place I can optimize? I sure hope so, since I
+just hit an OutOfMemoryError on the *sample data*!
+
+I don't see how I can avoid modeling the full string.
+
+Maybe I can have the StringBuilder work on smaller chunks, thereby allowing for
+more frequent garbage collection? That probably will work. I'm pretty sure that
+StringBuilder has to have an underlying linked list.
+
+That doesn't seem to be speeding things up, but hopefully it'll help with the
+memory problem. (Two hours later...) memory didn't blow up, but the sample data
+isn't finished processing yet. It occurs to me that I don't need to use a
+StringBuilder. I can create two arrays of characters and merge them. Let's see
+how that goes.
+
+Oh, yes. This is *much* faster. Still taking a while to get through part 2, but
+in a few seconds we got past the point that the StringBuilder approach took a
+couple of hours while I was in a meeting. But we still ran out of memory when
+growing the new polymer on iteration 28 of the sample data.
diff --git a/2021/src/main/java/edu/unl/cse/bohn/year2021/Day14.java b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day14.java
new file mode 100644
index 0000000..cabe3f2
--- /dev/null
+++ b/2021/src/main/java/edu/unl/cse/bohn/year2021/Day14.java
@@ -0,0 +1,122 @@
+package edu.unl.cse.bohn.year2021;
+
+import edu.unl.cse.bohn.Puzzle;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@SuppressWarnings("unused")
+public class Day14 extends Puzzle {
+    private Map<String, Character> rules;
+
+    public Day14(boolean isProductionReady) {
+        super(isProductionReady);
+        sampleData = """
+                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""";
+    }
+
+    private String parseData(List<String> data) {
+        String initialMolecule = data.get(0);
+        rules = new HashMap<>();
+        for (String rule : data) {
+            if (rule.contains(" -> ")) {
+                String[] halves = rule.split(" -> ");
+                rules.put(halves[0], halves[1].charAt(0));
+            }
+        }
+        return initialMolecule;
+    }
+
+    @SuppressWarnings("CommentedOutCode")
+    private String growMolecule(String molecule) {
+//        StringBuilder growingMolecule = new StringBuilder();
+//        String pattern = "";
+//        for (int i = 0; i < molecule.length() - 1; i++) {
+//            pattern = molecule.substring(i, i + 2);
+//            growingMolecule.append(pattern.charAt(0)).append(rules.get(pattern));
+//        }
+//        growingMolecule.append(pattern.charAt(1));
+//        return growingMolecule.toString();
+        char[] oldElements = molecule.toCharArray();
+        char[] newElements = new char[2 * molecule.length() - 1];
+        for (int i = 0; i < oldElements.length - 1; i++) {
+            newElements[2 * i] = oldElements[i];
+            newElements[2 * i + 1] = rules.get(molecule.substring(i, i + 2));
+        }
+        newElements[newElements.length - 1] = oldElements[oldElements.length - 1];
+        return new String(newElements);
+    }
+
+    private Map<Character, Long> countElements(String molecule) {
+        Map<Character, Long> counts = new HashMap<>();
+        for (char element : molecule.toCharArray()) {
+            if (!counts.containsKey(element)) {
+                counts.put(element, 0L);
+            }
+            counts.put(element, counts.get(element) + 1);
+        }
+        return counts;
+    }
+
+    private long producePolymer(List<String> data, int numberOfSteps) {
+        String molecule = parseData(data);
+        for (int i = 0; i < numberOfSteps; i++) {
+            System.out.print("Molecular growth (" + i + ")--\toriginal size: " + molecule.length());
+            molecule = growMolecule(molecule);
+            System.out.println("\tnew size: " + molecule.length());
+//            if (molecule.length() < 60) {
+//                String expectedString = switch (i) {
+//                    case 0 -> "NCNBCHB";
+//                    case 1 -> "NBCCNBBBCBHCB";
+//                    case 2 -> "NBBBCNCCNBBNBNBBCHBHHBCHB";
+//                    case 3 -> "NBBNBNBBCCNBCNCCNBBNBBNBBBNBBNBBCBHCBHHNHCBBCBHCB";
+//                    default -> "??";
+//                };
+//                System.out.println("\texpected: " + expectedString);
+//                System.out.println("\t  actual: " + molecule);
+//                assert (molecule.equals(expectedString));
+//            }
+        }
+        Map<Character, Long> elementCounts = countElements(molecule);
+        char leastFrequentElement = molecule.charAt(0);
+        char mostFrequentElement = molecule.charAt(0);
+        for (char element : elementCounts.keySet()) {
+            if (elementCounts.get(element) < elementCounts.get(leastFrequentElement)) {
+                leastFrequentElement = element;
+            }
+            if (elementCounts.get(element) > elementCounts.get(mostFrequentElement)) {
+                mostFrequentElement = element;
+            }
+        }
+        return elementCounts.get(mostFrequentElement) - elementCounts.get(leastFrequentElement);
+    }
+
+    @Override
+    public long computePart1(List<String> data) {
+        return producePolymer(data, 10);
+    }
+
+    @Override
+    public long computePart2(List<String> data) {
+        return producePolymer(data, 40);
+    }
+}
-- 
GitLab