Архив метки: adventofcode

Advent of Code 2023: Day 4: Scratchcards

Про задачу четвертого дня сказать особенно нечего. По условию — проще третей. По парсингу ввода — проще второй. Решается быстро.

Сразу прикинул, что с использованием примитивов или оборачиванием в типы из стандартной библиотеки — будет портяночно. Поэтому — отдельный класс с логикой:

public class Card {
    private static final Predicate<String> EMPTY = s -> " ".equals(s) || "".equals(s);
    Integer cardN, winCnt;
    Set<Integer> win, have;
    List<Card> nextCards = new ArrayList<>(); // added for part 2
    public Card(String cardN, String[] win, String[] have) {
        this.cardN = Integer.parseInt(cardN.replaceAll("^Card\\s+", ""));
        this.win = parseNums(win);
        this.have = parseNums(have);
        this.winCnt = (int) this.have.stream().filter(h -> this.win.contains(h)).count();
    }
    void addNext(List<Card> nextCards) { // added for part 2
        this.nextCards.addAll(nextCards);
    }
    Stream<Card> flat() { // added for part 2
        return Stream.concat(Stream.of(this),
                this.nextCards.stream().flatMap(Card::flat));
    }
    private static Set<Integer> parseNums(String[] nums) {
        return Arrays.stream(nums).filter(not(EMPTY)).map(Integer::parseInt)
                .collect(Collectors.toSet());
    }
};
Читать далее Advent of Code 2023: Day 4: Scratchcards

Advent of Code 2023: Day 3: Gear Ratios

В начале была лень… Стойкое нежелание возиться с границами массивов. Оно, и только оно толкнуло меня на скользкую дорожку замены матрицы примитивов на List<List<>>.

Следующим шагом на пути окунания в пучины многословия — стало создание контейнеров для упаковки данных со «схемы двигателя». Вот они:

public record Coord(Integer row, Integer column){};
public record Cell(Coord coord, Integer codePoint){
    Set<Coord> border() {return Set.of(
            new Coord(coord.row(), coord.column() - 1), // left
            new Coord(coord.row() - 1, coord.column() - 1), // top-left
            new Coord(coord.row() - 1, coord.column()), // top
            new Coord(coord.row() - 1, coord.column() + 1), // top-right
            new Coord(coord.row(), coord.column() + 1), // right
            new Coord(coord.row() + 1, coord.column() + 1), // bot-right
            new Coord(coord.row() + 1, coord.column()), // bottom
            new Coord(coord.row() + 1, coord.column() - 1) // bot-left
    );}
};
public record Part(List<Cell> cells){
    public Part {
        cells = List.copyOf(cells);
    }
    Set<Coord> border() {
        return cells.stream()
            .flatMap(c -> c.border().stream())
            .filter(bCoord -> cells.stream()
                    .noneMatch(c -> bCoord.equals(c.coord())))
            .collect(Collectors.toSet());
    }
    Integer value() {
        return Integer.parseInt(cells.stream()
                .map(c -> Character.toChars(c.codePoint()))
                .map(String::new).collect(Collectors.joining()));
    }
};
Читать далее Advent of Code 2023: Day 3: Gear Ratios

Advent of Code 2023: Day 2: Cube Conundrum

Страшный, длинный, мучительный парсинг ввода…
Хотелось бы, конечно, иметь возможность сделать по-перловому, что-то типа: echo -e "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green\nGame 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue" | perl -wlne '@matches = $_ =~ /(?:\G(?!\A)|^Game) (\S+)/g; print join("=",@matches)'.

Немного почистить после, или сразу докрутить регулярку, чтобы не захватывала разделители. Сложить в хэш. И закончить кучей циклов в итоге 🙁

С другой стороны — и возня с java Matcher не вдохновляла, и описывать структуру в виде POJO — для чисто скриптового решения — зачем.

split — дёшево и сердито, fastpath бонусом. К счастью — ввод был одинаков для обеих частей загадки.

Читать далее Advent of Code 2023: Day 2: Cube Conundrum

Advent of Code 2023: Day 1: Trebuchet?!

