Photo by Any Lane from Pexels

I’m participating in the Advent of Code 2021. Here’s my solutions for Day 8 - Seven Segment Search.

Problem 1

In the Puzzles for Day 8, the wires on our 7-segment displays are malfunctioning and we need to figure out what it’s really saying. For part 1, we’ll figure out how many times digits 1, 4, 7 and 8 are appearing in the output. Our displays are represented by:

  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

and our input comes in the format of:

cedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab | cdfeb fcadb cdfeb cdbaf

The active wires are in the section before the | and the displays are after the pipe. For part 1, we’re only looking at the second part of each line and picking out the easy to identify numbers. For instance, if there are only two active elements, then it has to be a 1, since 1 is the only digit that is represented by two illuminated elements. Similarly for 4 (4 elements), 7 (3 eleements) and 8 (all elements).

Solution

All my solutions are written in C#. You can find all my solutions in my Git repo.

Part 1 is straightforward. We iterate through the input and look at the active element count for each output. If it’s 2, 3, 4 or 7 characters long, it represents 1, 7, 4 and 8 respectively. We count the total outputs for each, and add them together to get our solution. A simple switch/case statement block seemed to be a good choice for this one.

Console.WriteLine("Advent of Code 2021");
Console.WriteLine("Day 8 - Puzzle 1");

//Solution logic goes here

//Load input array
string[] lines = System.IO.File.ReadAllLines("input.txt");

var counts = new Dictionary<int, int>();

foreach (var line in lines)
{
    var split = line.Split('|');
    var wires = split[0].Split(' ');
    var outputs = split[1].Split(' ');

    //focus on easy digits
    foreach (var output in outputs)
    {
        switch (output.Length)
        {
            case 2:
                //can only be a 1
                if (counts.ContainsKey(1))
                {
                    counts[1]++;
                }
                else
                {
                    counts[1] = 1;
                }
                break;
            case 3:
                //can only be a 7
                if (counts.ContainsKey(7))
                {
                    counts[7]++;
                }
                else
                {
                    counts[7] = 1;
                }
                break;
            case 4:
                //can only be a 4
                if (counts.ContainsKey(4))
                {
                    counts[4]++;
                }
                else
                {
                    counts[4] = 1;
                }
                break;
            case 5:
                break;
            case 6:
                break;
            case 7:
                //can only be an 8
                if (counts.ContainsKey(8))
                {
                    counts[8]++;
                }
                else
                {
                    counts[8] = 1;
                }

                break;
            default:
                //should not get here
                break;
        }

    }
}

Console.WriteLine($"Total times we see 1, 4, 7 or 8: {counts[1] + counts[4] + counts[7] + counts[8] }");


//Stop and wait for enter before exiting
Console.ReadLine();

The answer is: 554

Problem 2

The second half of the puzzle is the hard one. We need to take the input data and what we learned from the outputs and then use that to deduce all the other numbers. In order to do that, we’ll have to analyze what is learned from part 1 in order to determine what letters actually represent what elements of the display.

Solution

For this one, it was a matter of figuring out what numbers we could identify based on matches to known numbers. The first thing I did was to sort the character strings by letter. This would make it easy to compare/contrast between strings. Next, I re-ordered the input wire array from shortest length to longest length. That also made it easier for comparing one array element to another.

There are 3 numbers that have 5 segments (2, 3 & 5) and 3 numbers that have 6 segments (0, 6 & 9). I looked at the 5 segment numbers first.

  • 3 is the only one that matches both segments of 1, so that was easy.
  • 5 was the next easiest, as of the remaining two options, it matches both of the segments of a 4 that aren’t a match to a 1 (middle bar and top left)
  • That left 2 as the remaining option for a 5 segment number

Lastly I looked at the 6 segment numbers.

  • 6 was easiest to identify as it would not match both elements of a 1
  • 9 was next as it would be the only one to match all 4 segments of a number 4
  • That left 0 as the final number.
using System.Text;

Console.WriteLine("Advent of Code 2021");
Console.WriteLine("Day 8 - Puzzle 2");

//Solution logic goes here

//Load input array
string[] lines = System.IO.File.ReadAllLines("input.txt");

var counts = new Dictionary<int, int>();

