153 lines
3.6 KiB
Dart
153 lines
3.6 KiB
Dart
import "dart:convert";
|
|
import "dart:io";
|
|
import "dart:math";
|
|
|
|
import "package:args/command_runner.dart";
|
|
|
|
class Range implements Comparable<Range> {
|
|
// The start and end of this range
|
|
final int start;
|
|
final int end;
|
|
|
|
Range(this.start, this.end);
|
|
|
|
@override
|
|
String toString() {
|
|
return "[$start..$end]";
|
|
}
|
|
|
|
bool inRangeInclusive(int value) {
|
|
return start <= value && value <= end;
|
|
}
|
|
|
|
int length() {
|
|
return end - start + 1;
|
|
}
|
|
|
|
/// Ordered by start and then by end
|
|
@override
|
|
int compareTo(Range other) {
|
|
int startCompare = start.compareTo(other.start);
|
|
if (startCompare == 0) {
|
|
return end.compareTo(other.end);
|
|
}
|
|
return startCompare;
|
|
}
|
|
}
|
|
|
|
class RangeSet {
|
|
final List<Range> ranges;
|
|
|
|
RangeSet._internal(this.ranges);
|
|
|
|
factory RangeSet(Iterable<Range> ranges) {
|
|
// Create and sort ranges list
|
|
List<Range> rangesList = List.from(ranges, growable: false);
|
|
rangesList.sort();
|
|
|
|
// Combine and compact adjacent ranges
|
|
List<Range> compactedList = [rangesList[0]];
|
|
for (Range range in rangesList.sublist(1)) {
|
|
Range lastRange = compactedList.last;
|
|
if (range.start <= lastRange.end) {
|
|
compactedList[compactedList.length - 1] = Range(
|
|
lastRange.start,
|
|
max(lastRange.end, range.end),
|
|
);
|
|
} else {
|
|
compactedList.add(range);
|
|
}
|
|
}
|
|
return RangeSet._internal(compactedList);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return ranges.toString();
|
|
}
|
|
|
|
Range operator [](int index) => ranges[index];
|
|
|
|
bool inRangeSetInclusive(int value) {
|
|
for (var range in ranges) {
|
|
if (range.inRangeInclusive(value)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int length() {
|
|
return ranges.map((r) => r.length()).reduce((left, right) => left + right);
|
|
}
|
|
}
|
|
|
|
class Day5Command extends Command {
|
|
// The [name] and [description] properties must be defined by every
|
|
// subclass.
|
|
@override
|
|
final name = "day5";
|
|
|
|
@override
|
|
final description = "Run Advent of Code 2025 Day 5";
|
|
|
|
Day5Command() {
|
|
// we can add command specific arguments here.
|
|
// [argParser] is automatically created by the parent class.
|
|
}
|
|
|
|
// [run] may also return a Future.
|
|
@override
|
|
Future<void> run() async {
|
|
// [argResults] is set before [run()] is called and contains the flags/options
|
|
// passed to this command.
|
|
|
|
if (argResults!.rest.length != 1) {
|
|
print(
|
|
"Expected 1 positional arguments, found ${argResults!.rest.length}",
|
|
);
|
|
exit(1);
|
|
}
|
|
var filePath = argResults!.rest[0];
|
|
|
|
print("Parsing file: $filePath");
|
|
var inputFile = File(filePath);
|
|
var listOfRanges = inputFile
|
|
.openRead()
|
|
.transform(utf8.decoder)
|
|
.transform(LineSplitter())
|
|
.takeWhile((line) => line.isNotEmpty)
|
|
.map((line) {
|
|
var splitLine = line.split("-");
|
|
return Range(int.parse(splitLine[0]), int.parse(splitLine[1]));
|
|
})
|
|
.toList();
|
|
|
|
List<int> listOfIds = await inputFile
|
|
.openRead()
|
|
.transform(utf8.decoder)
|
|
.transform(LineSplitter())
|
|
.skipWhile((line) => line.isNotEmpty)
|
|
.skipWhile((line) => line.isEmpty)
|
|
.map((line) {
|
|
return int.parse(line);
|
|
})
|
|
.toList();
|
|
RangeSet freshIdRanges = RangeSet(await listOfRanges);
|
|
|
|
//print(freshIdRanges);
|
|
//print(listOfIds);
|
|
|
|
int freshCount = 0;
|
|
|
|
for (int id in listOfIds) {
|
|
if (freshIdRanges.inRangeSetInclusive(id)) {
|
|
freshCount++;
|
|
}
|
|
}
|
|
|
|
print("Fresh IDs: $freshCount");
|
|
print("Total ingredient ids considered fresh: ${freshIdRanges.length()}");
|
|
}
|
|
}
|