From 200abbb81514e84dab01967aadd39bc425d0bd22 Mon Sep 17 00:00:00 2001
From: Christopher Bohn <bohn@unl.edu>
Date: Wed, 30 Nov 2022 20:14:13 -0600
Subject: [PATCH] getting ready for 2022 Advent of Code

---
 2022/README.md                                |  4 +
 2022/pom.xml                                  | 25 +++++
 .../java/edu/unl/cse/bohn/ImportData.java     | 91 +++++++++++++++++++
 2022/src/main/java/edu/unl/cse/bohn/Main.java | 49 ++++++++++
 .../main/java/edu/unl/cse/bohn/Puzzle.java    | 28 ++++++
 2022/src/main/java/edu/unl/cse/bohn/year2021  |  1 +
 2022/src/main/resources/apikeys.TEMPLATE      |  3 +
 scaffolding/ImportData.java                   | 91 +++++++++++++++++++
 scaffolding/Main.java                         | 49 ++++++++++
 scaffolding/Puzzle.java                       | 28 ++++++
 10 files changed, 369 insertions(+)
 create mode 100644 2022/README.md
 create mode 100644 2022/pom.xml
 create mode 100644 2022/src/main/java/edu/unl/cse/bohn/ImportData.java
 create mode 100644 2022/src/main/java/edu/unl/cse/bohn/Main.java
 create mode 100644 2022/src/main/java/edu/unl/cse/bohn/Puzzle.java
 create mode 120000 2022/src/main/java/edu/unl/cse/bohn/year2021
 create mode 100644 2022/src/main/resources/apikeys.TEMPLATE
 create mode 100644 scaffolding/ImportData.java
 create mode 100644 scaffolding/Main.java
 create mode 100644 scaffolding/Puzzle.java

