-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
apply the much cleaner and more cleaver solution of @p-kovacs
- Loading branch information
Showing
1 changed file
with
51 additions
and
223 deletions.
There are no files selected for viewing
274 changes: 51 additions & 223 deletions
274
aoc2023/src/main/java/io/github/zebalu/aoc2023/days/Day12.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,259 +1,87 @@ | ||
package io.github.zebalu.aoc2023.days; | ||
|
||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.nio.file.*; | ||
import java.util.*; | ||
import java.util.stream.IntStream; | ||
import java.util.stream.Stream; | ||
|
||
public class Day12 { | ||
public static void main(String[] args) { | ||
String input = readInput();//readInput(); | ||
var as = input.lines().map(Arrangement::parse).toList(); | ||
System.out.println(as.stream().parallel().mapToLong(a->count(a, new State(0,0,0), new HashMap<>())).sum()); | ||
System.out.println(as.stream().map(a->a.unFold(5)).parallel().mapToLong(a->count(a, new State(0,0,0), new HashMap<>())).sum()); | ||
var as = readInput().lines().map(Arrangement::parse).toList(); | ||
System.out.println(as.stream().parallel().mapToLong(a -> count(a, 0, 0, new HashMap<>())).sum()); | ||
System.out.println(as.stream().map(a -> a.unFold(5)).parallel().mapToLong(a -> count(a, 0, 0, new HashMap<>())).sum()); | ||
} | ||
|
||
private static String readInput() { | ||
try { | ||
return Files.readString(Path.of("day12.txt").toAbsolutePath()); | ||
} catch (IOException e) { | ||
throw new IllegalStateException(e); | ||
} catch (Exception e) { | ||
throw new IllegalStateException(e); | ||
} | ||
} | ||
|
||
private record Arrangement(char[] desc, List<Integer> nums) { | ||
static Arrangement parse(String line) { | ||
var parts = line.split(" "); | ||
return new Arrangement(parts[0].toCharArray(), Arrays.stream(parts[1].split(",")).map(Integer::parseInt).toList()); | ||
} | ||
|
||
boolean isFilled() { | ||
return IntStream.range(0, desc.length).mapToObj(i->(Character)desc[i]).allMatch(c->c!='?'); | ||
} | ||
|
||
boolean isCorrect() { | ||
List<Integer> counted = countHasheGroups(); | ||
if(counted.size()!=nums.size()) { | ||
return false; | ||
} else { | ||
for(int i=0; i<nums.size(); ++i) { | ||
if(nums.get(i) != counted.get(i)) { | ||
return false; | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
// original part1: slow | ||
long countPossibles(Set<String> visited) { | ||
visited.add(asString()); | ||
if(isCorrect()) { | ||
return 1L; | ||
} else if(isFilled()) { | ||
return 0L; | ||
} else { | ||
return extensions(visited).mapToLong(a->a.countPossibles(visited)).sum(); | ||
} | ||
} | ||
|
||
Stream<String> asPossibleStrings(Set<String> visited) { | ||
visited.add(asString()); | ||
if(isCorrect()) { | ||
return Stream.of(asString()); | ||
} else if(isFilled()) { | ||
return Stream.empty(); | ||
} else { | ||
return extensions(visited).flatMap(a->a.asPossibleStrings(visited)); | ||
} | ||
} | ||
|
||
String asString() { | ||
return new String(desc); | ||
return new Arrangement(parts[0].toCharArray(), | ||
Arrays.stream(parts[1].split(",")).map(Integer::parseInt).toList()); | ||
} | ||
|
||
Stream<Arrangement> extensions(Set<String> visited) { | ||
if(isFilled()) { | ||
return Stream.empty(); | ||
} else { | ||
return IntStream.range(0, desc.length).filter(i->desc[i]=='?').mapToObj(i->{ | ||
//List<Integer> cpy = new ArrayList<>(nums); | ||
char[] nDesc = Arrays.copyOf(desc, desc.length); | ||
nDesc[i] = '#'; | ||
return new Arrangement(nDesc, nums); | ||
}).filter(a->!visited.contains(a.asString())); | ||
} | ||
} | ||
|
||
List<Integer> countHasheGroups() { | ||
List<Integer> result = new ArrayList<>(); | ||
int current = 0; | ||
for(int i=0; i<desc.length; ++i) { | ||
if(desc[i]=='#') { | ||
++current; | ||
} else if(current != 0) { | ||
result.add(current); | ||
current = 0; | ||
} | ||
} | ||
if(current!=0) { | ||
result.add(current); | ||
} | ||
return result; | ||
} | ||
|
||
|
||
Arrangement unFold(int folds) { | ||
List<Character> newChars = new ArrayList<>(); | ||
char[] nc = new char[desc.length * folds + folds - 1]; | ||
List<Integer> newNums = new ArrayList<>(); | ||
for(int i=0; i<folds; ++i) { | ||
for(int j=0; j<desc.length; ++j) { | ||
newChars.add(desc[j]); | ||
int k = 0; | ||
for (int i = 0; i < folds; ++i) { | ||
for (int j = 0; j < desc.length; ++j) { | ||
nc[k++] = desc[j]; | ||
} | ||
newNums.addAll(nums); | ||
if(i<folds-1) { | ||
newChars.add('?'); | ||
if (i < folds - 1) { | ||
nc[k++] = '?'; | ||
} | ||
} | ||
char[] nc = new char[newChars.size()]; | ||
for(int i=0; i<nc.length; ++i) { | ||
nc[i] = newChars.get(i); | ||
} | ||
return new Arrangement(nc, newNums); | ||
} | ||
|
||
} | ||
|
||
private record State(int pos, int blockPos, long currLen) { | ||
|
||
} | ||
|
||
// credits: https://github.com/jonathanpaulson/AdventOfCode/blob/master/2023/12.py | ||
private static long count(Arrangement arrangement, State state, Map<State, Long> prev) { | ||
if (prev.containsKey(state)) { | ||
return prev.get(state); | ||
} | ||
if (state.pos == arrangement.desc.length) { | ||
if (state.blockPos == arrangement.nums.size() && state.currLen == 0) { | ||
return 1L; | ||
} else if (state.blockPos == arrangement.nums.size() - 1 | ||
&& arrangement.nums.get(state.blockPos) == state.currLen) { | ||
return 1L; | ||
} else { | ||
return 0L; | ||
} | ||
} | ||
char at = arrangement.desc[state.pos]; | ||
long sum = 0L; | ||
for (char c : new char[] { '.', '#' }) { | ||
if (at == c || at == '?') { | ||
if (c == '.' && state.currLen == 0) { | ||
sum += count(arrangement, new State(state.pos + 1, state.blockPos, state.currLen), prev); | ||
} else if (c == '.' && state.currLen > 0 && state.blockPos < arrangement.nums.size() | ||
&& arrangement.nums.get(state.blockPos) == state.currLen) { | ||
sum += count(arrangement, new State(state.pos + 1, state.blockPos + 1, 0), prev); | ||
|
||
} else if (c == '#') { | ||
sum += count(arrangement, new State(state.pos + 1, state.blockPos, state.currLen + 1), prev); | ||
} | ||
} | ||
int countRequiredLengthFrom(int group) { | ||
return IntStream.range(group, nums.size()).map(i -> nums.get(i) + 1).sum(); | ||
} | ||
prev.put(state, sum); | ||
return sum; | ||
} | ||
|
||
// original part 2: many minutes | ||
private static Set<List<Integer>> findAllPos(int fromC, int fromL, Arrangement arrangement) { | ||
if(fromL==arrangement.nums.size()) { | ||
return Set.of(new ArrayList<>()); | ||
} | ||
Set<List<Integer>> result = new HashSet<>(); | ||
boolean shouldStop = false; | ||
for(int i=fromC; i<arrangement.desc.length; ++i) { | ||
List<Integer> current = new ArrayList<>(); | ||
boolean isPossible = true; | ||
|
||
int length = arrangement.nums.get(fromL); | ||
int pos = canStartFrom(i, length, arrangement.desc); | ||
if(pos<0) { | ||
current.clear(); | ||
isPossible = false; | ||
//return Set.of(new ArrayList<>()); | ||
} else { | ||
Set<List<Integer>> found = findAllPos(pos+length+1, fromL+1, arrangement); | ||
found.stream().peek(l-> l.add(0, pos)).forEach(result::add); | ||
if(contains(pos, pos+length, '#', arrangement.desc)) { | ||
return result; | ||
} | ||
} | ||
|
||
} | ||
return result; | ||
} | ||
|
||
private static boolean contains(int start, int end, char ch, char[] chars) { | ||
for(int i=start; i<end; ++i) { | ||
if(chars[i] == ch) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
// credit: https://github.com/p-kovacs/advent-of-code-2023/blob/master/src/main/java/com/github/pkovacs/aoc/y2023/Day12.java | ||
private static long count(Arrangement arrangement, int fieldIndex, int groupIndex, Map<Long, Long> cache) { | ||
long cacheKey = toKey(fieldIndex, groupIndex); | ||
if (cache.containsKey(cacheKey)) { | ||
return cache.get(cacheKey); | ||
} | ||
if (groupIndex == arrangement.nums().size()) { | ||
return noneMatch(arrangement.desc(), fieldIndex, arrangement.desc().length, '#') ? 1 : 0; | ||
} | ||
long count = 0; | ||
int length = arrangement.nums().get(groupIndex); | ||
int maxPos = arrangement.desc().length - arrangement.countRequiredLengthFrom(groupIndex) + 1; | ||
boolean canContinue = true; | ||
for (int i = fieldIndex; i <= maxPos && canContinue; i++) { | ||
if (i > fieldIndex && arrangement.desc()[i - 1] == '#') { | ||
canContinue = false; | ||
} else if (noneMatch(arrangement.desc(), i, i + length, '.') | ||
&& (i == arrangement.desc().length - length || arrangement.desc()[i + length] != '#')) { | ||
count += count(arrangement, i + length + 1, groupIndex + 1, cache); | ||
} | ||
} | ||
cache.put(cacheKey, count); | ||
return count; | ||
} | ||
|
||
private static int canStartFrom(int start, int length, char[] chars) { | ||
if(chars.length<start+length) { | ||
return -1; | ||
} | ||
for(int i=start; i<chars.length; ++i) { | ||
if(chars[i]!='.') { | ||
if(isAllPossible(chars, i, length)) { | ||
return i; | ||
} else { | ||
return -1; | ||
} | ||
private static boolean noneMatch(char[] array, int from, int to, char ch) { | ||
for (int i = from; i < to; i++) { | ||
if (array[i] == ch) { | ||
return false; | ||
} | ||
} | ||
return -1; | ||
return true; | ||
} | ||
|
||
private static boolean isAllPossible(char[] chars, int pos, int length) { | ||
if(chars.length<pos+length) { | ||
return false; | ||
} else { | ||
for (int i = pos; i < pos + length; ++i) { | ||
if (chars[i] == '.') { | ||
return false; | ||
} | ||
} | ||
if(pos>0 && pos+length<chars.length) { | ||
return chars[pos-1]!='#' && chars[pos+length] != '#'; | ||
} | ||
if(pos == 0) { | ||
if(length == chars.length) { | ||
return true; | ||
} | ||
return chars[pos+length+1] != '#'; | ||
} | ||
if(pos+length == chars.length) { | ||
return chars[pos-1] != '#'; | ||
} | ||
return true; | ||
} | ||
private static long toKey(long fieldIndex, long groupIndex) { | ||
return (fieldIndex << 32) | groupIndex; | ||
} | ||
|
||
private static final String example = """ | ||
???.### 1,1,3 | ||
.??..??...?##. 1,1,3 | ||
?#?#?#?#?#?#?#? 1,3,1,6 | ||
????.#...#... 4,1,1 | ||
????.######..#####. 1,6,5 | ||
?###???????? 3,2,1"""; | ||
|
||
} |