Advent of Code 2018: Day 1

This year I'll be submitting my solutions in Java. I haven't done this in the past because it's rather verbose and not ideal for the hacky little scripts that are usually required for Advent of Code, but it seems like good practice for writing readable code!

Part 1

The first part of Day 1 involves receiving a list of "frequency adjustments" (a positive or negative integer) and summing them. This could be done in one line with something like:

System.out.println(Stream.of(args[0].split("[ \n]")).mapToInt(Integer::parseInt).sum());

but that wasn't what I was going for! So instead I tried to create an extensible system for adjusting frequency:

interface FrequencyAdjustment {

    static FrequencyAdjustment fromString(String adjustmentString) {
        if (adjustmentString.startsWith("+")) {
            return new AdditionFrequencyAdjustment(Integer.parseInt(adjustmentString.substring(1).trim()));
        } else if (adjustmentString.startsWith("-")) {
            return new SubtractionFrequencyAdjustment(Integer.parseInt(adjustmentString.substring(1).trim()));
        } else {
            throw new IllegalArgumentException(String.format("'%s' is not a valid adjustment.", adjustmentString));
        }
    }

    int apply(int accumulator);
}

class AdditionFrequencyAdjustment implements FrequencyAdjustment {
    private final int value;

    AdditionFrequencyAdjustment(int value) {
        this.value = value;
    }

    @Override
    public int apply(int accumulator) {
        return accumulator + value;
    }
}

class SubtractionFrequencyAdjustment implements FrequencyAdjustment {
    private final int value;

    SubtractionFrequencyAdjustment(int value) {
        this.value = value;
    }

    @Override
    public int apply(int accumulator) {
        return accumulator - value;
    }
}

public class FrequencyAdjuster {
    private static final String VALID_INPUT_REGEX = "([+-][1-9][0-9]*[ \\n])*([+-][1-9][0-9]*)?";
    private int result;
    private List<FrequencyAdjustment> adjustments;
    private int adjustmentPointer = 0;

    public FrequencyAdjuster(String frequencyAdjustmentsString) {
        if (!frequencyAdjustmentStringIsValid(frequencyAdjustmentsString)) {
            throw new IllegalArgumentException(String.format("'%s' is not a valid input", frequencyAdjustmentsString));
        }

        adjustments = parse(frequencyAdjustmentsString);
        result = 0;
    }

    public static boolean frequencyAdjustmentStringIsValid(String frequencyAdjustmentsString) {
        return frequencyAdjustmentsString.matches(VALID_INPUT_REGEX);
    }

    private List<FrequencyAdjustment> parse(String frequencyAdjustmentsString) {
        return Stream.of(frequencyAdjustmentsString.split("[ \n]"))
                .map(FrequencyAdjustment::fromString)
                .collect(Collectors.toList());
    }

    void reset() {
        adjustmentPointer = 0;
    }

    int applyOne() {
        FrequencyAdjustment adjustment = adjustments.get(adjustmentPointer);
        result = adjustment.apply(result);

        adjustmentPointer++;

        if (adjustmentPointer >= adjustments.size()) {
            adjustmentPointer = 0;
        }

        return result;
    }

    int applyToEnd() {
        applyOne();

        while (adjustmentPointer != 0) {
            applyOne();
        }

        return result;
    }

    public int get() {
        return result;
    }

    public static void main(String[] args) {
        if (args.length < 1) {
            System.err.println("Error: First argument must be adjustment string");
            System.exit(1);
        }

        String frequencyAdjustments = args[0];
        
        if (!FrequencyAdjuster.frequencyAdjustmentStringIsValid(frequencyAdjustments)) {
            System.err.println("Error: Frequency adjustments are not valid.");
            System.exit(1);
        }

        int result = new FrequencyAdjuster(frequencyAdjustments).applyToEnd();

        System.out.println("The result of running through the adjustments is " + Integer.toString(result));
    }
}

Just a few more lines! But I'm sure I'll be prepared for Part 2 now!

Part 2

Oh... never mind - my part 1 preparation didn't help at all...

Part 2 is to repeatedly loop through the adjustments until we find the first repeated value. Here's my solution for this part, relying on the FrequencyAdjuster from Part 1:

class FrequencyRepeatFinder {
    private FrequencyAdjuster frequencyAdjuster;

    FrequencyRepeatFinder(FrequencyAdjuster frequencyAdjuster) {
        this.frequencyAdjuster = frequencyAdjuster;
    }

    int find() {
        frequencyAdjuster.reset();
        Set<Integer> previousFrequencies = new HashSet<>();

        int nextFrequency = 0;
        while (!previousFrequencies.contains(nextFrequency)) {
            previousFrequencies.add(nextFrequency);

            nextFrequency = frequencyAdjuster.applyOne();
        }

        return nextFrequency;
    }

    public static void main(String[] args) {
        if (args.length < 1) {
            System.err.println("Error: First argument must be adjustment string");
            System.exit(1);
        }

        String frequencyAdjustments = args[0];

        if (!FrequencyAdjuster.frequencyAdjustmentStringIsValid(frequencyAdjustments)) {
            System.err.println("Error: Frequency adjustments are not valid.");
            System.exit(1);
        }

        FrequencyAdjuster frequencyAdjuster = new FrequencyAdjuster(frequencyAdjustments);
        FrequencyRepeatFinder repeatFinder = new FrequencyRepeatFinder(frequencyAdjuster);

        int result = repeatFinder.find();

        System.out.println("The first repeat in frequencies is " + Integer.toString(result));
    }
}

We'll see how long I can be bothered to continue writing actually applications rather than one-liners!

Advent Of Code runs until December 25. You should get involved!

Show Comments

Get the latest posts delivered right to your inbox.