Снова AoC — и снова начат с опозданием. Дуплет! Основная идея прежняя — решать в jshell, пока это не станет слишком многословным. Экономия на буковках, в общем.

Обвязка для загрузки условий задачи — та же, что для AoC-2022. Тег для задачек 2023 года — adventofcode-2023 (или раздел Problem Solving).

Читать далее Advent of Code 2023: Day 1: Trebuchet?!

Advent of Code 2022: Day 21

Снова мартышки — те, да не те. Эти позадорней, поживей, поактивней 🙂 Но решение чем-то схожее — соседи, операции, кидают банан чиселку, занимаются эквилибристикой. Для этой загадки обвязка получилась потолще, конечно.

Мартышкины ужимки

enum Action {
    ADD('+', Long::sum),
    SUBTRACT('-', (op1, op2) -> op1 - op2),
    DIVIDE('/', (op1, op2) -> op1 / op2),
    MULTIPLY('*', (op1, op2) -> op1 * op2);
    final char code;
    final BiFunction<Long, Long, Long> func;
    static final Map<Character, Action> OPS = Arrays.stream(Action.values())
        .collect(Collectors.toUnmodifiableMap(it -> it.code, it -> it));
    Action(char code, BiFunction<Long, Long, Long> func) {
        this.code = code;
        this.func = func;
    }
    BiFunction<Long, Long, Long> invert(boolean isPrev) {
        switch (this) {
            case ADD: return (op1, op2) -> op2 - op1;
            case MULTIPLY: return (op1, op2) -> op2 / op1;
            case SUBTRACT: return isPrev ? ADD.func : this.func;
            case DIVIDE: return isPrev ? MULTIPLY.func : this.func;
            default: return null;
        }
    }
}
Читать далее Advent of Code 2022: Day 21

Advent of Code 2022: Day 20

Настало Появилось время продолжить AoC за, уже оставшийся в прошлом, 2022 год.

Задачка двадцатого дня была чем-то похожа на обезьяньи игры — потому не вызвала слишком много затруднений.

Упростить решение помогло наличие библиотечного метода Math.floorMod (про который я вычитал в треде на реддите).

Перемешивание координат

static void mix(List<Map.Entry<Integer, Long>> mixed, List<Map.Entry<Integer, Long>> reference) {
    for (var coordinate : reference) {
        int idx = mixed.indexOf(coordinate);
        mixed.remove(idx);
        int newIdx = Math.floorMod(idx + coordinate.getValue(), mixed.size());
        mixed.add(newIdx, coordinate);
    }
}

ЕМНИП, получилось нечто вроде двойного хэширования. Но с ходу приспособить что-то типа LinkedHashMap для хранения координат не удалось, а дольше искать было лень. Так что — остановился на списках.

Вычисление результата

static void day20(String puzzleInputUri) throws IOException, InterruptedException {
    long key = 811589153; // 1 for part 1
    int mixes = 10; // 1 for part 1

    AtomicInteger idx = new AtomicInteger(0);
    var encrypted = client.send(request.uri((URI.create(puzzleInputUri))).build(), BodyHandlers.ofLines())
        .body()
        .map(coord -> Map.entry(idx.getAndIncrement(), Long.parseLong(coord) * key))
        .collect(Collectors.toList());

    var reference = new ArrayList<>(encrypted);
    for (int i = 0; i < mixes; i++) {
        mix(encrypted, reference);
    }

    encrypted.stream()
        .filter(c -> c.getValue() == 0)
        .findAny()
        .map(encrypted::indexOf)
        .map(startIdx -> IntStream.of(startIdx + 1000, startIdx + 2000, startIdx + 3000)
            .map(i -> i % encrypted.size())
            .mapToLong(i -> encrypted.get(i).getValue())
            .sum()
        )
        .ifPresent(System.out::println);
}

Надеюсь, загадки оставшихся дней будут не сильно сложнее 🙂

Исходные данные: https://adventofcode.com/2022/day/20/input

Advent of Code 2022: Day 19

Тяжело дались мне эти игры в Factorio. Хотя, вроде бы, с виду и похожи на слоновьи пляски на вентилях.

В этой задачке тоже пришлось погуглить подсказки, когда нашел варианты отсечек для рекурсии — дело пошло значительно веселее!

