Advent of Code – 8. nap
A 8. nap az előző nap sikeres menekülése után észrevesszük, hogy a tengeralattjáróban a 4 számjegyű hétszegmenses kijelzők meghibásodtak a menekülés közben. Nagy bajban leszünk nélkülük, úgyhogy rá kell jönnünk, hogy mi ment félre.
A hétszegmenses kijelzők onnan kapták a nevüket, hogy 7db LED segítségével 0-9-ig számjegyek megjelenítésére képesek. A szegmensek kiosztása és a megjeleníthető számjegyek a következőek:
0: 1: 2: 3: 4:
aaaa .... aaaa aaaa ....
b c . c . c . c b c
b c . c . c . c b c
.... .... dddd dddd dddd
e f . f e . . f . f
e f . f e . . f . f
gggg .... gggg gggg ....
5: 6: 7: 8: 9:
aaaa aaaa aaaa aaaa aaaa
b . b . . c b c b c
b . b . . c b c b c
dddd dddd .... dddd dddd
. f e f . f e f . f
. f e f . f e f . f
gggg gggg .... gggg gggg
Tehát ha egy 1-es számjegyet szeretnénk rajzolni, akkor a c és f szegmenseket kell bekapcsolni. Viszont a gond az, hogy a vezérlőjelek összekavarodtak rendesen és egy 1-es esetén kaphatunk dg vagy bármilyen két egyedi betűs kombinációt (persze a és g betű között). Ugyanez igaz a további számjegyekre is.
Bemenetként megkapjuk a 10 darab számjegy kirajzolásához használt kombinációkat szóközzel elválasztva. Ezt követi egy | jel, ami után 4db szám kódolt formában következik. Tehát a bemenet egy sora így néz ki:
be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb | fdgacbe cefdb cefbgd gcbe
A feladat első felében csak a | jel utáni számokkal kell foglalkoznunk és megszámolnunk, hogy összesen hány darab 1-es, 4-es, 7-es és 8-as számjegyet kapunk a kimeneten.
Szerencsére ez egy egyszerű feladat, mivel ezek a számjegyek a hosszukról azonosíthatóak komoly dekódolás nélkül. Némi LINQ segítségével és a korábbi napokon már használt beolvasó metódusokkal ez viszonylag könnyen megoldható:
var encodedNumbers = FileReader.ReadLinesOfFile("inputs\\08.txt", '|', (tokens) =>
{
return tokens[1].Trim().Split(' ');
});
int solution = encodedNumbers
.SelectMany(x => x)
.Where(x => IsOneFourSevenOrEight(x))
.Count();
Console.WriteLine(solution);
A kiválogatásért az IsOneFourSevenOrEight metódus felel, ami a következő:
private static bool IsOneFourSevenOrEight(string combination)
{
const int one = 2;
const int four = 4;
const int seven = 3;
const int eight = 7;
return combination.Length == one
|| combination.Length == four
|| combination.Length == seven
|| combination.Length == eight;
}
2. feladat
Az első feladat csak a bemelegítés volt, mivel a 2. megoldása a | jel utáni számjegyekből alkotott számok összege lesz. A trükk az, hogy a fájlban soronként változik a számjegyek kódolása. Ez a | jel előtti 10db szám kombinációból fejthető vissza. A kérdés csak az, hogy hogy. Az algoritmus megírásához én készítettem egy táblázatot, ami vizuálisan mutatta, hogy melyik számjegy esetén melyik szegmensek aktívak.
Ez alapján némi LINQ segítségével és logikus következtetéssel meghatározható a bemenetekből, hogy melyik melyik számjegyet jelenti. Azért, hogy a fő kód ne legyen túlzsúfolt, a dekódoló logikát kiszerveztem a Day08DecodeTable osztályomba:
internal class Day08DecodeTable
{
private readonly Dictionary<string, char> _digitMappings;
public Day08DecodeTable(string[] items)
{
_digitMappings = new Dictionary<string, char>(10);
var one = items
.First((x) => x.Length == 2)
.OrderBy(x => x)
.ToArray();
var four = items
.First((x) => x.Length == 4)
.OrderBy(x => x)
.ToArray();
var seven = items
.First((x) => x.Length == 3)
.OrderBy(x => x)
.ToArray();
var eight = items
.First((x) => x.Length == 7)
.OrderBy(x => x)
.ToArray();
var originalBD = four.Except(one);
var originalA = seven.Except(one);
var ogriginalEG = eight.Except(four).Except(originalA);
var nine = items
.First(x => x.Length == 6 && x.Except(four)
.Except(originalA).Count() == 1)
.OrderBy(x => x)
.ToArray();
var originalG = nine.Except(four).Except(originalA);
var originalE = ogriginalEG.Except(originalG);
var zero = items
.First((x) => x.Length == 6 && x.Except(seven)
.Except(ogriginalEG).Count() == 1)
.OrderBy(x => x)
.ToArray();
var originalAB = zero.Except(seven).Except(ogriginalEG);
var originalD = originalBD.Except(originalAB);
var six = items
.First((x) => x.Length == 6 && x.Except(originalA)
.Except(originalBD).Except(originalG)
.Except(originalE).Count() == 1)
.OrderBy(x => x)
.ToArray();
var bottomRight = six
.Except(originalA)
.Except(originalBD)
.Except(originalG)
.Except(originalE);
var topRight = one.Except(bottomRight);
var two = eight
.Except(bottomRight)
.Except(originalAB)
.OrderBy(x =>x)
.ToArray();
var three = nine
.Except(originalAB)
.OrderBy(x => x)
.ToArray();
var five = six
.Except(originalE)
.OrderBy(x => x)
.ToArray();
_digitMappings.Add(new string(zero), '0');
_digitMappings.Add(new string(one), '1');
_digitMappings.Add(new string(two), '2');
_digitMappings.Add(new string(three), '3');
_digitMappings.Add(new string(four), '4');
_digitMappings.Add(new string(five), '5');
_digitMappings.Add(new string(six), '6');
_digitMappings.Add(new string(seven), '7');
_digitMappings.Add(new string(eight), '8');
_digitMappings.Add(new string(nine), '9');
}
public int Decode(string[] input)
{
StringBuilder sb = new(4);
foreach (var combination in input)
{
string orderedCombination
= new string(combination.OrderBy(x => x).ToArray());
sb.Append(_digitMappings[orderedCombination]);
}
return int.Parse(sb.ToString());
}
}
Ez a konstruktorban elvégzi a dekódolást, majd a _digitMappings belső Dictionary-ben tárolja a kombinációkhoz tartozó számjegyeket. Feltűnhet, hogy minden kombinációpár rendezett formátumban van (OrderBy) tárolva. Erre azért van szükség, mert a bemeneten ha ba kombinációt kapunk, akkor abból kikövetkeztethetjük, hogy ez az egyes kódja. Azonban előfordulhat, hogy a | jel utáni részben ab párt kapunk, ami szintén ugyanaz az 1-es, csak a betűk más sorrendben vannak.
Az osztály Decode metódusa végzi a | jel utáni rész dekódolását. Először itt is rendezzük a bemenetet, hogy meg tudjuk találni a dekódolási táblázatban. Ezután a kapott számjegyet hozzáfűzzük egy StringBuilder típusú változóhoz, amit aztán végül szöveggé, majd számmá alakítunk.
Ezzel meg is van a nagy része a feladatnak, mostmár csak a bemeneti fájl soraiban található számokat kell dekódolnunk és összeadnunk:
var encodedNumbers = FileReader.ReadLinesOfFile("inputs\\08.txt", '|', (tokens) =>
{
string[] decodableInput = tokens[0].Trim().Split(' ');
string[] numberNeeded = tokens[1].Trim().Split(' ');
return (decodableInput, numberNeeded);
});
long sum = 0;
foreach (var (decodableInput, numberNeeded) in encodedNumbers)
{
var table = new Day08DecodeTable(decodableInput);
int decoded = table.Decode(numberNeeded);
sum += decoded;
}
Console.WriteLine(sum);
Véleményem szerint ennek a napnak a megítélése nagymértékben függ attól, hogy milyen háttérrel rendelkezik az ember. Nekem volt szerencsém mikrovezérlőkkel és azon belül is hétszegmenses kijelzők programozásával foglalkozni, így mondhatni a háttér adott volt, de azért kellett egy jó órát gondolkodnom a megvalósításon.
