# Cristiana's Dev Blog # Advent of Code 2021: Day 8

This blog post is eighth in the Advent of Code 2021 series and shows a JavaScript-based solution to the problem described in Day 8. The challenge was about decoding four-digit numbers represented using a seven-segment display. The difficulty in solving this problem increased compared to the previous days and I struggled a bit to find the solution in part two. However, this was my favourite challenge so far.

All solutions are in this GitHub repository.

## Understanding the Problem

The problem is about decoding digits represented using a seven-segment display. Each segment has a name from `a` to `g`. By default, if we name the segments in order, the digits from `0` to `9` are represented by the segments `abcefg`, `cf`, `acdeg`, `acdfg`, `bcdf`, `abdfg`, `abdefg`, `acf`, `abcdefg` and `abcdfg` . The problem is we don't know which letter corresponds to which segment. We must deduct it from a set of ten unique segment patterns representing digits from 0 to 9 given in random order. For example, given the pattern

``````acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab
``````

we must find a way to deduce the name of each segment. The segment mapping from the example is illustrated below. The pattern from the example represents the following digits:

``````acedgfb: 8 cdfbe: 5 gcdfa: 2 fbcad: 3 dab: 7 cefabd: 9 cdfgeb: 6 eafb: 4 cagedb: 0 ab: 1
``````

## Understanding the Input

We are given multiple lines, each containing ten unique signal patterns, a delimiter and four output values. For example,

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

## Solving Part One

The goal is to count the number of four-digit output values representing either `1`, `4`, `7` and `8`. The problem description hints that these digits have a unique number of segments. Indeed only `1` has two segments, only `4` has four segments, only `7` has three segments and only `8` has seven segments.

To solve part one we must:

• check if any output value has a unique length
• sum all output values with unique length

Since we only need the output value for part one, we can extract them from each line using the delimiter `|`.

``````const lines = data.split('\n')
const outputValues = lines.map((line) => line.split(' | '))
``````

Then to extract each digit into an array we can use the space as separator.

``````const digits = outputValue.split(" ")
``````

### Check if any output value has a unique length

Since `1`, `4`, `7`, `8` are represented using `2`, `4`, `3`, `7` segments respectively, we can check whether the length of the digit is one of those numbers. The `includes` function is useful for performing this check.

``````[2, 4, 3, 7].includes(digit.length)
``````

### Count all output values with unique length

We can use the `reduce` function to count output values respecting the condition previously defined.

``````digits.reduce((digitCount, digit) => {
if ([2, 4, 3, 7].includes(digit.length)) {
return digitCount + 1
}
return digitCount
}, 0)
``````

Then, we can use the `reduce` function again to sum digits from all lines.

``````return outputValues.reduce((digitCount, outputValue) => {
const digits = outputValue.split(" ")

return digitCount + digits.reduce((count, digit) => {
if([2, 4, 3, 7].includes(digit.length)) {
return count + 1
}
return count
},0)
}, 0)
``````

The final solution for part one is in my GitHub repository.

## Solving Part Two

The second part of the problem was the tricky one. The goal of the second part was to decode all output values, from a four-digit number and sum all four-digit numbers.

I used a top-down approach where I wrote higher-level functions first and, thus defined the overall structure of the program, then defined the lower-level functions and went into detail. Here is how I broke down the problem:

• sum all output numbers
• decode output number
• find segment mapping
• reveal corresponding digits using segment map
• find output value based on revealed digits

The trickiest part was finding the segment mapping, in other words, identifying the name of each segment. The strategy was:

• calculating segment occurrences in all ten unique patterns
• identifying the segments with unique occurrences
• identifying digits with unique segment length
• use a process of elimination

### Sum all output numbers

We can outline the solution as follows. We define the `getOutput` function afterwards.

``````const solve = (data) => {
const lines = data.split('\n')
return lines.reduce((sum, line) => sum + getOutput(line), 0)
}
``````

### Decode output number

We can further outline the solution:

``````const getOutput = (line) => {
const [segmentPatternsString, outputValuesString] = line.split(' | ')
const segmentMap = findSegmentNamesMap(segmentPatternsString)
const correspondingSevenSegmentDisplay = getSevenSegmentDisplay(segmentMap)

return getOutputDigits(outputValuesString, correspondingSevenSegmentDisplay)
}
``````

Let's look at an example line. Given the line

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

the `segmentPatternsString` is

``````acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab
``````

and `outputValuesString` is

``````cdfeb fcadb cdfeb cdbaf
``````

