diff --git a/aoc2023/src/main/java/io/github/zebalu/aoc2023/days/Day23.java b/aoc2023/src/main/java/io/github/zebalu/aoc2023/days/Day23.java index f17f9d2..6fea5d7 100644 --- a/aoc2023/src/main/java/io/github/zebalu/aoc2023/days/Day23.java +++ b/aoc2023/src/main/java/io/github/zebalu/aoc2023/days/Day23.java @@ -1,52 +1,22 @@ package io.github.zebalu.aoc2023.days; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.PriorityQueue; -import java.util.Queue; -import java.util.Set; +import java.nio.file.*; +import java.util.*; +import java.util.Map.Entry; +import java.util.function.*; public class Day23 { + public static void main(String[] args) { - String input = readInput(); //example; //readInput(); - System.out.println(input); - List maze = input.lines().toList(); + var maze = readInput().lines().toList(); Coord start = new Coord(1,0); - Coord target = new Coord(maze.getLast().length()-2, maze.size()-1); - System.out.println(longestPath(maze, start, target)); + Coord target = new Coord(maze.size()-2, maze.size()-1); + var longest = longestPathLength(maze, start, target, (m,n)->n.part1Neighbours(m), n->true); + System.out.println(longest); WeightedGraph wg = new WeightedGraph(maze, start, target); System.out.println(wg.longestPath(start, target)); } - private static long longestPath(List maze, Coord start, Coord target) { - Queue queue = new PriorityQueue<>(StepCount.LONGEST_COMPARTOR); - Map steps = new HashMap<>(); - queue.add(new StepCount(start, null, 0));; - steps.put(start, 0); - long longest = 0L; - while(!queue.isEmpty()) { - StepCount at = queue.poll(); - int nextCount = at.count()+1; - at.coord().next(maze).stream().filter(n->!n.equals(at.prev) && n.isValid(maze) && (!steps.containsKey(n)||steps.get(n) < nextCount) &&n.extract(maze) != '#').forEach(n->{ - queue.add(new StepCount(n, at.coord(), nextCount)); - steps.put(n, nextCount); - }); - if(target.equals(at.coord())) { - longest = Math.max(longest, at.count()); - } - } - return longest; - } - private static String readInput() { try { return Files.readString(Path.of("day23.txt").toAbsolutePath()); @@ -55,241 +25,178 @@ private static String readInput() { } } - record Coord(int x, int y) { - boolean isValid(List maze) { - return 0<=x && 0 <= y && x longestPath(List maze, Coord start, Coord target, BiFunction, Coord, List> nextList, Predicate filter) { + SequencedSet result = new LinkedHashSet<>(); + Stack> stack = new Stack<>(); + stack.add(new LinkedHashSet<>(Set.of(start))); + while(!stack.isEmpty()) { + SequencedSet curr = stack.pop(); + if(target.equals(curr.getLast())) { + if (result.size()filter.test(n) && !curr.contains(n)).forEach(n->{ + var next = new LinkedHashSet<>(curr); + next.add(n); + stack.push(next); + }); + } } + return result; + } + + private static int longestPathLength(List maze, Coord start, Coord target, BiFunction, Coord, List> nextList, Predicate filter) { + return longestPath(maze, start, target, nextList, filter).size()-1; + } + + private record Coord(int x, int y) { char extract(List maze) { return maze.get(y).charAt(x); } - 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)); + + char extractAny(List maze) { + if(y<0 || x<0 || maze.size() next(List maze) { - return switch(extract(maze)) { - case '^' -> List.of(new Coord(x, y-1)); - case 'v' -> List.of(new Coord(x, y+1)); - case '>' -> List.of(new Coord(x+1, y)); - case '<' -> List.of(new Coord(x-1, y)); - case '.' -> neighbours(); - default -> throw new IllegalStateException("can not start from: "+extract(maze)); - }; + + Coord north() { + return new Coord(x,y-1); } - } - - record StepCount(Coord coord, Coord prev, int count) { - static Comparator LONGEST_COMPARTOR = Comparator.comparingInt(StepCount::count).reversed(); - } - - record StepCount2(Coord coord, Set prevs, int count) { - static Comparator LONGEST_COMPARTOR = Comparator.comparingInt(StepCount2::count).reversed(); - } - - private static class WeightedGraph { - private Set junktions = new LinkedHashSet<>(); - private Map> directConnections = new LinkedHashMap<>(); - private Map costs = new HashMap<>(); - WeightedGraph(List maze, Coord start, Coord target) { - junktions.add(start); - junktions.add(target); - for(int y=0; yn.isValid(maze) && n.extract(maze)!='#').count(); - if(validNextSteps>2) { - junktions.add(at); - } - } - } - } - fillDirectConnections(maze); + + Coord south() { + return new Coord(x,y+1); } - private void fillDirectConnections(List maze) { - List junktionList = new ArrayList<>(junktions); - for(int i=0; i available = new HashSet<>(); - for(int j=i+1; j0) { - available.add(to); - costs.put(cp1, path); - costs.put(cp2, path); - Set toSet = directConnections.computeIfAbsent(to, k->new HashSet<>()); - toSet.add(from); - } - } - var stored = directConnections.computeIfAbsent(from, k->new HashSet<>()); - stored.addAll(available); - } + Coord west() { + return new Coord(x-1,y); } - private int longestMazePathWithoutOtherJunktions(List maze, Coord start, Coord target) { - Queue queue = new PriorityQueue<>(StepCount2.LONGEST_COMPARTOR); - Map steps = new HashMap<>(); - queue.add(new StepCount2(start, Set.of(start), 0));; - steps.put(start, 0); - int longest = -1; - while(!queue.isEmpty()) { - StepCount2 at = queue.poll(); - int nextCount = at.count()+1; - if(target.equals(at.coord())) { - longest = Math.max(longest, at.count()); - } else { - at.coord().neighbours().stream().filter(n->!at.prevs().contains(n) && n.isValid(maze) && n.extract(maze) != '#' && (!junktions.contains(n) || target.equals(n)) - &&(!steps.containsKey(n) || steps.get(n){ - Set nps = new HashSet<>(at.prevs); - nps.add(at.coord); - queue.add(new StepCount2(n, nps, nextCount)); - steps.put(n, nextCount); - }); - } + Coord east() { + return new Coord(x+1,y); + } + + List part1Neighbours(List maze) { + List result = new ArrayList<>(); + switch(extract(maze)) { + case '.' -> { + appendIfMatch(result, maze, north(), '^'); + appendIfMatch(result, maze, south(), 'v'); + appendIfMatch(result, maze, east(), '>'); + appendIfMatch(result, maze, west(), '<'); } - return longest; + case 'v' -> result.add(south()); + case '^' -> result.add(north()); + case '<' -> result.add(west()); + case '>' -> result.add(east()); + case '#' -> throw new IllegalStateException("can not stand here: "+this); + } + return result; } - long longestPath(Coord start, Coord target) { - Queue queue = new LinkedList<>(); //new PriorityQueue<>(StepCount2.LONGEST_COMPARTOR); - Map steps = new HashMap<>(); - queue.add(new StepCount2(start, Set.of(start), 0));; - steps.put(start, 0); - long longest = -1L; - while(!queue.isEmpty()) { - StepCount2 at = queue.poll(); - directConnections.get(at.coord()).stream().filter(n->!at.prevs().contains(n) - &&(!steps.containsKey(n) || steps.get(n) < at.count+costs.get(new CostPair(at.coord, n)))).forEach(n->{ - Set nps = new HashSet<>(at.prevs); - nps.add(at.coord); - queue.add(new StepCount2(n, nps, at.count+costs.get(new CostPair(at.coord, n)))); - //steps.put(n, at.count+costs.get(new CostPair(at.coord, n))); - }); - if(target.equals(at.coord())) { - longest = Math.max(longest, at.count()); - //System.out.println("at target: "+longest); - } + List part2Neighbours(List maze) { + List result = new ArrayList<>(); + switch(extract(maze)) { + case '#' -> throw new IllegalStateException("can not stand here: "+this); + default -> { + appendIfNotMatch(result, maze, north(), '#'); + appendIfNotMatch(result, maze, south(), '#'); + appendIfNotMatch(result, maze, east(), '#'); + appendIfNotMatch(result, maze, west(), '#'); } - return longest; + } + return result; } - private record CostPair(Coord from, Coord to) { - + private static void appendIfMatch(List collector, List maze, Coord coord, char accepted) { + char at = coord.extractAny(maze); + if(at == '.' || at == accepted) { + collector.add(coord); + } } + private static void appendIfNotMatch(List collector, List maze, Coord coord, char rejected) { + char at = coord.extractAny(maze); + if(at == '.' || at != rejected) { + collector.add(coord); + } + } } - /* - private static class WeightedGraph1 { - private Set junktions = new LinkedHashSet<>(); - private Map> directConnections = new LinkedHashMap<>(); - private Map costs = new HashMap<>(); - WeightedGraph1(List maze, Coord start, Coord target) { - junktions.add(start); - junktions.add(target); - for(int y=0; y forks = new LinkedHashSet<>(); + private SequencedMap forkIds = new LinkedHashMap<>(); + private Map connections = new LinkedHashMap(); + private Map costs = new LinkedHashMap<>(); + + public WeightedGraph(List maze, Coord start, Coord target) { + forks.add(start); + int id = 0; + forkIds.put(start, id); + for(int y=1; yn.isValid(maze) && n.extract(maze)!='#').count(); - if(validNextSteps>2) { - junktions.add(at); + if(ch != '#') { + Coord c = new Coord(x,y); + if(c.part2Neighbours(maze).size()>2) { + forks.add(c); + forkIds.put(c, ++id); } } } } - for(var c: junktions) { - System.out.println("junktion:\t"+c); - } - fillDirectConnections(maze); - for(var dc: directConnections.entrySet()) { - System.out.println("dc:\t"+dc); + forks.add(target); + forkIds.put(target, ++id); + for(int i=0; i maze) { - List junktionList = new ArrayList<>(junktions); - for(int i=0; i available = new HashSet<>(); - for(int j=i+1; j0) { - available.add(to); - costs.put(cp1, path); - costs.put(cp2, path); - Set toSet = directConnections.computeIfAbsent(to, k->new HashSet<>()); - toSet.add(from); + for(Entry iEntry: forkIds.entrySet()) { + for(Entry jEntry: forkIds.entrySet()) { + if(iEntry.getValue() < jEntry.getValue()) { + int length = longestPathLength(maze, iEntry.getKey(), jEntry.getKey(), (m,n)->n.part2Neighbours(m), c->!forks.contains(c) || c.equals(jEntry.getKey())); + if(length > 0) { + connections.get(iEntry.getValue()).set(jEntry.getValue()); + connections.get(jEntry.getValue()).set(iEntry.getValue()); + costs.put(new Edge(iEntry.getValue(), jEntry.getValue()), length); + costs.put(new Edge(jEntry.getValue(), iEntry.getValue()), length); + } } } - var stored = directConnections.computeIfAbsent(from, k->new HashSet<>()); - stored.addAll(available); } } - private int longestMazePathWithoutOtherJunktions(List maze, Coord start, Coord target) { - Queue queue = new PriorityQueue<>(StepCount2.LONGEST_COMPARTOR); - Map steps = new HashMap<>(); - queue.add(new StepCount2(start, Set.of(start), 0));; - steps.put(start, 0); - int longest = -1; - while(!queue.isEmpty()) { - StepCount2 at = queue.poll(); - int nextCount = at.count()+1; - if (target.equals(at.coord())) { - longest = Math.max(longest, at.count()); - } else { - at.coord().neighbours().stream().filter(n -> !at.prevs().contains(n) && n.isValid(maze) - && n.extract(maze) != '#' && (!junktions.contains(n) || target.equals(n)) - && (!steps.containsKey(n) || steps.get(n) { - Set nps = new HashSet<>(at.prevs); - nps.add(at.coord); - queue.add(new StepCount2(n, nps, nextCount)); - steps.put(n, nextCount); - }); - } - } - return longest; - } - - long longestPath(Coord start, Coord target) { - Queue queue = new LinkedList<>();//new PriorityQueue<>(StepCount.LONGEST_COMPARTOR); - Map steps = new HashMap<>(); - queue.add(new StepCount2(start, Set.of(start), 0));; - steps.put(start, 0); - long longest = -1L; - while(!queue.isEmpty()) { - StepCount2 at = queue.poll(); - directConnections.get(at.coord()).stream().filter(n->!at.prevs().contains(n) - && (!steps.containsKey(n) || steps.get(n)<(at.count+costs.get(new CostPair(at.coord, n))))).forEach(n->{ - Set nps = new HashSet<>(at.prevs); - nps.add(at.coord); - queue.add(new StepCount2(n, nps, at.count+costs.get(new CostPair(at.coord, n)))); - steps.put(n, at.count+costs.get(new CostPair(at.coord, n))); - }); - if(target.equals(at.coord())) { - longest = Math.max(longest, at.count()); - System.out.println("at target: "+longest); + int longestPath(Coord start, Coord target) { + int startId = forkIds.get(start); + int targetId = forkIds.get(target); + Stack stack = new Stack<>(); + BitSet startBs = new BitSet(forks.size()); + startBs.set(forkIds.get(start)); + stack.add(new Step(startId, startBs, 0)); + int longest = 0; + while(!stack.isEmpty()) { + Step curr = stack.pop(); + BitSet cons = connections.get(curr.last); + for(int i=0; i