diff --git a/2022/README.md b/2022/README.md
new file mode 100644
index 0000000..03da0a2
--- /dev/null
+++ b/2022/README.md
@@ -0,0 +1,4 @@
+# 2022 Advent of Code Solutions
+
+## Day 1
+
diff --git a/2022/pom.xml b/2022/pom.xml
new file mode 100644
index 0000000..22efcff
--- /dev/null
+++ b/2022/pom.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>edu.unl.cse.bohn</groupId>
+    <artifactId>AdventOfCoding</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <properties>
+        <maven.compiler.source>17</maven.compiler.source>
+        <maven.compiler.target>17</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.googlecode.json-simple</groupId>
+            <artifactId>json-simple</artifactId>
+            <version>1.1.1</version>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/2022/src/main/java/edu/unl/cse/bohn/ImportData.java b/2022/src/main/java/edu/unl/cse/bohn/ImportData.java
new file mode 100644
index 0000000..e472acd
--- /dev/null
+++ b/2022/src/main/java/edu/unl/cse/bohn/ImportData.java
@@ -0,0 +1,91 @@
+package edu.unl.cse.bohn;
+
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLConnection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+
+public class ImportData {
+
+    public static final String FILENAME = "apikeys.json";
+
+    protected final String protocol;
+    protected final String host;
+    protected final String path;
+    protected final String apiKey;
+
+    @SuppressWarnings("unused")
+    public static List<String> readFile(String filename) throws IOException {
+        List<String> data = new LinkedList<>();
+        BufferedReader bufferedReader;
+        bufferedReader = new BufferedReader(new FileReader(filename));
+        while (bufferedReader.ready()) {
+            data.add(bufferedReader.readLine());
+        }
+        return data;
+    }
+
+    public ImportData(String apiKeyName, int year, int day) {
+        String apiKey;
+        protocol = "https";
+        host = "adventofcode.com";
+        path = "/" + year + "/day/" + day + "/input";
+        try {
+            apiKey = getApiKey(apiKeyName);
+        } catch (IOException ioException) {
+            System.err.println("Could not retrieve API key: " + ioException.getMessage());
+            apiKey = null;
+        }
+        this.apiKey = apiKey;
+    }
+
+    protected String getApiKey(String apiKeyName) throws IOException {
+        JSONObject apiKeyJson;
+        try (InputStreamReader inputStreamReader = new InputStreamReader(
+                Objects.requireNonNull(ImportData.class.getClassLoader().getResourceAsStream(FILENAME)))) {
+            apiKeyJson = (JSONObject)new JSONParser().parse(inputStreamReader);
+        } catch (NullPointerException nullPointerException) {
+            FileNotFoundException newException = new FileNotFoundException("File " + FILENAME + " not found.");
+            newException.initCause(nullPointerException);
+            throw newException;
+        } catch (ParseException parseException) {
+            throw new IOException("Error while parsing file " + FILENAME + ".", parseException);
+        }
+        if (!apiKeyJson.containsKey(apiKeyName)) {
+            System.err.println("WARNING! Could not locate API key named " + apiKeyName + " in file " + FILENAME + ".");
+        }
+        String apiKey = apiKeyJson.get(apiKeyName).toString();
+        if (apiKey.equals("")) {
+            System.err.println("WARNING! API key named " + apiKeyName + " in file " + FILENAME + " is blank.");
+        }
+        return apiKey;
+    }
+
+    public List<String> importData() throws IOException {
+        List<String> data = new LinkedList<>();
+        BufferedReader bufferedReader;
+        try {
+            URLConnection connection = new URI(protocol, host, path, null).toURL().openConnection();
+            connection.setRequestProperty("Cookie", apiKey);
+            bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+        } catch (URISyntaxException | MalformedURLException originalException) {
+            throw new IOException("Could not retrieve usable data from " + host + ".", originalException);
+        }
+        while (bufferedReader.ready()) {
+            data.add(bufferedReader.readLine());
+        }
+        return data;
+    }
+}
diff --git a/2022/src/main/java/edu/unl/cse/bohn/Main.java b/2022/src/main/java/edu/unl/cse/bohn/Main.java
new file mode 100644
index 0000000..875e4b5
--- /dev/null
+++ b/2022/src/main/java/edu/unl/cse/bohn/Main.java
@@ -0,0 +1,49 @@
+package edu.unl.cse.bohn;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Calendar;
+import java.util.Scanner;
+
+public class Main {
+    public static final String defaultApiKey = "aoc";
+
+    private static String getUserInput(String prompt, String defaultValue, Scanner scanner) {
+        System.out.print(prompt + " [" + defaultValue + "]: ");
+        String userInput = scanner.nextLine();
+        return userInput.equals("") ? defaultValue : userInput;
+    }
+
+    public static void main(String... arguments) {
+        Scanner scanner = new Scanner(System.in);
+        String apiKey = getUserInput("Enter API key", defaultApiKey, scanner);
+        Calendar calendar = Calendar.getInstance();
+        int year = Integer.parseInt(getUserInput("Enter puzzle year",
+                Integer.toString(calendar.get(Calendar.YEAR)), scanner));
+        int day = Integer.parseInt(getUserInput("Enter puzzle day",
+                Integer.toString(calendar.get(Calendar.DAY_OF_MONTH)), scanner));
+        boolean useProductionData = getUserInput("Use sample data (Y/N)?", "Y", scanner).toUpperCase().charAt(0) != 'Y';
+        scanner.close();
+        ImportData dataSource = new ImportData(apiKey, year, day);
+        String className = Main.class.getPackageName() + ".year" + year + ".Day" + day;
+        Puzzle puzzle = null;
+        try {
+            puzzle = (Puzzle)Class.forName(className).getConstructors()[0].newInstance(useProductionData);
+        } catch (ClassNotFoundException classNotFoundException) {
+            System.err.println("Could not find class " + className);
+            System.exit(1);
+        } catch (InstantiationException ignored) {
+            System.err.println(className + " is an abstract class.");
+            System.exit(1);
+        } catch (IllegalAccessException ignored) {
+            System.err.println("The requested constructor for " + className + " is inaccessible.");
+            System.exit(1);
+        } catch (InvocationTargetException invocationTargetException) {
+            System.err.println("The constructor for " + className + " threw an exception.");
+            System.err.println(invocationTargetException.getMessage());
+            Throwable originalException = invocationTargetException.getCause();
+            System.err.println("Caused by: " + originalException);
+            System.exit(1);
+        }
+        puzzle.solvePuzzle(dataSource);
+    }
+}
diff --git a/2022/src/main/java/edu/unl/cse/bohn/Puzzle.java b/2022/src/main/java/edu/unl/cse/bohn/Puzzle.java
new file mode 100644
index 0000000..100704c
--- /dev/null
+++ b/2022/src/main/java/edu/unl/cse/bohn/Puzzle.java
@@ -0,0 +1,28 @@
+package edu.unl.cse.bohn;
+
+import java.io.IOException;
+import java.util.List;
+
+public abstract class Puzzle {
+    protected String sampleData = "";
+    protected boolean isProductionReady;
+
+    public Puzzle(boolean isProductionReady) {
+        this.isProductionReady = isProductionReady;
+    }
+
+    public abstract long computePart1(List<String> data);
+    public abstract long computePart2(List<String> data);
+
+    public void solvePuzzle(ImportData dataSource) {
+        List<String> data = null;
+        try {
+            data = isProductionReady ? dataSource.importData() : List.of(sampleData.split(System.lineSeparator()));
+        } catch (IOException ioException) {
+            System.err.println("Could not retrieve data: " + ioException);
+            System.exit(1);
+        }
+        System.out.println("Part 1: " + computePart1(data));
+        System.out.println("Part 2: " + computePart2(data));
+    }
+}
diff --git a/2022/src/main/java/edu/unl/cse/bohn/year2021 b/2022/src/main/java/edu/unl/cse/bohn/year2021
new file mode 120000
index 0000000..9eb349c
--- /dev/null
+++ b/2022/src/main/java/edu/unl/cse/bohn/year2021
@@ -0,0 +1 @@
+../../../../../../../../2021/src/main/java/edu/unl/cse/bohn/year2021
\ No newline at end of file
diff --git a/2022/src/main/resources/apikeys.TEMPLATE b/2022/src/main/resources/apikeys.TEMPLATE
new file mode 100644
index 0000000..13923fa
--- /dev/null
+++ b/2022/src/main/resources/apikeys.TEMPLATE
@@ -0,0 +1,3 @@
+{
+    "aoc": ""
+}
diff --git a/scaffolding/ImportData.java b/scaffolding/ImportData.java
new file mode 100644
index 0000000..e472acd
--- /dev/null
+++ b/scaffolding/ImportData.java
@@ -0,0 +1,91 @@
+package edu.unl.cse.bohn;
+
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLConnection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+
+public class ImportData {
+
+    public static final String FILENAME = "apikeys.json";
+
+    protected final String protocol;
+    protected final String host;
+    protected final String path;
+    protected final String apiKey;
+
+    @SuppressWarnings("unused")
+    public static List<String> readFile(String filename) throws IOException {
+        List<String> data = new LinkedList<>();
+        BufferedReader bufferedReader;
+        bufferedReader = new BufferedReader(new FileReader(filename));
+        while (bufferedReader.ready()) {
+            data.add(bufferedReader.readLine());
+        }
+        return data;
+    }
+
+    public ImportData(String apiKeyName, int year, int day) {
+        String apiKey;
+        protocol = "https";
+        host = "adventofcode.com";
+        path = "/" + year + "/day/" + day + "/input";
+        try {
+            apiKey = getApiKey(apiKeyName);
+        } catch (IOException ioException) {
+            System.err.println("Could not retrieve API key: " + ioException.getMessage());
+            apiKey = null;
+        }
+        this.apiKey = apiKey;
+    }
+
+    protected String getApiKey(String apiKeyName) throws IOException {
+        JSONObject apiKeyJson;
+        try (InputStreamReader inputStreamReader = new InputStreamReader(
+                Objects.requireNonNull(ImportData.class.getClassLoader().getResourceAsStream(FILENAME)))) {
+            apiKeyJson = (JSONObject)new JSONParser().parse(inputStreamReader);
+        } catch (NullPointerException nullPointerException) {
+            FileNotFoundException newException = new FileNotFoundException("File " + FILENAME + " not found.");
+            newException.initCause(nullPointerException);
+            throw newException;
+        } catch (ParseException parseException) {
+            throw new IOException("Error while parsing file " + FILENAME + ".", parseException);
+        }
+        if (!apiKeyJson.containsKey(apiKeyName)) {
+            System.err.println("WARNING! Could not locate API key named " + apiKeyName + " in file " + FILENAME + ".");
+        }
+        String apiKey = apiKeyJson.get(apiKeyName).toString();
+        if (apiKey.equals("")) {
+            System.err.println("WARNING! API key named " + apiKeyName + " in file " + FILENAME + " is blank.");
+        }
+        return apiKey;
+    }
+
+    public List<String> importData() throws IOException {
+        List<String> data = new LinkedList<>();
+        BufferedReader bufferedReader;
+        try {
+            URLConnection connection = new URI(protocol, host, path, null).toURL().openConnection();
+            connection.setRequestProperty("Cookie", apiKey);
+            bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+        } catch (URISyntaxException | MalformedURLException originalException) {
+            throw new IOException("Could not retrieve usable data from " + host + ".", originalException);
+        }
+        while (bufferedReader.ready()) {
+            data.add(bufferedReader.readLine());
+        }
+        return data;
+    }
+}
diff --git a/scaffolding/Main.java b/scaffolding/Main.java
new file mode 100644
index 0000000..875e4b5
--- /dev/null
+++ b/scaffolding/Main.java
@@ -0,0 +1,49 @@
+package edu.unl.cse.bohn;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Calendar;
+import java.util.Scanner;
+
+public class Main {
+    public static final String defaultApiKey = "aoc";
+
+    private static String getUserInput(String prompt, String defaultValue, Scanner scanner) {
+        System.out.print(prompt + " [" + defaultValue + "]: ");
+        String userInput = scanner.nextLine();
+        return userInput.equals("") ? defaultValue : userInput;
+    }
+
+    public static void main(String... arguments) {
+        Scanner scanner = new Scanner(System.in);
+        String apiKey = getUserInput("Enter API key", defaultApiKey, scanner);
+        Calendar calendar = Calendar.getInstance();
+        int year = Integer.parseInt(getUserInput("Enter puzzle year",
+                Integer.toString(calendar.get(Calendar.YEAR)), scanner));
+        int day = Integer.parseInt(getUserInput("Enter puzzle day",
+                Integer.toString(calendar.get(Calendar.DAY_OF_MONTH)), scanner));
+        boolean useProductionData = getUserInput("Use sample data (Y/N)?", "Y", scanner).toUpperCase().charAt(0) != 'Y';
+        scanner.close();
+        ImportData dataSource = new ImportData(apiKey, year, day);
+        String className = Main.class.getPackageName() + ".year" + year + ".Day" + day;
+        Puzzle puzzle = null;
+        try {
+            puzzle = (Puzzle)Class.forName(className).getConstructors()[0].newInstance(useProductionData);
+        } catch (ClassNotFoundException classNotFoundException) {
+            System.err.println("Could not find class " + className);
+            System.exit(1);
+        } catch (InstantiationException ignored) {
+            System.err.println(className + " is an abstract class.");
+            System.exit(1);
+        } catch (IllegalAccessException ignored) {
+            System.err.println("The requested constructor for " + className + " is inaccessible.");
+            System.exit(1);
+        } catch (InvocationTargetException invocationTargetException) {
+            System.err.println("The constructor for " + className + " threw an exception.");
+            System.err.println(invocationTargetException.getMessage());
+            Throwable originalException = invocationTargetException.getCause();
+            System.err.println("Caused by: " + originalException);
+            System.exit(1);
+        }
+        puzzle.solvePuzzle(dataSource);
+    }
+}
diff --git a/scaffolding/Puzzle.java b/scaffolding/Puzzle.java
new file mode 100644
index 0000000..100704c
--- /dev/null
+++ b/scaffolding/Puzzle.java
@@ -0,0 +1,28 @@
+package edu.unl.cse.bohn;
+
+import java.io.IOException;
+import java.util.List;
+
+public abstract class Puzzle {
+    protected String sampleData = "";
+    protected boolean isProductionReady;
+
+    public Puzzle(boolean isProductionReady) {
+        this.isProductionReady = isProductionReady;
+    }
+
+    public abstract long computePart1(List<String> data);
+    public abstract long computePart2(List<String> data);
+
+    public void solvePuzzle(ImportData dataSource) {
+        List<String> data = null;
+        try {
+            data = isProductionReady ? dataSource.importData() : List.of(sampleData.split(System.lineSeparator()));
+        } catch (IOException ioException) {
+            System.err.println("Could not retrieve data: " + ioException);
+            System.exit(1);
+        }
+        System.out.println("Part 1: " + computePart1(data));
+        System.out.println("Part 2: " + computePart2(data));
+    }
+}
-- 
GitLab