Files
adventofcode2025/lib/day5/command.dart

153 lines
3.6 KiB
Dart
Raw Normal View History

2025-12-09 15:18:20 -06:00
import "dart:convert";
import "dart:io";
2025-12-09 17:05:28 -06:00
import "dart:math";
2025-12-09 15:18:20 -06:00
import "package:args/command_runner.dart";
2025-12-09 17:05:28 -06:00
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);
}
}
2025-12-09 15:18:20 -06:00
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);
2025-12-09 17:05:28 -06:00
var listOfRanges = inputFile
2025-12-09 15:18:20 -06:00
.openRead()
.transform(utf8.decoder)
.transform(LineSplitter())
2025-12-09 17:05:28 -06:00
.takeWhile((line) => line.isNotEmpty)
2025-12-09 15:18:20 -06:00
.map((line) {
2025-12-09 17:05:28 -06:00
var splitLine = line.split("-");
return Range(int.parse(splitLine[0]), int.parse(splitLine[1]));
2025-12-09 15:18:20 -06:00
})
.toList();
2025-12-09 17:05:28 -06:00
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()}");
2025-12-09 15:18:20 -06:00
}
}