1
0
Fork 0
mirror of https://github.com/HenkKalkwater/aoc-2020 synced 2024-11-26 04:45:18 +00:00
aoc-2020/source/day16.d
Chris Josten 7e096ec120
Added day 16 part 2
After replacing allowedValues[field] with allowedValues[column] and
ticket[column] with ticket[field] on lines 85 and 87, it works. Why? I don't know.
2020-12-16 18:21:59 +01:00

158 lines
4.3 KiB
D

import std.conv;
import std.typecons;
import dayutil;
struct Range {
this(int start, int end) { this.start = start; this.end = end; }
int start;
int end;
}
Variant run(int part, File input, bool bigboy, string[] args) {
auto sections = input.byLineCopy.array.splitter!empty.array;
auto tmp = parseFields(sections[0]);
Range[][] fields = tmp[0];
string[] fieldNames = tmp[1];
/*debug foreach(field, name; zip(fields, fieldNames)) {
writeln(name, ": ", field);
}*/
int[] myTicket = sections[1][1].splitter(",").array.map!(to!int).array;
// debug writeln(myTicket);
int[][] tickets = sections[2].drop(1).map!(x => x.splitter(",").array.map!(to!int).array).array;
return Variant(parts!long(part,
() => part1(fields.joiner.array, tickets),
() => part2(fields, fieldNames, myTicket, tickets)));
}
Tuple!(Range[][], string[]) parseFields(R)(R input) if (isInputRange!R && isSomeString!(ElementType!R)) {
string[] fieldNames;
Range[][] result;
foreach(line; input) {
auto split1 = line.splitter(": ").array;
Range[] ranges = split1[1].splitter(" or ").map!((e) {
int[] parts = e.splitter("-").array.map!(to!int).array;
return Range(parts[0], parts[1]);
}).array;
fieldNames ~= [split1[0]];
result ~= ranges;
}
return tuple(result, fieldNames);
}
int part1(Range[] allowedValues, int[][] tickets) {
int errorRate = 0;
foreach(number; tickets.joiner) {
if (!allowedValues.canFind!((element, needle) => element.start <= needle && needle <= element.end)(number)) {
errorRate += number;
}
}
return errorRate;
}
long part2(Range[][] allowedValues, string[] fieldNames, int[] myTicket, int[][] tickets) {
auto flatValues = allowedValues.joiner.array;
// discard invalid tickets
// debug writeln("Ticket count: ", tickets.length);
int[][] validTickets = tickets.filter!((ticket) {
foreach(number; ticket) {
if (!flatValues.canFind!((element, needle) => element.start <= needle && needle <= element.end)(number)) {
return false;
}
}
return true;
}).array;
validTickets ~= myTicket;
import std.format;
// debug writeln("Valid ticket count: ", validTickets.length);
int[][] candidatesPerField;
candidatesPerField.reserve(fieldNames.length);
foreach(field; 0..fieldNames.length) {
//debug write("Matching ", fieldNames[field], " with: ");
int[] candidates = iota(0, cast(int) fieldNames.length).filter!((column) {
bool result = validTickets.all!(
(ticket) => allowedValues[column]
.canFind!((range, needle) => range.start <= needle
&& needle <= range.end)(ticket[field]));
//debug write(validTickets.map!((t) => t[column]), " (", result, "); ");
return result;
}).array;
//debug writeln();
candidatesPerField ~= [candidates];
}
long result = 1;
immutable string pred = "a.value.length < b.value.length";
auto sortedCandidates = candidatesPerField.enumerate.array.sort!pred;
//debug writeln("Candidates: ", sortedCandidates);
Tuple!(int, string)[] values;
values.reserve(6);
while (sortedCandidates.length > 0) {
ulong index = sortedCandidates.front.index;
int[] candidates = sortedCandidates.front.value;
assert(candidates.length == 1, "Cannot solve this");
int bestCandidate = candidates[0];
// debug writeln(index, " maps to ", fieldNames[bestCandidate]);
if (fieldNames[bestCandidate].startsWith("departure ")) {
result *= myTicket[index];
values ~= tuple(myTicket[index], fieldNames[bestCandidate]);
//debug writeln("Taking ", myTicket[index], "; (result: ", result, ")");
}
sortedCandidates.popFront();
sortedCandidates = sortedCandidates.map!((x) {
x.value = x.value.filter!(y => y != bestCandidate).array;
return x;
}).array.sort!pred;
}
// debug writeln(values);
return result;
}
unittest {
string input = q"EOS
class: 1-3 or 5-7
row: 6-11 or 33-44
seat: 13-40 or 45-50
your ticket:
7,1,14
nearby tickets:
7,3,47
40,4,50
55,2,20
38,6,12
EOS";
auto sections = input.lineSplitter.splitter!empty.array;
auto tmp = parseFields(sections[0]);
Range[][] fields = tmp[0];
string[] fieldNames = tmp[1];
assert(fields[0] == [Range(1,3), Range(5,7)]);
int[] myTicket = sections[1].array[1].splitter(",").array.map!(to!int).array;
int[][] tickets = sections[2].drop(1).map!(x => x.splitter(",").array.map!(to!int).array).array;
assert(part1(fields.joiner.array, tickets) == 71);
part2(fields, fieldNames, myTicket, tickets);
}