[TASK] Solved Day 8

This commit is contained in:
2021-12-09 22:29:00 +01:00
parent ab613666e7
commit 3a9184ae37
4 changed files with 388 additions and 0 deletions

166
functions/src/day8.ts Normal file
View File

@@ -0,0 +1,166 @@
import Day from "./day";
import Utils from "./utils";
const FULL = "abcdefg";
const SIGNAL_DISPLAYS = [
"abcefg", "cf", "acdeg", "acdfg", "bcdf", "abdfg", "abdefg", "acf", "abcdefg", "abcdfg"
]
class Day8 implements Day {
part1(input: string[]): number | string {
const signals = input.map(line => Signal.parse(line));
return Utils.sum(signals.map(s => s.outputValues.filter(o =>
o.length === SIGNAL_DISPLAYS[1].length ||
o.length === SIGNAL_DISPLAYS[4].length ||
o.length === SIGNAL_DISPLAYS[7].length ||
o.length === SIGNAL_DISPLAYS[8].length).length));
}
part2(input: string[]): number | string {
const signals = input.map(line => Signal.parse(line));
return Utils.sum(signals.map(s => s.decode()));
}
}
class Signal {
readonly inputValues: string[];
readonly outputValues: string[];
constructor(inputValues: string[], outputValues: string[]) {
this.inputValues = inputValues;
this.outputValues = outputValues;
}
static parse(line: string): Signal {
const split = line.split(/\s*\|\s*/)
return new Signal(
split[0].split(/\s+/),
split[1].split(/\s+/)
);
}
decode(): number {
const values = [...this.inputValues, ...this.outputValues];
// mappings maps the signal values to all possible correct wires (we'll filter this later on)
const mappings: {[key: string]: string} = { "a": FULL, "b": FULL, "c": FULL, "d": FULL, "e": FULL, "f": FULL, "g": FULL };
// First, we use the "easy" (EZ) numbers. We know that every value of length must map to:
// 2 is a 1: cf
// 4 is a 4: acdfg
// 3 is a 7: acf
// 7 is an 8, but that doesn't give any additional information (since it maps to everything anyway)
for (const nr of [1, 4, 7]) {
const ezSignal = SIGNAL_DISPLAYS[nr];
const ezCoded = values.find(v => v.length === ezSignal.length);
if (ezCoded) {
for (const ezCodedSymbol of ezCoded) {
mappings[ezCodedSymbol] = Signal.intersect(ezSignal, mappings[ezCodedSymbol]);
}
}
}
// 0, 6 and 9 all miss one bar (length of 6). The only bars that can be missing are:
// 0: d
// 6: c
// 9: e
// So we can use this to deduct that the missing element in every number of length 6, must map to either d, c, or e
const lengthSixValues = values.filter(v => v.length === 6);
for (const value of lengthSixValues) {
for (const codedSymbol of FULL) {
if (!value.includes(codedSymbol)) {
mappings[codedSymbol] = Signal.intersect("cde", mappings[codedSymbol]);
}
}
}
// 2, 3 and 5 all miss two bars:
// 2: b, f
// 3: b, e
// 5: c, e
// So this means that the two missing elements in every number of length 5 must map to either b, c, e or f:
const lengthFiveValues = values.filter(v => v.length === 5);
for (const value of lengthFiveValues) {
for (const codedSymbol of FULL) {
if (!value.includes(codedSymbol)) {
mappings[codedSymbol] = Signal.intersect("bcef", mappings[codedSymbol]);
}
}
}
// By now, we probably know some mappings for sure (only one possibility), so we can remove the sure ones from the mappings:
let toClean = FULL;
let cleanedThisRound = "xxx";
while(toClean.length > 0 && cleanedThisRound.length > 0) {
cleanedThisRound = "";
for (const codedSymbol of toClean) {
if (mappings[codedSymbol].length === 1) {
const decodedSymbol = mappings[codedSymbol];
Object.keys(mappings).filter(cs => cs != codedSymbol)
.forEach(cs => mappings[cs] = Signal.removeMapping(mappings[cs], decodedSymbol));
cleanedThisRound += codedSymbol;
}
}
for (const cleanedSymbol of cleanedThisRound) {
toClean = toClean.replace(cleanedSymbol, "");
}
}
let res = 0;
for (let i = 0; i < this.outputValues.length; i++) {
const codedValue = this.outputValues[i];
const scale = Math.pow(10, this.outputValues.length - i - 1)
res += SIGNAL_DISPLAYS.indexOf(Signal.sort(this.applyMapping(codedValue, mappings))) * scale;
}
return res;
}
private static intersect(map1: string, map2: string): string {
let res = "";
for (const char of map1) {
if (map2.includes(char)) {
res += char;
}
}
return res;
}
private static removeMapping(mapping: string, decodedSymbol: string): string {
return mapping.replace(decodedSymbol, "");
}
private applyMapping(value: string, mapping: {[key: string]: string}): string {
let res = "";
for (const codedSymbol of value) {
res += mapping[codedSymbol];
}
if (res.length !== value.length) {
throw Error("Mapping is incomplete for signal: " + this.inputValues.join(" ") + " | " + this.outputValues.join(" "));
}
return res;
}
private static sort(value: string): string {
let res = "";
for (const c of FULL) {
if (value.includes(c)) {
res += c;
}
}
return res;
}
}
export default Day8;

View File

@@ -9,6 +9,7 @@ import Day4 from "./day4";
import Day5 from "./day5";
import Day6 from "./day6";
import Day7 from "./day7";
import Day8 from "./day8";
// // Start writing Firebase Functions
@@ -32,6 +33,7 @@ export const day = {
5: functions.region("europe-west1").https.onRequest((request, response) => { processDay(new Day5(), request, response) }),
6: functions.region("europe-west1").https.onRequest((request, response) => { processDay(new Day6(), request, response) }),
7: functions.region("europe-west1").https.onRequest((request, response) => { processDay(new Day7(), request, response) }),
8: functions.region("europe-west1").https.onRequest((request, response) => { processDay(new Day8(), request, response) }),
}