Виновница торжества

static int mostGeodes(int ore, int clay, int obsidian, int geode, int oreBot, int clayBot,
                      int obsidianBot, int geodeBot, int time, int maxTime, int[] botsCost) {
    if (time == maxTime) {
        return geode;
    }
    int oreTotal = ore + oreBot;
    int clayTotal = clay + clayBot;
    int obsidianTotal = obsidian + obsidianBot;
    int geodeTotal = geode + geodeBot;

    if (ore >= botsCost[4] && obsidian >= botsCost[5]) {
        return mostGeodes(oreTotal - botsCost[4], clayTotal, obsidianTotal - botsCost[5],
            geodeTotal, oreBot, clayBot, obsidianBot, geodeBot + 1, time + 1, maxTime, botsCost);
    }
    if (clayBot >= botsCost[3] && obsidianBot < botsCost[5] && ore >= botsCost[2] && clay >= botsCost[3]) {
        return mostGeodes(oreTotal - botsCost[2], clayTotal - botsCost[3], obsidianTotal, geodeTotal,
            oreBot, clayBot, obsidianBot + 1, geodeBot, time + 1, maxTime, botsCost);
    }

    int best = 0;
    if (obsidianBot < botsCost[5] && ore >= botsCost[2] && clay >= botsCost[3]) {
        best = Math.max(best, mostGeodes(oreTotal - botsCost[2], clayTotal - botsCost[3], obsidianTotal,
            geodeTotal, oreBot, clayBot, obsidianBot + 1, geodeBot, time + 1, maxTime, botsCost));
    }
    if (clayBot < botsCost[3] && ore >= botsCost[1]) {
        best = Math.max(best, mostGeodes(oreTotal - botsCost[1], clayTotal, obsidianTotal, geodeTotal, oreBot,
            clayBot + 1, obsidianBot, geodeBot, time + 1, maxTime, botsCost));
    }
    if (oreBot < 4 && ore >= botsCost[0]) {
        best = Math.max(best, mostGeodes(oreTotal - botsCost[0], clayTotal, obsidianTotal, geodeTotal,
            oreBot + 1, clayBot, obsidianBot, geodeBot, time + 1, maxTime, botsCost));
    }
    if (ore <= 4) {
        best = Math.max(best, mostGeodes(oreTotal, clayTotal, obsidianTotal, geodeTotal, oreBot, clayBot, obsidianBot,
            geodeBot, time + 1, maxTime, botsCost));
    }
    return best;
}

Сильно сократила решение

static void day19(String puzzleInputUri) throws IOException, InterruptedException {
    var blueprints = client.send(request.uri((URI.create(puzzleInputUri))).build(), BodyHandlers.ofLines())
        .body()
        .map(blueprint -> Arrays.stream(blueprint.split("[^0-9]+")).skip(1).mapToInt(Integer::parseInt).toArray())
        .collect(Collectors.toList());

    int answer1 = 0;
    int answer2 = 1;
    for (int i = 0; i < blueprints.size(); i++) {
        int[] cost = blueprints.get(i);
        answer1 += cost[0] * mostGeodes(0, 0, 0, 0, 1, 0, 0, 0, 0, 24, Arrays.copyOfRange(cost, 1, cost.length));
        if (i < 3) {
            answer2 *= mostGeodes(0, 0, 0, 0, 1, 0, 0, 0, 0, 32, Arrays.copyOfRange(cost, 1, cost.length));
        }
    }
    System.out.printf("Answer 1: %d %nAnswer 2: %d", answer1, answer2);
}
Исходные данные: https://adventofcode.com/2022/day/19/input

Advent of Code 2022: Day 18

Загадка восемнадцатого дня, на мой вкус, могла бы служить образцом для многих предыдущих загадок.

Минимум возни с парсингом ввода и кодированием очередных обходов графа. Максимум размышлений над поисками решения.

В то же время, само решение — не переусложнённое. Похожей по соотношению интерес/сложность для меня была задачка с обработкой сигналов.

Решение тут получилось компактным

