package com.mattrixwv.adventOfCode24.days; import java.util.ArrayList; import java.util.List; import com.mattrixwv.Stopwatch; import com.mattrixwv.Triple; public class Problem12 extends Problem{ private static final String inputFileName = "days/Problem11.txt"; private List map; private List> patrolHistory; private List> loopLocations; //private int loopCounter = 0; //private int LOOP_MAX = 7; //private int LOOP_MAX = 100; public Problem12(){ super(); description = "Find the number of places that a new obstacle could be placed that creates a patrol loop."; result = "Unresolved"; } public String runSolution(){ Stopwatch timer = new Stopwatch(); timer.start(); throw new RuntimeException("This problem is not yet solved"); //Read the file /* map = new ArrayList<>(); try(Scanner scanner = new Scanner(this.getClass().getClassLoader().getResourceAsStream(inputFileName))){ scanner.useDelimiter("\n"); while(scanner.hasNext()){ map.add(scanner.next()); } } /* */ //Answer: 6 /* map = new ArrayList<>(List.of( "....#.....", ".........#", "..........", "..#.......", ".......#..", "..........", ".#..^.....", "........#.", "#.........", "......#..." )); /* */ //Answer: 19 /* map = new ArrayList<>(List.of( "...........#.....#......", "...................#....", "...#.....##.............", "......................#.", "..................#.....", "..#.....................", "....................#...", "........................", ".#........^.............", "..........#..........#..", "..#.....#..........#....", "........#.....#..#......" )); /* */ /* //Find the current location of the guard Triple currentLocation = findInitialLocation(); patrolHistory = new ArrayList<>(); loopLocations = new ArrayList<>(); //printMap(); //Loop while the guard is still on the map traverseMap(currentLocation, false); //printMap(); placePotentialLoops(); //System.out.println("\n\n"); printMap(); //Save the results timer.stop(); result = "The number of positions that a new object could be placed to create a patrol loop is " + loopLocations.size() + ".\nIt took " + timer.toString() + " to run the algorithm."; return result; /* */ } private Triple findInitialLocation(){ for(int row = 0;row < map.size();++row){ String mapLine = map.get(row); int startIndex = mapLine.indexOf("^"); if(startIndex != -1){ return new Triple<>(row, startIndex, '^'); } } //If it gets this far it was not found return new Triple<>(-1, -1, '\0'); } private void traverseMap(Triple currentLocation, boolean loopCheck){ boolean inBounds = true; while(inBounds){ if(patrolHistory.contains(currentLocation)){ patrolHistory.add(currentLocation); break; } else{ patrolHistory.add(currentLocation); } //Move in that direction as far as possible if(currentLocation.getC() == '^'){ currentLocation = moveUp(currentLocation, loopCheck); } else if(currentLocation.getC() == '>'){ currentLocation = moveRight(currentLocation, loopCheck); } else if(currentLocation.getC() == 'v'){ currentLocation = moveDown(currentLocation, loopCheck); } else if(currentLocation.getC() == '<'){ currentLocation = moveLeft(currentLocation, loopCheck); } else{ throw new RuntimeException("There was an error in the location: " + currentLocation); } //Determine if still in bounds //if(loopCounter > LOOP_MAX || currentLocation.getC() == '\0'){ if(currentLocation.getC() == '\0'){ inBounds = false; } //++loopCounter; } } private Triple moveUp(Triple currentLocation, boolean loopCheck){ while(true){ String currentMapString = map.get(currentLocation.getA()); //If the current Y value is == 0 then the guard left the area if(currentLocation.getA() == 0){ currentMapString = currentMapString.replace("^", "X"); map.set(currentLocation.getA(), currentMapString); return new Triple<>(-1, -1, '\0'); } String nextMapString = map.get(currentLocation.getA() - 1); //If the next Y value contains a # then the guard needs to move to the next stage if(nextMapString.charAt(currentLocation.getB()) == '#'){ currentMapString = currentMapString.replace("^", ">"); map.set(currentLocation.getA(), currentMapString); return new Triple<>(currentLocation.getA(), currentLocation.getB(), '>'); } //If the next Y value is a . or X then move to the new location and mark the current location with an X else{ currentMapString = currentMapString.replace("^", "X"); map.set(currentLocation.getA(), currentMapString); StringBuilder newMapString = new StringBuilder(nextMapString); newMapString.setCharAt(currentLocation.getB(), '^'); map.set(currentLocation.getA() - 1, newMapString.toString()); currentLocation = new Triple<>(currentLocation.getA() - 1, currentLocation.getB(), '^'); //Check for the potential to create a loop if(!loopCheck){ Triple potentialLoop = new Triple<>(currentLocation.getA() - 1, currentLocation.getB(), '>'); if(createsLoop(potentialLoop) && !loopContains(potentialLoop)){ loopLocations.add(potentialLoop); } } } } } private Triple moveRight(Triple currentLocation, boolean loopCheck){ while(true){ String currentMapString = map.get(currentLocation.getA()); //If the current X value is == the length of the string then the guard left the area if(currentLocation.getB() == (currentMapString.length() - 1)){ currentMapString = currentMapString.replace(">", "X"); map.set(currentLocation.getA(), currentMapString); return new Triple<>(-1, -1, '\0'); } String nextMapString = map.get(currentLocation.getA()); //If the next X value contains a # then the guard needs to move to the next stage if(nextMapString.charAt(currentLocation.getB() + 1) == '#'){ currentMapString = currentMapString.replace(">", "v"); map.set(currentLocation.getA(), currentMapString); return new Triple<>(currentLocation.getA(), currentLocation.getB(), 'v'); } //If the next X value is a . or X then move to the new location and mark the current location with an X else{ currentMapString = currentMapString.replace(">", "X"); map.set(currentLocation.getA(), currentMapString); StringBuilder newMapString = new StringBuilder(nextMapString); newMapString.setCharAt(currentLocation.getB() + 1, '>'); map.set(currentLocation.getA(), newMapString.toString()); currentLocation = new Triple<>(currentLocation.getA(), currentLocation.getB() + 1, '>'); //Check for the potential to create a loop if(!loopCheck){ Triple potentialLoop = new Triple<>(currentLocation.getA(), currentLocation.getB() + 1, 'v'); if(createsLoop(potentialLoop) && !loopContains(potentialLoop)){ loopLocations.add(potentialLoop); } } } } } private Triple moveDown(Triple currentLocation, boolean loopCheck){ while(true){ String currentMapString = map.get(currentLocation.getA()); //If the current Y value is == the length of the string then the guard left the area if(currentLocation.getA() == (map.size() - 1)){ currentMapString = currentMapString.replace("v", "X"); map.set(currentLocation.getA(), currentMapString); return new Triple<>(-1, -1, '\0'); } String nextMapString = map.get(currentLocation.getA() + 1); //If the next Y value contains a # then the guard needs to move to the next stage if(nextMapString.charAt(currentLocation.getB()) == '#'){ currentMapString = currentMapString.replace("v", "<"); map.set(currentLocation.getA(), currentMapString); return new Triple<>(currentLocation.getA(), currentLocation.getB(), '<'); } //If the next Y value is a . or X then move to the new location and mark the current location with an X else{ currentMapString = currentMapString.replace("v", "X"); map.set(currentLocation.getA(), currentMapString); StringBuilder newMapString = new StringBuilder(nextMapString); newMapString.setCharAt(currentLocation.getB(), 'v'); map.set(currentLocation.getA() + 1, newMapString.toString()); currentLocation = new Triple<>(currentLocation.getA() + 1, currentLocation.getB(), 'v'); //Check for the potential to create a loop if(!loopCheck){ Triple potentialLoop = new Triple<>(currentLocation.getA() + 1, currentLocation.getB(), '<'); if(createsLoop(potentialLoop) && !loopContains(potentialLoop)){ loopLocations.add(potentialLoop); } } } } } private Triple moveLeft(Triple currentLocation, boolean loopCheck){ while(true){ String currentMapString = map.get(currentLocation.getA()); //If the current X value is == 0 then the guard left the area if(currentLocation.getB() == 0){ currentMapString = currentMapString.replace("<", "X"); map.set(currentLocation.getA(), currentMapString); return new Triple<>(-1, -1, '\0'); } String nextMapString = map.get(currentLocation.getA()); //If the next X value contains a # then the guard needs to move to the next stage if(nextMapString.charAt(currentLocation.getB() - 1) == '#'){ currentMapString = currentMapString.replace("<", "^"); map.set(currentLocation.getA(), currentMapString); return new Triple<>(currentLocation.getA(), currentLocation.getB(), '^'); } //If the next X value is a . or X then move to the new location and mark the current location with an X else{ currentMapString = currentMapString.replace("<", "X"); map.set(currentLocation.getA(), currentMapString); StringBuilder newMapString = new StringBuilder(nextMapString); newMapString.setCharAt(currentLocation.getB() - 1, '<'); map.set(currentLocation.getA(), newMapString.toString()); currentLocation = new Triple<>(currentLocation.getA(), currentLocation.getB() - 1, '<'); //Check for the potential to create a loop if(!loopCheck){ Triple potentialLoop = new Triple<>(currentLocation.getA(), currentLocation.getB() - 1, '^'); if(createsLoop(potentialLoop)){ loopLocations.add(potentialLoop); } } } } } private boolean createsLoop(Triple newObstacleLocation){ if(loopContains(newObstacleLocation)){ return false; } if(newObstacleLocation.getA() >= map.size()){ return false; } if(newObstacleLocation.getA() < 0){ return false; } if(newObstacleLocation.getB() >= map.get(newObstacleLocation.getA()).length()){ return false; } if(newObstacleLocation.getB() < 0){ return false; } if(map.get(newObstacleLocation.getA()).charAt(newObstacleLocation.getB()) != '.'){ return false; } boolean loop = false; List originalMap = new ArrayList<>(map); List> originalHistory = new ArrayList<>(patrolHistory); patrolHistory = new ArrayList<>(); //int orgLoopCnt = loopCounter; //loopCounter = 0; //int orgLoopMax = LOOP_MAX; //LOOP_MAX = 1000; StringBuilder mapLine = new StringBuilder(map.get(newObstacleLocation.getA())); mapLine.setCharAt(newObstacleLocation.getB(), '#'); map.set(newObstacleLocation.getA(), mapLine.toString()); traverseMap(originalHistory.get(0), true); /* */ if(newObstacleLocation.getC() == '^'){ //traverseMap(new Triple<>(newObstacleLocation.getA(), newObstacleLocation.getB() + 1, newObstacleLocation.getC()), true); } else if(newObstacleLocation.getC() == '>'){ //traverseMap(new Triple<>(newObstacleLocation.getA() + 1, newObstacleLocation.getB(), newObstacleLocation.getC()), true); } else if(newObstacleLocation.getC() == 'v'){ //traverseMap(new Triple<>(newObstacleLocation.getA(), newObstacleLocation.getB() - 1, newObstacleLocation.getC()), true); } else if(newObstacleLocation.getC() == '<'){ //traverseMap(new Triple<>(newObstacleLocation.getA() - 1, newObstacleLocation.getB(), newObstacleLocation.getC()), true); } /* */ int lastLocation = patrolHistory.indexOf(patrolHistory.get(patrolHistory.size() - 1)); if((lastLocation != -1) && (lastLocation != patrolHistory.size() - 1)){ loop = true; } map = originalMap; patrolHistory = originalHistory; //loopCounter = orgLoopCnt; //LOOP_MAX = orgLoopMax; return loop; } private boolean loopContains(Triple loopLocation){ for(Triple location : loopLocations){ if(location.getA() == loopLocation.getA() && location.getB() == loopLocation.getB()){ return true; } } return false; } private void printMap(){ for(String mapLine : map){ System.out.println(mapLine); } } private void placePotentialLoops(){ for(Triple loopLocation : loopLocations){ StringBuilder mapLine = new StringBuilder(map.get(loopLocation.getA())); mapLine.setCharAt(loopLocation.getB(), 'O'); map.set(loopLocation.getA(), mapLine.toString()); } } } //! Too small /* 360 */ //! Too large /* 1938 */ //! ? /* 1065 */ //! ? /* Find the number of places that a new obstacle could be placed that creates a patrol loop. The number of positions that a new object could be placed to create a patrol loop is 1682. It took 1.641 seconds to run the algorithm. */ //! How? /* 1723 */