diff --git a/java/src/main/java/day5/Main.java b/java/src/main/java/day5/Main.java new file mode 100644 index 0000000..b7c0446 --- /dev/null +++ b/java/src/main/java/day5/Main.java @@ -0,0 +1,132 @@ +package day5; + +import javafx.util.Pair; +import util.FileReader; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class Main { + private static List>> seedToSoilMappers = new ArrayList<>(); + private static List>> soilToFertilizerMappers = new ArrayList<>(); + private static List>> fertilizerToWaterMappers = new ArrayList<>(); + private static List>> waterToLightMappers = new ArrayList<>(); + private static List>> lightToTemperatureMappers = new ArrayList<>(); + private static List>> temperatureToHumidityMappers = new ArrayList<>(); + private static List>> humidityToLocationMappers = new ArrayList<>(); + + private static final boolean IS_SEEDS_RANGE_ENABLED = true; // Controls if part 1 or 2 of the problem is executed + private static final String SEEDS_PREFIX = "seeds: "; + private static final Map>>> HEADERS_TO_MAPPERS = + new LinkedHashMap>>>() {{ + put("seed-to-soil map:", seedToSoilMappers); + put("soil-to-fertilizer map:", soilToFertilizerMappers); + put("fertilizer-to-water map:", fertilizerToWaterMappers); + put("water-to-light map:", waterToLightMappers); + put("light-to-temperature map:", lightToTemperatureMappers); + put("temperature-to-humidity map:", temperatureToHumidityMappers); + put("humidity-to-location map:", humidityToLocationMappers); + }}; + + private static final List>>> MAPPERS = new ArrayList<>(HEADERS_TO_MAPPERS.values()); + + + public static void main(String[] args) { + String inputFilePath = System.getProperty("user.dir") + "\\java\\src\\main\\java\\day5\\input.txt"; + FileReader reader = new FileReader(inputFilePath); + List inputData = new ArrayList<>(reader.readContents()); + + SeedGenerator seedGenerator = getSeedGenerator(inputData.remove(0)); + buildElementMappers(inputData, seedGenerator); + + Function mergedMappers = MAPPERS.stream() + .map(Main::createMappersFunction) + .reduce(Function.identity(), Function::andThen); + + Long closestLocation = Long.MAX_VALUE; + while (seedGenerator.hasNext()) { + Long seed = seedGenerator.getSeed(); + Long location = mergedMappers.apply(seed); + closestLocation = Math.min(closestLocation, location); + seedGenerator.pushSkip(); + } + + System.out.println(closestLocation); + } + + private static SeedGenerator getSeedGenerator(String seedsInputData) { + List splitSeedsInputData = Arrays.asList(seedsInputData.replace(SEEDS_PREFIX, "").split(" ")); + List> seedRanges; + + if (!IS_SEEDS_RANGE_ENABLED) { + seedRanges = splitSeedsInputData.stream() + .map(Long::parseLong) + .map(seedValue -> new Pair(seedValue, 1L)) + .collect(Collectors.toList()); + } else { + seedRanges = new ArrayList<>(); + for (Integer index = 0; index < splitSeedsInputData.size(); index = index + 2) { + Long seedRangeStart = Long.parseLong(splitSeedsInputData.get(index)); + Long seedRangeLength = Long.parseLong(splitSeedsInputData.get(index + 1)); + seedRanges.add(new Pair<>(seedRangeStart, seedRangeLength)); + } + } + + return new SeedGenerator(seedRanges); + } + + private static void buildElementMappers(List inputData, SeedGenerator seedGenerator) { + AtomicReference>>> currentMapper = new AtomicReference<>(); + inputData.stream() + .filter(string -> !string.isEmpty()) + .forEachOrdered(string -> { + if (HEADERS_TO_MAPPERS.containsKey(string)) { + currentMapper.set(HEADERS_TO_MAPPERS.get(string)); + } else { + String[] splitString = string.split(" ", 3); + addMapperFunction( + currentMapper.get(), + seedGenerator, + Long.parseLong(splitString[0]), + Long.parseLong(splitString[1]), + Long.parseLong(splitString[2]) + ); + } + }); + } + + private static void addMapperFunction( + List>> mappersList, + SeedGenerator seedGenerator, + Long outputRangeStart, + Long inputRangeStart, + Long rangeLength + ) { + mappersList.add((inputNumber) -> { + if (inputNumber < inputRangeStart) { + seedGenerator.addSkip(inputRangeStart - inputNumber); + return Optional.empty(); + } else if (inputNumber >= inputRangeStart + rangeLength) { + return Optional.empty(); + } + seedGenerator.addSkip(inputRangeStart + rangeLength - inputNumber); + return Optional.of(outputRangeStart + inputNumber - inputRangeStart); + }); + } + + private static Function createMappersFunction(List>> mappers) { + return (inputValue) -> mappers.stream() + .map(mapper -> mapper.apply(inputValue)) + .filter(Optional::isPresent) + .map(Optional::get) + .findFirst() + .orElse(inputValue); + } +} diff --git a/java/src/main/java/day5/SeedGenerator.java b/java/src/main/java/day5/SeedGenerator.java new file mode 100644 index 0000000..3a5e1dd --- /dev/null +++ b/java/src/main/java/day5/SeedGenerator.java @@ -0,0 +1,47 @@ +package day5; + +import javafx.util.Pair; + +import java.util.ArrayList; +import java.util.List; + +public class SeedGenerator { + + private final List> seedRanges; + private Long listIndex = 0L; + private Long rangeIndex = 0L; + private List skipValues = new ArrayList<>(); + + public SeedGenerator(List> seedRanges) { + this.seedRanges = seedRanges; + } + + public Long getSeed() { + if (rangeIndex >= seedRanges.get(Math.toIntExact(listIndex)).getValue()) { + listIndex++; + rangeIndex = 0L; + } + Long seed = seedRanges.get(Math.toIntExact(listIndex)).getKey() + rangeIndex; + rangeIndex++; + return seed; + } + + public boolean hasNext() { + return !(listIndex >= seedRanges.size() - 1 + && rangeIndex >= seedRanges.get(seedRanges.size() - 1).getValue()); + } + + public void addSkip(Long skipValue) { + skipValues.add(skipValue - 1); + } + + public void pushSkip() { + if (skipValues.isEmpty()) { + listIndex++; + rangeIndex = 0L; + } else { + rangeIndex += skipValues.stream().sorted().findFirst().orElse(0L); + skipValues = new ArrayList<>(); + } + } +}