diff --git a/aoc2023/src/main/java/io/github/zebalu/aoc2023/days/Day21.java b/aoc2023/src/main/java/io/github/zebalu/aoc2023/days/Day21.java index 4bb9a74..1fd3ae5 100644 --- a/aoc2023/src/main/java/io/github/zebalu/aoc2023/days/Day21.java +++ b/aoc2023/src/main/java/io/github/zebalu/aoc2023/days/Day21.java @@ -1,29 +1,16 @@ package io.github.zebalu.aoc2023.days; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.Set; -import java.util.function.IntPredicate; -import java.util.function.Predicate; -import java.util.stream.IntStream; +import java.nio.file.*; +import java.util.*; +import java.util.function.*; /** - * This is a mess; I will clean it once... * Based on the formula from: https://github.com/abnew123/aoc2023/blob/main/src/solutions/Day21.java */ public class Day21 { public static void main(String[] args) { - String input = readInput();//example; //readInput(); //example; //readInput(); - //System.out.println(input); + String input = readInput(); List maze = input.lines().toList(); -// List maze = expand(input.lines().toList(), 5); Coord start = null; for(int y=0; yc.y==0)); - System.out.println("out south: \t"+stepsToGetOut(start, maze, c->c.y==maze.size()-1)); - System.out.println("out east: \t"+stepsToGetOut(start, maze, c->c.x==maze.getFirst().length()-1)); - System.out.println("out west: \t"+stepsToGetOut(start, maze, c->c.x==0)); - System.out.println("out north2: \t"+stepsToGetOut(new Coord(start.x, maze.size()-1), maze, c->c.y==0)); - System.out.println(findAllAvailable(start, maze).size()); - System.out.println(findAllAvailableInParity(start, maze, true).size()); - System.out.println(findAllAvailableInParity(start, maze, false).size()); - System.out.println(countStepsToFille(new Coord(0,0), maze, findAllAvailableInParity(new Coord(0,0), maze, true))); - System.out.println(countStepsToFille(new Coord(0,0), maze, findAllAvailableInParity(new Coord(0,0), maze, false))); - System.out.println("------------"); - System.out.println(countStepsToFille(new Coord(start.x,0), maze, findAllAvailableInParity(new Coord(start.x,0), maze, true))); - System.out.println(countStepsToFille(new Coord(start.x,0), maze, findAllAvailableInParity(new Coord(start.x,0), maze, false))); - */ - /* - var ass = assumption(maze, start, 26501365); - System.out.println(ass); - System.out.println("592723929260582\t<--\texpected"); - System.out.println(592723929260582L-ass+"\t<--<\tdiff"); - */ - System.out.println(withDeltaTracking(start, maze, 64)); - System.out.println(withDeltaTracking(start, maze, 26501365)); + System.out.println(withDeltaTracking(start, maze, 64, false)); + System.out.println(withDeltaTracking(start, maze, 26501365, true)); } - private static long assumption(List maze, Coord start, int step) { - long getoutCount = stepsToGetOut(start, maze, c->c.y==0); - long remaing = step-getoutCount; - int tileRadious = (int)remaing / maze.size(); - long borderLength = tileRadious*4; - //tileRadious = 6; - long etc1 = 2*tileRadious-1;//tileRadious; - long otc1 = 2*tileRadious+1;//tileRadious-1; - System.out.println(etc1+"\tetc1"); - System.out.println(otc1+"\totc1"); - long evenTileCount = (etc1+1)*(tileRadious) / 2; - long oddTileCount = (otc1 + 1)*(tileRadious+1) / 2 - borderLength; - System.out.println("otc: "+oddTileCount); - System.out.println("etc: "+evenTileCount); - System.out.println("tile radious: "+tileRadious); - System.out.println("bl: "+borderLength); - long internalPoints = findInternalArea( - List.of(new Coord(-tileRadious, 0), new Coord(0, -tileRadious), new Coord(tileRadious, 0), new Coord(0, tileRadious)), - borderLength) - borderLength; - System.out.println(borderLength+"\tassumption bl"); - System.out.println(internalPoints+"\tassumption ip"); - var allAvailbalePoints = findAllAvailable(start, maze); - var oddAvailablePoints = findAllAvailableInParity(start, maze, false); - var evenAvailablePoints = findAllAvailableInParity(start, maze, true); - long internalCount = internalPoints * oddAvailablePoints.size();// evenTileCount * evenAvailablePoints.size() + oddTileCount * oddAvailablePoints.size(); - System.out.println(internalCount+"\tassumption internal count"); - long westCount = part1(maze, new Coord(maze.get(start.y).length()-1, start.y), maze.size()); - long eastCount = part1(maze, new Coord(0, start.y), maze.size()); - long northCount = part1(maze, new Coord(start.x, maze.size()-1), maze.size()); - long southCount = part1(maze, new Coord(start.x, 0), maze.size()); - long internal_and_vertexes = internalCount + westCount + eastCount + northCount + southCount; - System.out.println(internal_and_vertexes +"\tass-no-border"); - List corners = List.of(new Coord(0,0), - new Coord(maze.getFirst().length()-1,0), - new Coord(maze.size()-1, 0), - new Coord(maze.size()-1, maze.getLast().length()-1)); - long borderPointCount = corners.stream().mapToLong(c->part1(maze, c, maze.size())*(tileRadious)).sum(); - return borderPointCount + internal_and_vertexes; - } - - private static long findInternalArea(List coords, long line) { - long area = 0L; - for(int i=0; i< coords.size(); ++i) { - Coord ci = coords.get(i); - Coord cn = coords.get((i + 1) % coords.size()); - area += ((long)ci.y + cn.y) * (ci.x - cn.x); - } - return (Math.abs(area) + (long)line) / 2 + 1; - } - - private static long part1(List maze, Coord start, final int maxSteps) { - Queue queue = new LinkedList<>(); - Map stepCount = new HashMap<>(); - stepCount.put(start, maxSteps); - queue.add(new State(start, maxSteps)); - Set visited = new HashSet<>(); - visited.add(start); - int stepsc = 0; - for(int i=0; i= 0) { - curr.coord().neighbours().stream().filter(n -> n.isValid(maze) && n.extract(maze) != '#' - && !visited.contains(n) - /* && (stepCount.get(n) == null || stepCount.get(n) < nextStep) */).forEach(pos -> { - stepCount.put(pos, nextStep); - visited.add(pos); - }); - } - } - int ii = i; - visited.forEach(c->{ - queue.add(new State(c, maxSteps-ii-1)); - }); - } - return visited.size(); - } - private static long part1_infinite(List maze, Coord start, final int maxSteps) { - Queue queue = new LinkedList<>(); - Map stepCount = new HashMap<>(); - stepCount.put(start, maxSteps); - queue.add(new State(start, maxSteps)); - Set visited = new HashSet<>(); - visited.add(start); - for(int i=0; i= 0) { - curr.coord().neighbours(maze).stream().filter(n -> n.extract(maze) != '#' - && !visited.contains(n) - /* && (stepCount.get(n) == null || stepCount.get(n) < nextStep) */).forEach(pos -> { - stepCount.put(pos, nextStep); - visited.add(pos); - }); - } - } - int ii = i; - visited.forEach(c->{ - queue.add(new State(c, maxSteps-ii-1)); - }); - } - return visited.size(); - } - private static void part2(List maze, Coord start, final int maxSteps) { - Set validCoordsInOriginalGared = new HashSet<>(); - for(int y=0; y queue = new LinkedList<>(); - queue.add(new State(start, maxSteps)); - int stepsTook = 0; - Set visited = new HashSet<>(); - Set prev = new HashSet<>(); - Set prev2 = new HashSet<>(); - List diffs = new ArrayList<>(); - visited.add(start); - for(int i=0; i(prev); - prev = new HashSet<>(visited); - */ - visited.clear(); - while (!queue.isEmpty()) { - State curr = queue.poll(); - int nextStep = curr.steps() - 1; - if (nextStep >= 0) { - curr.coord().neighbours().stream().filter(n -> n.extractInfinit(maze) != '#' - && !visited.contains(n)).forEach(pos -> { - visited.add(pos); - }); - } - } - int ii = i; - visited.forEach(c->{ - queue.add(new State(c, maxSteps-ii-1)); - }); - } - System.out.println("full gared after: "+stepsTook); - long lll = (maxSteps / stepsTook )*validCoordsInOriginalGared.size(); - System.out.println(visited.size()); - System.out.println(lll); - System.out.println(diffs); - var evens = IntStream.iterate(0, i->ii+2).mapToObj(i->diffs.get(i)).toList(); - var odds = IntStream.iterate(1, i->ii+2).mapToObj(i->diffs.get(i)).toList(); - System.out.println(evens); - System.out.println(odds); - } - - private static long withDeltaTracking(Coord start, List maze, int maxSteps) { + private static long withDeltaTracking(Coord start, List maze, int maxSteps, boolean isInfiniteMap) { long getOutCount = stepsToGetOut(start, maze, c->c.y==0); Set reached = new HashSet<>(); List totals = new ArrayList<>(); @@ -239,10 +37,16 @@ private static long withDeltaTracking(Coord start, List maze, int maxSte int index = 0; while(indexc.neighbours().stream()).filter(c->!reached.contains(c) && c.extractInfinit(maze) != '#').peek(reached::add).toList(); + todo = todo.stream().flatMap(c->c.neighbours().stream()).filter(c-> { + if(isInfiniteMap) { + return c.extractInfinit(maze) != '#' && !reached.contains(c); + } else { + return c.isValid(maze) && c.extract(maze) != '#' && !reached.contains(c); + } + }).peek(reached::add).toList(); if ( index % 2 == maxSteps %2) { totalReached += todo.size(); - if (index % maze.size() == getOutCount) { + if ((index % maze.size()) == getOutCount) { totals.add(totalReached); if(totals.size() > 1){ deltas.add(totals.getLast() - totals.get(totals.size() - 2)); @@ -254,7 +58,6 @@ private static long withDeltaTracking(Coord start, List maze, int maxSte filled = true; } } - } } if (filled) { @@ -283,104 +86,6 @@ private static long stepsToGetOut(Coord start, List maze, Predicate findAllAvailable(Coord start, List maze) { - Set visited = new HashSet<>(); - Queue queue = new LinkedList<>(); - queue.add(start); - while (!queue.isEmpty()) { - var curr = queue.poll(); - curr.neighbours().stream().filter(n -> n.isValid(maze) && n.extract(maze) != '#' && !visited.contains(n)) - .forEach(n -> { - queue.add(n); - visited.add(n); - }); - } - return visited; - } - private static Set findAllAvailableWithinSteps(Coord start, List maze, int steps) { - Set visited = new HashSet<>(); - Queue queue = new LinkedList<>(); - queue.add(start); - while (!queue.isEmpty()) { - var curr = queue.poll(); - curr.neighbours().stream().filter(n -> n.isValid(maze) && n.extract(maze) != '#' && !visited.contains(n)) - .forEach(n -> { - queue.add(n); - visited.add(n); - }); - } - return visited; - } - private static Set findAllAvailableInParity(Coord start, List maze, boolean isEven) { - Set evens = new HashSet<>(); - Set odds = new HashSet<>(); - Set visited = new HashSet<>(); - Queue queue = new LinkedList<>(); - queue.add(new State(start, 0)); - while (!queue.isEmpty()) { - var curr = queue.poll(); - visited.add(curr.coord()); - if(curr.steps()%2==0) { - evens.add(curr.coord()); - } else { - odds.add(curr.coord()); - } - curr.coord().neighbours().stream().filter(n -> n.isValid(maze) && n.extract(maze) != '#' && !visited.contains(n)) - .forEach(n -> { - queue.add(new State(n, curr.steps()+1)); - visited.add(n); - }); - } - return isEven?evens:odds; - } - private static long countStepsToFille(Coord start, List maze, Set targets) { - Queue queue = new LinkedList<>(); - queue.add(new State(start, 0)); - Set visited = new HashSet<>(); - visited.add(start); - int max = -1; - while (!queue.isEmpty() && !visited.containsAll(targets)) { - State curr = queue.poll(); - max = curr.steps() + 1; - curr.coord().neighbours().stream().filter(n -> n.isValid(maze) && n.extract(maze) != '#' - && !visited.contains(n)).forEach(pos -> { - visited.add(pos); - queue.add(new State(pos, curr.steps()+1)); - }); - } - return max; - } - private static boolean allFound(Set target, Set current, Set prev) { - Set common = new HashSet<>(current.size() + prev.size()); - common.addAll(current); - common.addAll(prev); - return common.containsAll(target); - } - private static long countMissing(Set current, Set prev) { - return current.stream().filter(c->!prev.contains(c)).count(); - } - - private static List expand(List original, int times) { - if(times%2!=1) { - throw new IllegalArgumentException("I can only work with odd numbers"); - } - List result = new ArrayList<>(); - for (int i = 0; i < times; ++i) { - for (String o : original) { - String replaced = o.replace('S', '.'); - StringBuilder sb = new StringBuilder(); - for (int j = 0; j < times; ++j) { - if (i == times / 2 && j == times / 2) { - sb.append(o); - } else { - sb.append(replaced); - } - } - result.add(sb.toString()); - } - } - return result; - } private static String readInput() { try { return Files.readString(Path.of("day21.txt").toAbsolutePath()); @@ -388,15 +93,11 @@ private static String readInput() { throw new IllegalStateException(e); } } - private record Coord(int x, int y) { + private record Coord(int x, int y) implements Comparable { + private static final Comparator COMPARATOR = Comparator.comparingInt(Coord::x).thenComparing(Coord::y); List neighbours() { return List.of(new Coord(x-1,y), new Coord(x+1, y), new Coord(x, y-1), new Coord(x, y+1)); } - List neighbours(List maze) { - int width = maze.get(y).length(); - int height = maze.size(); - return List.of(new Coord((x-1+width)%width,y), new Coord((x+1)%width, y), new Coord(x, (y-1+height)%height), new Coord(x, (y+1)%height)); - } boolean isValid(List maze) { return x>=0 && y>=0 && y maze){ int fx = ((x % maze.getFirst().length()) + maze.getFirst().length()) % maze.getFirst().length(); return maze.get(fy).charAt(fx); } + @Override + public int hashCode() { + return x*100_000+y; + } + @Override + public int compareTo(Coord o) { + return COMPARATOR.compare(this, o); + } + } - private record State (Coord coord, int steps) {} - - private static String example = """ -........... -.....###.#. -.###.##..#. -..#.#...#.. -....#.#.... -.##..S####. -.##..#...#. -.......##.. -.##.#.####. -.##..##.##. -..........."""; }