From 6b9777e97cb7d996946c3e0ce9c4a4e14edfd708 Mon Sep 17 00:00:00 2001 From: pareronia <49491686+pareronia@users.noreply.github.com> Date: Sat, 9 Dec 2023 13:55:09 +0100 Subject: [PATCH] AoC 2023 Day 7 - java --- README.md | 2 +- src/main/java/AoC2023_07.java | 119 ++++++++++++++++++ .../com/github/pareronia/aoc/Counter.java | 30 +++++ .../com/github/pareronia/aoc/StringOps.java | 28 ++++- 4 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 src/main/java/AoC2023_07.java create mode 100644 src/main/java/com/github/pareronia/aoc/Counter.java diff --git a/README.md b/README.md index efb70d77..bd27a536 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | | ---| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | python3 | [✓](src/main/python/AoC2023_01.py) | [✓](src/main/python/AoC2023_02.py) | [✓](src/main/python/AoC2023_03.py) | [✓](src/main/python/AoC2023_04.py) | [✓](src/main/python/AoC2023_05.py) | [✓](src/main/python/AoC2023_06.py) | [✓](src/main/python/AoC2023_07.py) | [✓](src/main/python/AoC2023_08.py) | [✓](src/main/python/AoC2023_09.py) | | | | | | | | | | | | | | | | | -| java | [✓](src/main/java/AoC2023_01.java) | [✓](src/main/java/AoC2023_02.java) | [✓](src/main/java/AoC2023_03.java) | [✓](src/main/java/AoC2023_04.java) | | [✓](src/main/java/AoC2023_06.java) | | [✓](src/main/java/AoC2023_08.java) | [✓](src/main/java/AoC2023_09.java) | | | | | | | | | | | | | | | | | +| java | [✓](src/main/java/AoC2023_01.java) | [✓](src/main/java/AoC2023_02.java) | [✓](src/main/java/AoC2023_03.java) | [✓](src/main/java/AoC2023_04.java) | | [✓](src/main/java/AoC2023_06.java) | [✓](src/main/java/AoC2023_07.java) | [✓](src/main/java/AoC2023_08.java) | [✓](src/main/java/AoC2023_09.java) | | | | | | | | | | | | | | | | | | bash | | | | | | | | | | | | | | | | | | | | | | | | | | | c++ | | | | | | | | | | | | | | | | | | | | | | | | | | | julia | | | | | | | | | | | | | | | | | | | | | | | | | | diff --git a/src/main/java/AoC2023_07.java b/src/main/java/AoC2023_07.java new file mode 100644 index 00000000..7bcb8b60 --- /dev/null +++ b/src/main/java/AoC2023_07.java @@ -0,0 +1,119 @@ +import static com.github.pareronia.aoc.IterTools.enumerateFrom; +import static com.github.pareronia.aoc.Utils.toAString; +import static java.util.Comparator.comparing; + +import java.util.Comparator; +import java.util.List; +import java.util.function.Function; + +import com.github.pareronia.aoc.Counter; +import com.github.pareronia.aoc.StringOps; +import com.github.pareronia.aoc.StringOps.StringSplit; +import com.github.pareronia.aoc.Utils; +import com.github.pareronia.aoc.solution.Sample; +import com.github.pareronia.aoc.solution.Samples; +import com.github.pareronia.aoc.solution.SolutionBase; + +public final class AoC2023_07 + extends SolutionBase, Integer, Integer> { + + private AoC2023_07(final boolean debug) { + super(debug); + } + + public static AoC2023_07 create() { + return new AoC2023_07(false); + } + + public static AoC2023_07 createDebug() { + return new AoC2023_07(true); + } + + @Override + protected List parseInput(final List inputs) { + return inputs.stream().map(Hand::fromInput).toList(); + } + + private int solve(final List hands, final Comparator order) { + return enumerateFrom(1, hands.stream().sorted(order)) + .mapToInt(e -> e.index() * e.value().bid()) + .sum(); + } + + @Override + public Integer solvePart1(final List hands) { + return solve(hands, Hand.comparator()); + } + + @Override + public Integer solvePart2(final List hands) { + return solve(hands, Hand.comparatorWithJokers()); + } + + @Override + @Samples({ + @Sample(method = "part1", input = TEST, expected = "6440"), + @Sample(method = "part2", input = TEST, expected = "5905"), + }) + public void samples() { + } + + public static void main(final String[] args) throws Exception { + AoC2023_07.create().run(); + } + + record Hand( + int bid, + int value, + String strength, + int valueWithJokers, + String strengthWithJokers + ) { + private static final Character JOKER = 'J'; + private static final String BEST = "AAAAA"; + + public static Hand fromInput(final String line) { + final Function getValue = cards -> { + final List> mc + = new Counter<>(Utils.asCharacterStream(cards).toList()).mostCommon(); + return (int) (2 * mc.get(0).count() + (mc.size() > 1 ? mc.get(1).count() : 0)); + }; + final Function withJokers = cards -> { + final Counter c = new Counter<>( + Utils.asCharacterStream(cards).filter(ch -> ch != JOKER).toList()); + if (c.isEmpty()) { + return BEST; + } + final Character best = c.mostCommon().get(0).value(); + return Utils.asCharacterStream(cards) + .map(ch -> ch == JOKER ? best : ch) + .collect(toAString()); + }; + + final StringSplit split = StringOps.splitOnce(line, " "); + final int bid = Integer.parseInt(split.right()); + final int value = getValue.apply(split.left()); + final String strength = StringOps.translate(split.left(), "TJQKA", "BCDEF"); + final int valueWithJokers = getValue.apply(withJokers.apply(split.left())); + final String strengthWithJokers = StringOps.translate(split.left(), "TJQKA", "B0DEF"); + return new Hand(bid, value, strength, valueWithJokers, strengthWithJokers); + } + + public static Comparator comparator() { + return comparing(Hand::value).thenComparing(comparing(Hand::strength)); + } + + public static Comparator comparatorWithJokers() { + return comparing(Hand::valueWithJokers) + .thenComparing(comparing(Hand::strengthWithJokers)); + } + } + + private static final String TEST = """ + 32T3K 765 + T55J5 684 + KK677 28 + KTJJT 220 + QQQJA 483 + """; +} diff --git a/src/main/java/com/github/pareronia/aoc/Counter.java b/src/main/java/com/github/pareronia/aoc/Counter.java new file mode 100644 index 00000000..b9f69d1e --- /dev/null +++ b/src/main/java/com/github/pareronia/aoc/Counter.java @@ -0,0 +1,30 @@ +package com.github.pareronia.aoc; + +import static java.util.stream.Collectors.counting; +import static java.util.stream.Collectors.groupingBy; + +import java.util.List; +import java.util.Map; + +public class Counter { + + private final Map counts; + + public Counter(final Iterable iterable) { + this.counts = Utils.stream(iterable.iterator()) + .collect(groupingBy(o -> o, counting())); + } + + public boolean isEmpty() { + return this.counts.isEmpty(); + } + + public List> mostCommon() { + return this.counts.entrySet().stream() + .sorted((e1, e2) -> Long.compare(e2.getValue(), e1.getValue())) + .map(e -> new Entry(e.getKey(), e.getValue())) + .toList(); + } + + public record Entry(T value, long count) {} +} diff --git a/src/main/java/com/github/pareronia/aoc/StringOps.java b/src/main/java/com/github/pareronia/aoc/StringOps.java index e9129b9d..af4decc1 100644 --- a/src/main/java/com/github/pareronia/aoc/StringOps.java +++ b/src/main/java/com/github/pareronia/aoc/StringOps.java @@ -16,6 +16,13 @@ public static List splitLines(final String input) { return asList((Objects.requireNonNull(input) + "\n").split("\\r?\\n")); } + public record StringSplit(String left, String right) {} + + public static StringSplit splitOnce(final String string, final String regex) { + final String[] splits = Objects.requireNonNull(string).split(regex); + return new StringSplit(splits[0], splits[1]); + } + public static List> toBlocks(final List inputs) { if (inputs.isEmpty()) { return Collections.emptyList(); @@ -23,11 +30,11 @@ public static List> toBlocks(final List inputs) { final List> blocks = new ArrayList<>(); int i = 0; final int last = inputs.size() - 1; - blocks.add(new ArrayList()); + blocks.add(new ArrayList<>()); for (int j = 0; j <= last; j++) { if (inputs.get(j).isEmpty()) { if (j != last) { - blocks.add(new ArrayList()); + blocks.add(new ArrayList<>()); i++; } } else { @@ -77,14 +84,14 @@ public static char[] move(final char[] ch, final int from, final int to) { assertTrue(from != to, () -> "Expected from and to to be different"); if (from < to) { final char[] ch1 = subarray(ch, 0, from); - final char[] ch2 = new char[] { ch[from] }; + final char[] ch2 = { ch[from] }; final char[] ch3 = subarray(ch, from + 1, to + 1); final char[] ch4 = subarray(ch, to + 1, ch.length); return addAll(ch1, addAll(ch3, addAll(ch2, ch4))); } else { final char[] ch1 = subarray(ch, 0, to); final char[] ch2 = subarray(ch, to, from); - final char[] ch3 = new char[] { ch[from] }; + final char[] ch3 = { ch[from] }; final char[] ch4 = subarray(ch, from + 1, ch.length); return addAll(ch1, addAll(ch3, addAll(ch2, ch4))); } @@ -127,4 +134,17 @@ public static char nextLetter(final char c, final int shift) { throw new IllegalArgumentException("Expected alphabetic char"); } } + + public static String translate(final String string, final String from, final String to) { + AssertUtils.assertTrue( + Objects.requireNonNull(from).length() == Objects.requireNonNull(to).length(), + () -> "from and to should be same length"); + final char[] tmp = new char[string.length()]; + for (int i = 0; i < string.length(); i++) { + final char ch = string.charAt(i); + final int idx = from.indexOf(ch); + tmp[i] = idx < 0 ? ch : to.charAt(idx); + } + return new String(tmp); + } } \ No newline at end of file