159 lines
4.8 KiB
D
159 lines
4.8 KiB
D
|
import std.file;
|
||
|
import std.string;
|
||
|
import std.conv;
|
||
|
import std.algorithm;
|
||
|
import std.stdio;
|
||
|
import std.array;
|
||
|
import std.format;
|
||
|
import std.ascii;
|
||
|
|
||
|
bool rangesOverlap(long startA, long endA, long startB, long endB) {
|
||
|
return (startA <= endB && startB <= endA) || (startB <= endA && startA <= endB);
|
||
|
}
|
||
|
|
||
|
struct Mapping {
|
||
|
long destinationRangeStart, sourceRangeStart, length;
|
||
|
|
||
|
long sourceRangeEnd() const {
|
||
|
return sourceRangeStart + length - 1;
|
||
|
}
|
||
|
|
||
|
long destinationRangeEnd() const {
|
||
|
return destinationRangeStart + length - 1;
|
||
|
}
|
||
|
|
||
|
long diff() const {
|
||
|
return destinationRangeStart - sourceRangeStart;
|
||
|
}
|
||
|
|
||
|
bool containsSourceValue(long value) {
|
||
|
return value >= sourceRangeStart && value <= sourceRangeEnd;
|
||
|
}
|
||
|
|
||
|
bool containsDestinationValue(long value) {
|
||
|
return value >= destinationRangeStart && value <= destinationRangeEnd;
|
||
|
}
|
||
|
|
||
|
long map(long value) {
|
||
|
if (containsSourceValue(value)) return value + diff;
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
long unmap(long value) {
|
||
|
if (containsDestinationValue(value)) return value - diff;
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
bool destinationOverlapsWithOtherSource(Mapping other) {
|
||
|
return rangesOverlap(destinationRangeStart, destinationRangeEnd, other.sourceRangeStart, other.sourceRangeEnd);
|
||
|
}
|
||
|
|
||
|
/// Failed attempt to optimize this solution.
|
||
|
Mapping[] combine(Mapping second) {
|
||
|
if (!destinationOverlapsWithOtherSource(second)) return [this, second];
|
||
|
long overlapStart = destinationRangeStart >= second.sourceRangeStart
|
||
|
? destinationRangeStart
|
||
|
: second.sourceRangeStart;
|
||
|
long overlapEnd = destinationRangeEnd <= second.sourceRangeEnd
|
||
|
? destinationRangeEnd
|
||
|
: second.sourceRangeEnd;
|
||
|
writefln!"Combining mapping %s and %s. Overlap: [%d, %d]"(this, second, overlapStart, overlapEnd);
|
||
|
Mapping[] combination;
|
||
|
return combination;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct Almanac {
|
||
|
long[] seeds;
|
||
|
Mapping[][] mappings;
|
||
|
}
|
||
|
|
||
|
Almanac parseAlmanac(string filename) {
|
||
|
string[] lines = File(filename).byLineCopy().array;
|
||
|
Almanac a;
|
||
|
a.seeds = lines[0].split(":")[1].split.map!(to!long).array;
|
||
|
Mapping[] currentMapping;
|
||
|
for (int i = 2; i < lines.length; i++) {
|
||
|
if (lines[i].length == 0 || !isDigit(lines[i][0])) {
|
||
|
if (currentMapping.length > 0) a.mappings ~= currentMapping;
|
||
|
currentMapping = [];
|
||
|
} else {
|
||
|
Mapping m;
|
||
|
formattedRead(lines[i], "%d %d %d", m.destinationRangeStart, m.sourceRangeStart, m.length);
|
||
|
currentMapping ~= m;
|
||
|
}
|
||
|
}
|
||
|
if (currentMapping.length > 0) a.mappings ~= currentMapping;
|
||
|
return a;
|
||
|
}
|
||
|
|
||
|
/// A failed attempt to optimize this!
|
||
|
Mapping[] combineMappingSets(Mapping[] firstSet, Mapping[] secondSet) {
|
||
|
Mapping[] result;
|
||
|
|
||
|
foreach (Mapping first; firstSet) {
|
||
|
foreach (Mapping second; secondSet) {
|
||
|
if (first.destinationOverlapsWithOtherSource(second)) {
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void part1() {
|
||
|
Almanac a = parseAlmanac("input.txt");
|
||
|
long minValue = 1_000_000_000;
|
||
|
foreach (long seed; a.seeds) {
|
||
|
long value = seed;
|
||
|
foreach (Mapping[] mappingSet; a.mappings) {
|
||
|
bool mapped = false;
|
||
|
foreach (Mapping mapping; mappingSet) {
|
||
|
if (mapping.containsSourceValue(value)) {
|
||
|
value = mapping.map(value);
|
||
|
mapped = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
minValue = min(minValue, value);
|
||
|
}
|
||
|
writeln(minValue);
|
||
|
}
|
||
|
|
||
|
void part2() {
|
||
|
import std.parallelism;
|
||
|
Almanac a = parseAlmanac("input.txt");
|
||
|
long[][] seedRanges;
|
||
|
for (int i = 0; i < a.seeds.length; i+=2) {
|
||
|
long seedRangeStart = a.seeds[i];
|
||
|
long seedRangeLength = a.seeds[i+1];
|
||
|
seedRanges ~= [seedRangeStart, seedRangeLength];
|
||
|
writefln!"Mapping %d seeds starting from %d."(seedRangeLength, seedRangeStart);
|
||
|
}
|
||
|
foreach (long[] seedRange; parallel(seedRanges)) {
|
||
|
long minValue = 1_000_000_000_000;
|
||
|
const long seedCount = seedRange[1];
|
||
|
for (long seed = seedRange[0]; seed < seedRange[0] + seedCount; seed++) {
|
||
|
long value = seed;
|
||
|
foreach (Mapping[] mappingSet; a.mappings) {
|
||
|
bool mapped = false;
|
||
|
foreach (Mapping mapping; mappingSet) {
|
||
|
if (mapping.containsSourceValue(value)) {
|
||
|
value = mapping.map(value);
|
||
|
mapped = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
minValue = min(minValue, value);
|
||
|
}
|
||
|
writefln!"Done searching %d seeds, local min = %d"(seedCount, minValue);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void main() {
|
||
|
part1();
|
||
|
part2();
|
||
|
}
|