static void day18(String puzzleInputUri) throws IOException, InterruptedException {
    List<Point> points = client.send(request.uri((URI.create(puzzleInputUri))).build(), HttpResponse.BodyHandlers.ofLines())
        .body()
        .map(line -> line.split(","))
        .map(xyz -> new Point(Integer.parseInt(xyz[0]), Integer.parseInt(xyz[1]), Integer.parseInt(xyz[2])))
        .collect(Collectors.toUnmodifiableList());
    long area1 = area(points);
    System.out.println("Answer 1: " + area1);
    List<Point> voids = findVoids(new Point(0, 0, 0), makeCube(points));
    System.out.println("Answer 2: " + (area1 - area(voids)));
}
Читать далее Advent of Code 2022: Day 18

Advent of Code 2022: Day 17

Тетрис, больше тетриса! Этот тетрис оказался поголоволомней прошлого. Общая идея была понятна сразу, но «на местности» постоянно вылезали какие-то подводные камешки 🙂

Сначала думал пойти через byte[][], выставляя нужные биты в единицу (на что намекала ширина поля), но закопался в сдвигах (жаль, что ввод не оказался набором сдвиговых операций, или я не разгадал его).

Ну, хотя бы с идеей движения «окном» не промахнулся — во второй части она пригодилась.

Подготовительная работа

enum Move {
    LEFT,
    RIGHT;
    private static final Map<Character, Move> cache = Map.of('<', LEFT, '>', RIGHT);
    static Move of(char code) {
        return cache.get(code);
    }
}

class Triple {
    public int left;
    public int middle;
    public List<Integer> right;
    public Triple(int left, int middle, List<Integer> right) {
        this.left = left;
        this.middle = middle;
        this.right = right;
    }
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        } else if (!(obj instanceof Triple)) {
            return false;
        } else {
            Triple other = (Triple) obj;
            return Objects.equals(left, other.left)
                && Objects.equals(middle, other.middle)
                && Objects.equals(right, other.right);
        }
    }
    public int hashCode() {
        return Objects.hashCode(left) ^ Objects.hashCode(middle)
            ^ Objects.hashCode(right);
    }
}

static boolean hasMove(boolean[][] shape, int x, int y, Map<Integer, boolean[]> rows) {
    for (int yIdx = 0; yIdx < shape.length; yIdx++) {
        int currentY = y + (shape.length - yIdx);
        if (rows.containsKey(currentY)) {
            boolean[] shapeRow = shape[yIdx];
            boolean[] fieldRow = rows.get(currentY);
            for (int xIdx = 0; xIdx < shapeRow.length; xIdx++) {
                if (fieldRow[x + xIdx] && shapeRow[xIdx]) return false;
            }
        }
    }
    return true;
}

Triple позаимствовал в Apache Commons, чтобы не изгаляться с импортом в консольный скрипт.

Читать далее Advent of Code 2022: Day 17

Advent of Code 2022: Day 16

Пропустил пятнадцатый день. Не осилил… Пока что! Что ж, настало время дня шестнадцатого.

Правда, здесь тоже не обошлось без «помощи зала» — часть решения пришлось подсмотреть в интернете. Иначе упорно не желала разгадываться вторая половина загадки.

В целом — задачи становятся интересней (и парсинг ввода не причиняет неудобств, как бывало). Но и времени занимают ощутимо больше, чем первая десятка.

Следить за котлом!

record Valve(String name, long flow, List<String> linked) {}
record State(Map<String, Long> openValves, Valve player, Valve elephant, long totalFlow) {}

static List<State> openValve(Valve v1, Valve v2, boolean both, Map<String, Valve> valves, State s, long flow) {
    if(v1.flow() > 0 && !s.openValves().containsKey(v1.name()) && (!both || (v2.flow() > 0 && !s.openValves().containsKey(v2.name())))) {
        Map<String, Long> newOpen = new HashMap<>(s.openValves());
        newOpen.put(v1.name(), v1.flow());
        if (both) {
            newOpen.put(v2.name(), v2.flow());
            return List.of(new State(newOpen, v1, v2, flow));
        }
        return v2.linked().stream().map(name -> new State(newOpen, v1, valves.get(name), flow)).collect(Collectors.toList());
    }
    return List.of();
}
Читать далее Advent of Code 2022: Day 16