The `findSegmentNamesMap` function returns the following mapping:

``````{
a: 'd',
b: 'e',
c: 'a',
d: 'f',
e: 'g',
f: 'b',
g: 'c',
}
``````

The `getSevenSegmentDisplay` function maps each digit based on the default seven segment display. So, the default seven-segment display

``````[ 'abcefg', 'cf', 'acdeg', 'acdfg', 'bcdf', 'abdfg', 'abdefg', 'acf', 'abcdefg', 'abcdfg']
``````

becomes the following based on the segment names map

``````['abcdeg', 'ab', 'acdfg', 'abcdf', 'abef', 'bcdef', 'bcdefg', 'abd', 'abcdefg', 'abcdef']
``````

Notice the letters are sorted alphabetically. This makes identifying the output digits easier. The `getOutputDigits` maps the `outputValuesString` to `5353`.

### Find segment mapping

We can start by initializing the segment name map:

``````const findSegmentNamesMap = (segmentPatternsString) => {
const segmentMap = { a: '', b: '', c: '', d: '', e: '', f: '', g: '' }

return segmentMap
}
``````

#### Identifying the segments with unique occurrences

Using the default segment names we have the digits from `0` to `9` represented as:

``````const sevenSegmentDisplay = [ 'abcefg', 'cf', 'acdeg', 'acdfg', 'bcdf', 'abdfg', 'abdefg', 'acf', 'abcdefg', 'abcdfg']
``````

and each letter occurs as follows

``````const abcdefgOccurence = { a: 8, b: 6, c: 8, d: 7, e: 4, f: 9, g: 7 }
``````

Thus, the `b`, `e` and `f` segments occur a unique number of times. We can use this information to determine the segment name mapping for these values.

``````const findSegmentNamesMap = (segmentPatternsString) => {
const segmentMap = { a: '', b: '', c: '', d: '', e: '', f: '', g: '' }

const letterOccurence = calculateLetterOccurence(segmentPatternsString)
segmentMap.b = findLetter(letterOccurence, abcdefgOccurence.b)
segmentMap.e = findLetter(letterOccurence, abcdefgOccurence.e)
segmentMap.f = findLetter(letterOccurence, abcdefgOccurence.f)

return segmentMap
}
``````

#### Identifying segments based on digits with unique segment length

Part one hinted at using the length of digits. We can deduce `a`, `c` and `d`.

To find the segment corresponding to `a`, we can identify numbers `1` and `7` in the `segmentPatternsString`. We are looking for the letter not present in `1`. For example, given `ab` and `abd` we can determine that `d` is the name corresponding to segment `a`.

``````const findA = (segmentPatternsString) => {
const one = findDigitBySignalsLength(segmentPatternsString, 2)
const seven = findDigitBySignalsLength(segmentPatternsString, 3)

let a = seven
one.split('').forEach((letter) => {
a = a.replace(letter, '')
})
return a
}
``````

To find the segment corresponding to `c`, we can identify the number `1` in the `segmentPatternsString` and eliminate the previously found segment corresponding to `f`. For example, given `ab` and the previously found `b` corresponding to `f`, we can deduce that `a` is the name corresponding to `c`.

``````const findC = (segmentPatternsString, letterF) => {
const one = segmentPatternsString
.split(' ')
.find((digit) => digit.length === 2)
return one.replace(letterF, '')
}
``````

To find the segment corresponding to `d`, we can identify the number`4` in the `segmentPatternsString` and eliminate the previously found segment corresponding to `b`, `c` and `f`. For example, given `eafb`, and the previously found `e` corresponding to `b`, `a` corresponding to `c` and `b` corresponding to `f`, we can deduce that `f` is the name corresponding to `d`.

``````const findD = (segmentPatternsString, signalMap) => {
const four = findDigitBySignalsLength(segmentPatternsString, 4)
return four
.replace(signalMap.b, '')
.replace(signalMap.c, '')
.replace(signalMap.f, '')
}
``````

#### Use a process of elimination

After mapping `a`, `b`, `c`, `d`, `e` and `f`, we can determine the segment corresponding to `g` by process of elimination.

``````const findG = (signalMap) => {
let allLetters = 'abcdefg'
Object.keys(signalMap).forEach(
(key) => (allLetters = allLetters.replace(signalMap[key], ''))
)
return allLetters
}
``````

The final solution for part two is in my GitHub repository.

## Conclusion

Day 8 was really challenging and entailed decoding numbers by identifying unique occurrences, segment lengths and by process of elimination.