var total = 0;
foreach (var line in lines)
{
    var split = line.Split('|');
    var wires = split[0].Trim().Split(' ');
    var outputs = split[1].Trim().Split(' ');
    var conversion = new Dictionary<string, string>();

    //sort letters in pattern
    for (int i = 0; i < 10; i++)
    {
        wires[i] = String.Concat(wires[i].OrderBy(x => x));
    }

    for (int j = 0; j < 4; j++)
    {
        outputs[j] = String.Concat(outputs[j].OrderBy(x => x));
    }

    //Start identifying segments
    Array.Sort(wires, (x, y) => x.Length.CompareTo(y.Length));

    //start with known values
    conversion["1"] = wires[0];
    conversion["4"] = wires[2];
    conversion["7"] = wires[1];
    conversion["8"] = wires[9];


    var fiveSegs = new List<string>() { wires[3], wires[4], wires[5] };
    //pick 3 - it's the only 5 count segment with both elements of 1
    if (wires[3].Contains(wires[0].Substring(0, 1)) && wires[3].Contains(wires[0].Substring(1, 1)) && fiveSegs.Contains(wires[3]))
    {
        conversion["3"] = wires[3];
        fiveSegs.Remove(wires[3]);
    }
    else if (wires[4].Contains(wires[0].Substring(0, 1)) && wires[4].Contains(wires[0].Substring(1, 1)) && fiveSegs.Contains(wires[4]))
    {
        conversion["3"] = wires[4];
        fiveSegs.Remove(wires[4]);
    }
    else
    {
        conversion["3"] = wires[5];
        fiveSegs.Remove(wires[5]);
    }

    //pick 5 - it's the only 5 count segment that will have both the middelAndTopLeft values
    //First, grab the two segments in a 4 that aren't in a 1
    var middleAndTopLeft = wires[2].Except(wires[0]).ToList();
    if (wires[3].Contains(middleAndTopLeft[0]) && wires[3].Contains(middleAndTopLeft[1]))
    {
        conversion["5"] = wires[3];
        fiveSegs.Remove(wires[3]);
    }
    else if (wires[4].Contains(middleAndTopLeft[0]) && wires[4].Contains(middleAndTopLeft[1]))
    {
        conversion["5"] = wires[4];
        fiveSegs.Remove(wires[4]);
    }
    else
    {
        conversion["5"] = wires[5];
        fiveSegs.Remove(wires[5]);
    }

    //pick 2 - it's what's left of the 5 segment numbers
    conversion["2"] = fiveSegs.First();

    var sixSegs = new List<string>() { wires[6], wires[7], wires[8] };
    //pick 6 - it won't match both elements of 1
    if (wires[6].Except(wires[0]).Count() == 5)
    {
        conversion["6"] = wires[6];
        sixSegs.Remove(wires[6]);
    }
    else if (wires[7].Except(wires[0]).Count() == 5)
    {
        conversion["6"] = wires[7];
        sixSegs.Remove(wires[7]);
    }
    else
    {
        conversion["6"] = wires[8];
        sixSegs.Remove(wires[8]);
    }

    //pick 9 - it will match all elements of 4
    if (wires[6].Except(wires[2]).Count() == 2 && sixSegs.Contains(wires[6]))
    {
        conversion["9"] = wires[6];
        sixSegs.Remove(wires[6]);
    }
    else if (wires[7].Except(wires[2]).Count() == 2 && sixSegs.Contains(wires[7]))
    {
        conversion["9"] = wires[7];
        sixSegs.Remove(wires[7]);
    }
    else
    {
        conversion["9"] = wires[8];
        sixSegs.Remove(wires[8]);
    }

    //pick 0 - it's what's left
    conversion["0"] = sixSegs.First();

    StringBuilder answer = new StringBuilder();
    for (var z = 0; z < 4; z++)
    {
        if (conversion.ContainsValue(outputs[z]))
        {
            answer.Append(conversion.FirstOrDefault(x => x.Value == outputs[z]).Key);
        }
        else
        {
            throw new Exception("No match found");
        }
    }

    total += Convert.ToInt32(answer.ToString());
}

Console.WriteLine($"Total sum of outputs: {total}");

//Stop and wait for enter before exiting
Console.ReadLine();

The answer is: 990964