Skip to content
Snippets Groups Projects
Commit 92b65f0f authored by Christopher Bohn's avatar Christopher Bohn :thinking:
Browse files

Finished Year 2022 Day 1

parent 200abbb8
No related branches found
No related tags found
No related merge requests found
......@@ -2,3 +2,34 @@
## Day 1
- [The problem](https://adventofcode.com/2022/day/1)
- [The solution](src/main/java/edu/unl/cse/bohn/year2022/Day1.java)
I like the first few days' problems -- they're simple enough that they make great computational thinking examples for my 100-level students.
Breaking the problem down into subproblems:
- Keep track of how many calories an elf is carrying
- Detect when we've reach the end of the list of items that elf is carrying
- Convert strings to integers
- Of the elves considered so far, keep track of the number of calories being carried by the elf who is carrying the most calories (or the three elves who are carrying the most calories)
- Update that count when we've finished with an elf
- For part 1, we must determine whether the most-recent elf is carrying more calories than the maximum among the previous elves
- For part 2, we must determine whether the most-recent elf is carrying more calories than any of the previous top-three
- Produce the result
- For part 1, the result is the maximum number of calories being carried by any one elf
- For part 2, the result is the sum of the number of calories being carried by the top three
While I wouldn't expect my students to be able to provide solutions in quite so few lines as I did -- we don't teach the Java streams API in CS1 -- they could definitely do this problem.
The only wrinkle is that a blank line isn't the *only* way to reach the end of an elf's list.
### Refactoring opportunity
Parts 1 & 2 differ only in how many "maximum" elves we're tracking and, consequently, how we perform our update.
My part 2 solution used a set that can never grow to more than 3 items because we're tracking the top three elves.
Part 1 can be thought of as a degenerate case of tracking the top "one" elf;
this suggests that it can be solved with a set that can never grow to more than 1 item.
Which means we can use the same solution for both parts, parameterized by the top "number" of elves, which becomes the upper limit of the size of the set.
## Day 2
(coming soon)
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;
}
}
../../../../../../../../scaffolding/ImportData.java
\ No newline at end of file
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);
}
}
../../../../../../../../scaffolding/Main.java
\ No newline at end of file
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));
}
}
../../../../../../../../scaffolding/Puzzle.java
\ No newline at end of file
package edu.unl.cse.bohn.year2022;
import edu.unl.cse.bohn.Puzzle;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@SuppressWarnings({"unused", "CommentedOutCode"})
public class Day1 extends Puzzle {
public Day1(boolean isProductionReady) {
super(isProductionReady);
sampleData = """
1000
2000
3000
4000
5000
6000
7000
8000
9000
10000""";
}
/*
@Override
public long computePart1(List<String> data) {
long maximumCalorieCount = Long.MIN_VALUE;
long currentCalorieCount = 0;
for (String datum: data) {
Long calories = datum.equals("") ? null : Long.valueOf(datum);
if (calories == null) {
maximumCalorieCount = Long.max(maximumCalorieCount, currentCalorieCount);
currentCalorieCount = 0; // make it ready for the next elf
} else {
currentCalorieCount += calories;
}
}
// we still need to compare the last elf's calorie count, because they didn't have a blank line following their last food item
maximumCalorieCount = Long.max(maximumCalorieCount, currentCalorieCount);
return maximumCalorieCount;
}
*/
@Override
public long computePart1(List<String> data) {
return getSumOfMaximumCalorieCounts(data, 1);
}
@Override
public long computePart2(List<String> data) {
return getSumOfMaximumCalorieCounts(data, 3);
}
private static Long getSumOfMaximumCalorieCounts(List<String> data, int numberOfMaximumCalorieCounts) {
Set<Long> maximumCalorieCounts = new HashSet<>();
long currentCalorieCount = 0;
for (String datum: data) {
Long calories = datum.equals("") ? null : Long.valueOf(datum);
if (calories == null) {
updateMaximumCounts(maximumCalorieCounts, currentCalorieCount, numberOfMaximumCalorieCounts);
currentCalorieCount = 0;
} else {
currentCalorieCount += calories;
}
}
updateMaximumCounts(maximumCalorieCounts, currentCalorieCount, numberOfMaximumCalorieCounts);
return maximumCalorieCounts.stream().reduce(0L, Long::sum);
}
private static void updateMaximumCounts(Set<Long> maximumCalorieCounts,
long currentCalorieCount,
int numberOfMaximumCalorieCounts) {
long calorieCountToAdd = currentCalorieCount;
if (maximumCalorieCounts.size() > numberOfMaximumCalorieCounts - 1) {
long thirdMaximumCalorieCount = maximumCalorieCounts.stream().min(Long::compareTo).orElseThrow();
maximumCalorieCounts.remove(thirdMaximumCalorieCount);
calorieCountToAdd = Long.max(thirdMaximumCalorieCount, calorieCountToAdd);
}
maximumCalorieCounts.add(calorieCountToAdd);
}
}
......@@ -2,5 +2,5 @@
My solutions to the [Advent of Code](https://adventofcode.com/) puzzles.
I'm making [notes](2021/README.md) about the 2021 puzzles and my solutions.
I'm making [notes](2022/README.md) about the 2022 puzzles and my solutions.
......@@ -78,7 +78,7 @@ public class ImportData {
BufferedReader bufferedReader;
try {
URLConnection connection = new URI(protocol, host, path, null).toURL().openConnection();
connection.setRequestProperty("Cookie", apiKey);
connection.setRequestProperty("Cookie", "session=" + apiKey);
bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
} catch (URISyntaxException | MalformedURLException originalException) {
throw new IOException("Could not retrieve usable data from " + host + ".", originalException);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment